Skip to content

Commit ad9fe4e

Browse files
committed
Implement exception introspection
Uses the new paste dependency (already used in ffi tests) to generate identifiers from proc-macros
1 parent 5fcc5d7 commit ad9fe4e

File tree

7 files changed

+79
-4
lines changed

7 files changed

+79
-4
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ smallvec = { version = "1.0", optional = true }
6060
uuid = { version = "1.11.0", optional = true }
6161
lock_api = { version = "0.4", optional = true }
6262
parking_lot = { version = "0.12", optional = true }
63-
iana-time-zone = { version = "0.1", optional = true, features = ["fallback"]}
63+
iana-time-zone = { version = "0.1", optional = true, features = ["fallback"] }
64+
paste = { version = "1", optional = true }
6465

6566
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
6667
portable-atomic = "1.0"
@@ -93,7 +94,7 @@ experimental-async = ["macros", "pyo3-macros/experimental-async"]
9394

9495
# Enables pyo3::inspect module and additional type information on FromPyObject
9596
# and IntoPy traits
96-
experimental-inspect = ["pyo3-macros/experimental-inspect"]
97+
experimental-inspect = ["pyo3-macros/experimental-inspect", "paste"]
9798

9899
# Enables macros: #[pyclass], #[pymodule], #[pyfunction] etc.
99100
macros = ["pyo3-macros"]

newsfragments/5666.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Introspection: adds support for exception created using `create_exception!`

pytests/src/exception.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use pyo3::create_exception;
2+
use pyo3::exceptions::{PyException, PyValueError};
3+
use pyo3::prelude::*;
4+
5+
create_exception!(pyo3_pytests.exception, CustomValueError, PyValueError);
6+
7+
create_exception!(pyo3_pytests.exception, CustomException, PyException);
8+
9+
#[pymodule(gil_used = false)]
10+
pub mod exception {
11+
#[pymodule_export]
12+
use super::{CustomException, CustomValueError};
13+
use pyo3::prelude::*;
14+
15+
#[pyfunction]
16+
fn raise_custom_value_error() -> PyResult<()> {
17+
Err(CustomValueError::new_err("custom value error"))
18+
}
19+
}

pytests/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod consts;
99
pub mod datetime;
1010
pub mod dict_iter;
1111
pub mod enums;
12+
mod exception;
1213
pub mod misc;
1314
pub mod objstore;
1415
pub mod othermod;
@@ -27,8 +28,8 @@ mod pyo3_pytests {
2728
use buf_and_str::buf_and_str;
2829
#[pymodule_export]
2930
use {
30-
comparisons::comparisons, consts::consts, enums::enums, pyclasses::pyclasses,
31-
pyfunctions::pyfunctions, subclassing::subclassing,
31+
comparisons::comparisons, consts::consts, enums::enums, exception::exception,
32+
pyclasses::pyclasses, pyfunctions::pyfunctions, subclassing::subclassing,
3233
};
3334

3435
// Inserting to sys.modules allows importing submodules nicely from Python

pytests/stubs/exception.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from typing import Any
2+
3+
class CustomException(Exception): ...
4+
class CustomValueError(ValueError): ...
5+
6+
def raise_custom_value_error() -> Any: ...

src/exceptions.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,55 @@ macro_rules! create_exception_type_object {
230230
).as_ptr() as *mut $crate::ffi::PyTypeObject
231231
}
232232
}
233+
234+
$crate::create_exception_introspection_data!($module, $name, $base);
233235
};
234236
}
235237

238+
/// Adds some introspection data for the exception if the `experimental-inspect` feature is enabled.
239+
#[cfg(not(feature = "experimental-inspect"))]
240+
#[doc(hidden)]
241+
#[macro_export]
242+
macro_rules! create_exception_introspection_data(
243+
($module: expr, $name: ident, $base: ty) => {};
244+
);
245+
246+
#[cfg(feature = "experimental-inspect")]
247+
#[doc(hidden)]
248+
#[macro_export]
249+
macro_rules! create_exception_introspection_data(
250+
($module: expr, $name: ident, $base: ty) => {
251+
const _: () = {
252+
const PIECES: &[&[u8]] = &[
253+
b"{\"type\":\"class\",\"id\":\"",
254+
$name::_PYO3_INTROSPECTION_ID.as_bytes(),
255+
b"\",\"name\":\"",
256+
stringify!($name).as_bytes(),
257+
b"\",\"bases\":[",
258+
{
259+
const BASE_LEN: usize = $crate::inspect::serialized_len_for_introspection(&<$base as $crate::type_object::PyTypeInfo>::TYPE_HINT);
260+
const BASE_SER: [u8; BASE_LEN] = {
261+
let mut result: [u8; BASE_LEN] = [0; BASE_LEN];
262+
$crate::inspect::serialize_for_introspection(&<$base as $crate::type_object::PyTypeInfo>::TYPE_HINT, &mut result);
263+
result
264+
};
265+
&BASE_SER
266+
},
267+
b"]}"
268+
];
269+
const PIECES_LEN: usize = $crate::impl_::concat::combined_len(PIECES);
270+
$crate::impl_::introspection::paste! {
271+
#[used]
272+
#[no_mangle]
273+
static [<PYO3_INTROSPECTION_1_ $name>]: $crate::impl_::introspection::SerializedIntrospectionFragment<PIECES_LEN> = $crate::impl_::introspection::SerializedIntrospectionFragment {
274+
length: PIECES_LEN as u32,
275+
fragment: $crate::impl_::concat::combine_to_array::<PIECES_LEN>(PIECES)
276+
};
277+
}
278+
};
279+
};
280+
);
281+
236282
macro_rules! impl_native_exception (
237283
($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => (
238284
#[doc = $doc]

src/impl_/introspection.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::conversion::IntoPyObject;
22
use crate::inspect::TypeHint;
3+
pub use paste::paste;
34

45
/// Trait to guess a function Python return type
56
///

0 commit comments

Comments
 (0)