Make new_type return Py<PyType> instead
This commit is contained in:
parent
64a2456d5e
commit
0ee13c1c1b
|
@ -12,7 +12,6 @@ use std::borrow::Cow;
|
|||
use std::cell::UnsafeCell;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
mod err_state;
|
||||
mod impls;
|
||||
|
@ -305,65 +304,27 @@ impl PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new exception type with the given name.
|
||||
///
|
||||
/// - `base` can be an existing exception type to subclass, or a tuple of classes.
|
||||
/// - `dict` specifies an optional dictionary of class variables and methods.
|
||||
///
|
||||
/// For a version of this function that also takes a docstring, see [`PyErr::new_type_with_doc`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if:
|
||||
/// - `name` is not of the form `<module>.<ExceptionName>`.
|
||||
/// - `name` cannot be converted to [`CString`]s.
|
||||
pub fn new_type<'p>(
|
||||
py: Python<'p>,
|
||||
name: &str,
|
||||
base: Option<&PyType>,
|
||||
dict: Option<PyObject>,
|
||||
) -> NonNull<ffi::PyTypeObject> {
|
||||
let base: *mut ffi::PyObject = match base {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(obj) => obj.as_ptr(),
|
||||
};
|
||||
|
||||
let dict: *mut ffi::PyObject = match dict {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(obj) => obj.as_ptr(),
|
||||
};
|
||||
|
||||
let null_terminated_name =
|
||||
CString::new(name).expect("Failed to initialize nul terminated exception name");
|
||||
|
||||
let ptr = unsafe {
|
||||
ffi::PyErr_NewException(null_terminated_name.as_ptr(), base, dict)
|
||||
as *mut ffi::PyTypeObject
|
||||
};
|
||||
match NonNull::new(ptr) {
|
||||
Some(not_null) => not_null,
|
||||
None => panic!("{}", PyErr::fetch(py)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new exception type with the given name and docstring.
|
||||
///
|
||||
/// - `base` can be an existing exception type to subclass, or a tuple of classes.
|
||||
/// - `dict` specifies an optional dictionary of class variables and methods.
|
||||
/// - `doc` will be the docstring seen by python users.
|
||||
///
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function returns an error if `name` is not of the form `<module>.<ExceptionName>`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if:
|
||||
/// - `name` is not of the form `<module>.<ExceptionName>`.
|
||||
/// - `name` or `doc` cannot be converted to [`CString`]s.
|
||||
pub fn new_type_with_doc<'p>(
|
||||
py: Python<'p>,
|
||||
/// This function will panic if `name` or `doc` cannot be converted to [`CString`]s.
|
||||
pub fn new_type(
|
||||
py: Python,
|
||||
name: &str,
|
||||
doc: Option<&str>,
|
||||
base: Option<&PyType>,
|
||||
dict: Option<PyObject>,
|
||||
) -> NonNull<ffi::PyTypeObject> {
|
||||
) -> PyResult<Py<PyType>> {
|
||||
let base: *mut ffi::PyObject = match base {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(obj) => obj.as_ptr(),
|
||||
|
@ -391,13 +352,10 @@ impl PyErr {
|
|||
null_terminated_doc_ptr,
|
||||
base,
|
||||
dict,
|
||||
) as *mut ffi::PyTypeObject
|
||||
)
|
||||
};
|
||||
|
||||
match NonNull::new(ptr) {
|
||||
Some(not_null) => not_null,
|
||||
None => panic!("{}", PyErr::fetch(py)),
|
||||
}
|
||||
unsafe { Py::from_owned_ptr_or_err(py, ptr) }
|
||||
}
|
||||
|
||||
/// Prints a standard traceback to `sys.stderr`.
|
||||
|
|
|
@ -131,7 +131,7 @@ macro_rules! import_exception {
|
|||
///
|
||||
/// * `module` is the name of the containing module.
|
||||
/// * `name` is the name of the new exception type.
|
||||
/// * `base` is the superclass of `MyError`, usually [`PyException`].
|
||||
/// * `base` is the base class of `MyError`, usually [`PyException`].
|
||||
/// * `doc` (optional) is the docstring visible to users (with `.__doc__` and `help()`) and
|
||||
/// accompanies your error type in your crate's documentation.
|
||||
///
|
||||
|
@ -159,9 +159,9 @@ macro_rules! import_exception {
|
|||
/// # fn main() -> PyResult<()> {
|
||||
/// # Python::with_gil(|py| -> PyResult<()> {
|
||||
/// # let fun = wrap_pyfunction!(raise_myerror, py)?;
|
||||
/// # let globals = pyo3::types::PyDict::new(py);
|
||||
/// # globals.set_item("MyError", py.get_type::<MyError>())?;
|
||||
/// # globals.set_item("raise_myerror", fun)?;
|
||||
/// # let locals = pyo3::types::PyDict::new(py);
|
||||
/// # locals.set_item("MyError", py.get_type::<MyError>())?;
|
||||
/// # locals.set_item("raise_myerror", fun)?;
|
||||
/// #
|
||||
/// # py.run(
|
||||
/// # "try:
|
||||
|
@ -169,8 +169,8 @@ macro_rules! import_exception {
|
|||
/// # except MyError as e:
|
||||
/// # assert e.__doc__ == 'Some description.'
|
||||
/// # assert str(e) == 'Some error happened.'",
|
||||
/// # Some(globals),
|
||||
/// # None,
|
||||
/// # Some(locals),
|
||||
/// # )?;
|
||||
/// #
|
||||
/// # Ok(())
|
||||
|
@ -238,20 +238,15 @@ macro_rules! create_exception_type_object {
|
|||
GILOnceCell::new();
|
||||
|
||||
TYPE_OBJECT
|
||||
.get_or_init(py, || unsafe {
|
||||
$crate::Py::from_owned_ptr(
|
||||
.get_or_init(py, ||
|
||||
$crate::PyErr::new_type(
|
||||
py,
|
||||
$crate::PyErr::new_type_with_doc(
|
||||
py,
|
||||
concat!(stringify!($module), ".", stringify!($name)),
|
||||
$doc,
|
||||
::std::option::Option::Some(py.get_type::<$base>()),
|
||||
::std::option::Option::None,
|
||||
)
|
||||
.as_ptr() as *mut $crate::ffi::PyObject,
|
||||
)
|
||||
})
|
||||
.as_ptr() as *mut _
|
||||
concat!(stringify!($module), ".", stringify!($name)),
|
||||
$doc,
|
||||
::std::option::Option::Some(py.get_type::<$base>()),
|
||||
::std::option::Option::None,
|
||||
).expect("Failed to initialize new exception type.")
|
||||
).as_ptr() as *mut $crate::ffi::PyTypeObject
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -796,6 +791,65 @@ mod tests {
|
|||
Some(ctx),
|
||||
)
|
||||
.unwrap();
|
||||
py.run("assert CustomError.__doc__ is None", None, Some(ctx))
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_exception_doc() {
|
||||
create_exception!(mymodule, CustomError, PyException, "Some docs");
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let error_type = py.get_type::<CustomError>();
|
||||
let ctx = [("CustomError", error_type)].into_py_dict(py);
|
||||
let type_description: String = py
|
||||
.eval("str(CustomError)", None, Some(ctx))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
|
||||
py.run(
|
||||
"assert CustomError('oops').args == ('oops',)",
|
||||
None,
|
||||
Some(ctx),
|
||||
)
|
||||
.unwrap();
|
||||
py.run("assert CustomError.__doc__ == 'Some docs'", None, Some(ctx))
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_exception_doc_expr() {
|
||||
create_exception!(
|
||||
mymodule,
|
||||
CustomError,
|
||||
PyException,
|
||||
concat!("Some", " more ", stringify!(docs))
|
||||
);
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let error_type = py.get_type::<CustomError>();
|
||||
let ctx = [("CustomError", error_type)].into_py_dict(py);
|
||||
let type_description: String = py
|
||||
.eval("str(CustomError)", None, Some(ctx))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
|
||||
py.run(
|
||||
"assert CustomError('oops').args == ('oops',)",
|
||||
None,
|
||||
Some(ctx),
|
||||
)
|
||||
.unwrap();
|
||||
py.run(
|
||||
"assert CustomError.__doc__ == 'Some more docs'",
|
||||
None,
|
||||
Some(ctx),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue