Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we export exceptions? #805

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions examples/rustapi_module/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@ def make_rust_extension(module_name):
],
packages=["rustapi_module"],
rust_extensions=[
make_rust_extension("rustapi_module.othermod"),
make_rust_extension("rustapi_module.buf_and_str"),
make_rust_extension("rustapi_module.cell"),
make_rust_extension("rustapi_module.datetime"),
make_rust_extension("rustapi_module.othermod"),
make_rust_extension("rustapi_module.subclassing"),
make_rust_extension("rustapi_module.test_dict"),
make_rust_extension("rustapi_module.buf_and_str"),
make_rust_extension("rustapi_module._pyo3_exceptions"),
],
install_requires=install_requires,
tests_require=tests_require,
Expand Down
22 changes: 22 additions & 0 deletions examples/rustapi_module/src/cell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Test for [?](https://github.com/PyO3/pyo3/issues/?)

use pyo3::prelude::*;

#[pyclass]
pub struct Mutable {}

#[pymethods]
impl Mutable {
#[new]
fn new() -> Self {
Mutable {}
}
fn invalid_borrow(&mut self, _other: &Mutable) {}
fn invalid_borrow_mut(&mut self, _other: &Mutable) {}
}

#[pymodule]
fn cell(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<Mutable>()?;
Ok(())
}
3 changes: 3 additions & 0 deletions examples/rustapi_module/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
pub mod buf_and_str;
pub mod cell;
pub mod datetime;
pub mod dict_iter;
pub mod othermod;
pub mod subclassing;

pyo3::proc_macro::export_exceptions!();
24 changes: 24 additions & 0 deletions examples/rustapi_module/tests/test_pycell_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from rustapi_module.cell import Mutable
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests fail.

from rustapi_module._pyo3_exceptions import PyBorrowError, PyBorrowMutError


def test_catch_borrrow():
m = Mutable()
try:
m.invalid_borrow(m)
assert False
except PyBorrowError:
pass
except Exception as e:
raise e


def test_catch_borrrowmut():
m = Mutable()
try:
m.invalid_borrow_mut(m)
assert False
except PyBorrowMutError:
pass
except Exception as e:
raise e
34 changes: 34 additions & 0 deletions pyo3cls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,37 @@ pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
)
.into()
}

#[proc_macro]
pub fn export_exceptions(_item: TokenStream) -> TokenStream {
let modname = syn::Ident::new("_pyo3_exceptions", proc_macro2::Span::call_site());

let mut ast = syn::parse_quote! {
fn _pyo3_exceptions(_py: pyo3::Python, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
use pyo3::type_object::PyTypeObject;
m.add("PyBorrowError", pyo3::pycell::PyBorrowError::type_object())?;
m.add(
"PyBorrowMutError",
pyo3::pycell::PyBorrowMutError::type_object(),
)?;
Ok(())
}
};

if let Err(err) = process_functions_in_module(&mut ast) {
return err.to_compile_error().into();
}

let doc = match get_doc(&ast.attrs, None, false) {
Ok(doc) => doc,
Err(err) => return err.to_compile_error().into(),
};

let expanded = py_init(&ast.sig.ident, &modname, doc);

quote!(
#ast
#expanded
)
.into()
}
2 changes: 1 addition & 1 deletion src/internal_tricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ macro_rules! private_impl {
macro_rules! pyo3_exception {
($name: ident, $base: ty) => {
$crate::impl_exception_boilerplate!($name);
$crate::create_exception_type_object!(pyo3_runtime, $name, $base);
$crate::create_exception_type_object!(_pyo3_exceptions, $name, $base);
};
}
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ pub mod types;

/// The proc macros, which are also part of the prelude
pub mod proc_macro {
pub use pyo3cls::pymodule;
/// The proc macro attributes
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
/// Function like macros
pub use pyo3cls::export_exceptions;
/// Attribute macros
pub use pyo3cls::{pyclass, pyfunction, pymethods, pymodule, pyproto};
}

/// Returns a function that takes a [Python] instance and returns a python function.
Expand Down
3 changes: 1 addition & 2 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ pub use crate::python::Python;
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
// This is only part of the prelude because we need it for the pymodule function
pub use crate::types::PyModule;
pub use pyo3cls::pymodule;
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
pub use pyo3cls::{export_exceptions, pyclass, pyfunction, pymethods, pymodule, pyproto};
35 changes: 35 additions & 0 deletions tests/test_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,38 @@ fn test_vararg_module() {
py_assert!(py, m, "m.int_vararg_fn() == [5, ()]");
py_assert!(py, m, "m.int_vararg_fn(1, 2) == [1, (2,)]");
}

pyo3::proc_macro::export_exceptions!();

#[pyfunction]
fn invalid_mutation(_a: &mut ValueClass, _b: &mut ValueClass) {}

#[pymodule]
fn module_with_exceptions(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<ValueClass>()?;
m.add_wrapped(pyo3::wrap_pyfunction!(invalid_mutation))?;
m.add_wrapped(pyo3::wrap_pymodule!(_pyo3_exceptions))?;
Ok(())
}

#[test]
fn catch_custom_exceptions() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test succeeds.

let gil = Python::acquire_gil();
let py = gil.python();
let m = pyo3::wrap_pymodule!(module_with_exceptions)(py);
pyo3::py_run!(
py,
m,
r"
v = m.ValueClass(0)
is_pyborrow_mut = False
try:
m.invalid_mutation(v, v)
assert False
except m._pyo3_exceptions.PyBorrowMutError:
is_pyborrow_mut = True

assert is_pyborrow_mut
"
);
}