From 09aef5d299c49e66111cb1ecd7694f5a131f4601 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Fri, 24 Jul 2020 09:05:16 +0100 Subject: [PATCH] Make `PyErr: Send + Sync` --- CHANGELOG.md | 1 + src/err.rs | 30 +++++++++++++++++++++++------- src/exceptions.rs | 6 ++++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97d0332c..fef0480a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Correct FFI definition `_PyLong_AsByteArray` `*mut c_uchar` argument instead of `*const c_uchar`. [#1029](https://github.com/PyO3/pyo3/pull/1029) - `PyType::as_type_ptr` is no longer `unsafe`. [#1047](https://github.com/PyO3/pyo3/pull/1047) - Change `PyIterator::from_object` to return `PyResult` instead of `Result`. [#1051](https://github.com/PyO3/pyo3/pull/1051) +- Implement `Send + Sync` for `PyErr`. `PyErr::new`, `PyErr::from_type`, `PyException::py_err` and `PyException::into` have had these bounds added to their arguments. [#1067](https://github.com/PyO3/pyo3/pull/1067) ### Removed - Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023) diff --git a/src/err.rs b/src/err.rs index 569cc921..e25b045b 100644 --- a/src/err.rs +++ b/src/err.rs @@ -26,12 +26,15 @@ use std::ptr::NonNull; pub enum PyErrValue { None, Value(PyObject), - ToArgs(Box), - ToObject(Box), + ToArgs(Box), + ToObject(Box), } impl PyErrValue { - pub fn from_err_args(value: T) -> Self { + pub fn from_err_args(value: T) -> Self + where + T: PyErrArguments + Send + Sync + 'static, + { let _ = Python::acquire_gil(); PyErrValue::ToArgs(Box::new(value)) } @@ -85,6 +88,8 @@ impl PyErr { /// * a tuple: the exception instance will be created using Python `T(*tuple)` /// * any other value: the exception instance will be created using Python `T(value)` /// + /// Note: if `value` is not `Send` or `Sync`, consider using `PyErr::from_value` instead. + /// /// Panics if `T` is not a Python class derived from `BaseException`. /// /// Example: @@ -101,7 +106,7 @@ impl PyErr { pub fn new(value: V) -> PyErr where T: PyTypeObject, - V: ToPyObject + 'static, + V: ToPyObject + Send + Sync + 'static, { let gil = ensure_gil(); let py = unsafe { gil.python() }; @@ -123,7 +128,7 @@ impl PyErr { /// `args` is the a tuple of arguments to pass to the exception constructor. pub fn from_type(exc: &PyType, args: A) -> PyErr where - A: ToPyObject + 'static, + A: ToPyObject + Send + Sync + 'static, { PyErr { ptype: exc.into(), @@ -559,13 +564,15 @@ impl PyErrArguments for io::Error { } } -impl std::convert::From> for PyErr { +impl std::convert::From> + for PyErr +{ fn from(err: std::io::IntoInnerError) -> PyErr { PyErr::from_value::(PyErrValue::from_err_args(err)) } } -impl PyErrArguments for std::io::IntoInnerError { +impl PyErrArguments for std::io::IntoInnerError { fn arguments(&self, py: Python) -> PyObject { self.to_string().to_object(py) } @@ -654,4 +661,13 @@ mod tests { std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| PyErr::fetch(py))).is_err(); assert!(started_unwind); } + + #[test] + fn test_pyerr_send_sync() { + fn is_send() {} + fn is_sync() {} + + is_send::(); + is_sync::(); + } } diff --git a/src/exceptions.rs b/src/exceptions.rs index 5e739057..6b365bdd 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -26,10 +26,12 @@ macro_rules! impl_exception_boilerplate { } impl $name { - pub fn py_err(args: V) -> $crate::PyErr { + pub fn py_err(args: V) -> $crate::PyErr { $crate::PyErr::new::<$name, V>(args) } - pub fn into(args: V) -> $crate::PyResult { + pub fn into( + args: V, + ) -> $crate::PyResult { $crate::PyErr::new::<$name, V>(args).into() } }