use crate::{ffi, Bound, PyResult, Python};
use std::ffi::CStr;
use std::ops;
#[doc(hidden)]
#[macro_export]
macro_rules! impl_exception_boilerplate {
($name: ident) => {
impl $name {
#[inline]
#[allow(dead_code, reason = "user may not call this function")]
pub fn new_err<A>(args: A) -> $crate::PyErr
where
A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
{
$crate::PyErr::new::<$name, A>(args)
}
}
impl $crate::ToPyErr for $name {}
};
}
#[macro_export]
macro_rules! import_exception {
($module: expr, $name: ident) => {
#[repr(transparent)]
#[allow(non_camel_case_types, reason = "matches imported exception name, e.g. `socket.herror`")]
pub struct $name($crate::PyAny);
$crate::impl_exception_boilerplate!($name);
$crate::pyobject_native_type_core!(
$name,
$name::type_object_raw,
stringify!($name),
stringify!($module),
#module=::std::option::Option::Some(stringify!($module))
);
impl $name {
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
use $crate::types::PyTypeMethods;
static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
$crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name));
TYPE_OBJECT.get(py).as_type_ptr()
}
}
};
}
#[macro_export]
#[deprecated(since = "0.27.0", note = "renamed to `import_exception!` instead")]
macro_rules! import_exception_bound {
($module: expr, $name: ident) => {
$crate::import_exception!($module, $name);
};
}
#[macro_export]
macro_rules! create_exception {
($module: expr, $name: ident, $base: ty) => {
#[repr(transparent)]
pub struct $name($crate::PyAny);
$crate::impl_exception_boilerplate!($name);
$crate::create_exception_type_object!($module, $name, $base, None);
};
($module: expr, $name: ident, $base: ty, $doc: expr) => {
#[repr(transparent)]
#[doc = $doc]
pub struct $name($crate::PyAny);
$crate::impl_exception_boilerplate!($name);
$crate::create_exception_type_object!($module, $name, $base, Some($doc));
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! create_exception_type_object {
($module: expr, $name: ident, $base: ty, None) => {
$crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::None);
};
($module: expr, $name: ident, $base: ty, Some($doc: expr)) => {
$crate::create_exception_type_object!(
$module,
$name,
$base,
::std::option::Option::Some($crate::ffi::c_str!($doc))
);
};
($module: expr, $name: ident, $base: ty, $doc: expr) => {
$crate::pyobject_native_type_named!($name);
unsafe impl $crate::type_object::PyTypeInfo for $name {
const NAME: &'static str = stringify!($name);
const MODULE: ::std::option::Option<&'static str> =
::std::option::Option::Some(stringify!($module));
$crate::create_exception_type_hint!($module, $name);
#[inline]
#[allow(clippy::redundant_closure_call)]
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
use $crate::sync::PyOnceLock;
static TYPE_OBJECT: PyOnceLock<$crate::Py<$crate::types::PyType>> =
PyOnceLock::new();
TYPE_OBJECT
.get_or_init(py, || {
$crate::PyErr::new_type(
py,
$crate::ffi::c_str!(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()
.cast()
}
}
impl $name {
#[doc(hidden)]
pub const _PYO3_DEF: $crate::impl_::pymodule::AddTypeToModule<Self> =
$crate::impl_::pymodule::AddTypeToModule::new();
#[allow(dead_code)]
#[doc(hidden)]
pub const _PYO3_INTROSPECTION_ID: &'static str =
concat!(stringify!($module), stringify!($name));
}
};
}
#[cfg(not(feature = "experimental-inspect"))]
#[doc(hidden)]
#[macro_export]
macro_rules! create_exception_type_hint(
($module: expr, $name: ident) => {};
);
#[cfg(feature = "experimental-inspect")]
#[doc(hidden)]
#[macro_export]
macro_rules! create_exception_type_hint(
($module: expr, $name: ident) => {
const TYPE_HINT: $crate::inspect::PyStaticExpr = $crate::inspect::PyStaticExpr::PyClass($crate::inspect::PyClassNameStaticExpr::new(
&$crate::type_hint_identifier!(stringify!($module), stringify!($name)),
Self::_PYO3_INTROSPECTION_ID
));
};
);
macro_rules! impl_native_exception (
($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => (
#[doc = $doc]
#[repr(transparent)]
#[allow(clippy::upper_case_acronyms, reason = "Python exception names")]
pub struct $name($crate::PyAny);
$crate::impl_exception_boilerplate!($name);
$crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }, "builtins", $python_name $(, #checkfunction=$checkfunction)?);
$crate::pyobject_subclassable_native_type!($name, $layout);
);
($name:ident, $exc_name:ident, $python_name:expr, $doc:expr) => (
impl_native_exception!($name, $exc_name, $python_name, $doc, $crate::ffi::PyBaseExceptionObject);
)
);
#[cfg(windows)]
macro_rules! impl_windows_native_exception (
($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path) => (
#[cfg(windows)]
#[doc = $doc]
#[repr(transparent)]
#[allow(clippy::upper_case_acronyms, reason = "Python exception names")]
pub struct $name($crate::PyAny);
$crate::impl_exception_boilerplate!($name);
$crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }, "builtins", $python_name);
);
($name:ident, $exc_name:ident, $python_name:expr, $doc:expr) => (
impl_windows_native_exception!($name, $exc_name, $python_name, $doc, $crate::ffi::PyBaseExceptionObject);
)
);
macro_rules! native_doc(
($name: literal, $alt: literal) => (
concat!(
"Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
", $alt
)
);
($name: literal) => (
concat!(
"
Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
# Example: Raising ", $name, " from Rust
This exception can be sent to Python code by converting it into a
[`PyErr`](crate::PyErr), where Python code can then catch it.
```
use pyo3::prelude::*;
use pyo3::exceptions::Py", $name, ";
#[pyfunction]
fn always_throws() -> PyResult<()> {
let message = \"I'm ", $name ,", and I was raised from Rust.\";
Err(Py", $name, "::new_err(message))
}
#
# Python::attach(|py| {
# let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
# let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\");
# assert!(err.is_instance_of::<Py", $name, ">(py))
# });
```
Python code:
```python
from my_module import always_throws
try:
always_throws()
except ", $name, " as e:
print(f\"Caught an exception: {e}\")
```
# Example: Catching ", $name, " in Rust
```
use pyo3::prelude::*;
use pyo3::exceptions::Py", $name, ";
use pyo3::ffi::c_str;
Python::attach(|py| {
let result: PyResult<()> = py.run(c_str!(\"raise ", $name, "\"), None, None);
let error_type = match result {
Ok(_) => \"Not an error\",
Err(error) if error.is_instance_of::<Py", $name, ">(py) => \"" , $name, "\",
Err(_) => \"Some other error\",
};
assert_eq!(error_type, \"", $name, "\");
});
```
"
)
);
);
impl_native_exception!(
PyBaseException,
PyExc_BaseException,
"BaseException",
native_doc!("BaseException"),
ffi::PyBaseExceptionObject,
#checkfunction=ffi::PyExceptionInstance_Check
);
impl_native_exception!(
PyException,
PyExc_Exception,
"Exception",
native_doc!("Exception")
);
impl_native_exception!(
PyStopAsyncIteration,
PyExc_StopAsyncIteration,
"StopAsyncIteration",
native_doc!("StopAsyncIteration")
);
impl_native_exception!(
PyStopIteration,
PyExc_StopIteration,
"StopIteration",
native_doc!("StopIteration"),
ffi::PyStopIterationObject
);
impl_native_exception!(
PyGeneratorExit,
PyExc_GeneratorExit,
"GeneratorExit",
native_doc!("GeneratorExit")
);
impl_native_exception!(
PyArithmeticError,
PyExc_ArithmeticError,
"ArithmeticError",
native_doc!("ArithmeticError")
);
impl_native_exception!(
PyLookupError,
PyExc_LookupError,
"LookupError",
native_doc!("LookupError")
);
impl_native_exception!(
PyAssertionError,
PyExc_AssertionError,
"AssertionError",
native_doc!("AssertionError")
);
impl_native_exception!(
PyAttributeError,
PyExc_AttributeError,
"AttributeError",
native_doc!("AttributeError")
);
impl_native_exception!(
PyBufferError,
PyExc_BufferError,
"BufferError",
native_doc!("BufferError")
);
impl_native_exception!(
PyEOFError,
PyExc_EOFError,
"EOFError",
native_doc!("EOFError")
);
impl_native_exception!(
PyFloatingPointError,
PyExc_FloatingPointError,
"FloatingPointError",
native_doc!("FloatingPointError")
);
#[cfg(not(any(PyPy, GraalPy)))]
impl_native_exception!(
PyOSError,
PyExc_OSError,
"OSError",
native_doc!("OSError"),
ffi::PyOSErrorObject
);
#[cfg(any(PyPy, GraalPy))]
impl_native_exception!(PyOSError, PyExc_OSError, "OSError", native_doc!("OSError"));
impl_native_exception!(
PyImportError,
PyExc_ImportError,
"ImportError",
native_doc!("ImportError")
);
impl_native_exception!(
PyModuleNotFoundError,
PyExc_ModuleNotFoundError,
"ModuleNotFoundError",
native_doc!("ModuleNotFoundError")
);
impl_native_exception!(
PyIndexError,
PyExc_IndexError,
"IndexError",
native_doc!("IndexError")
);
impl_native_exception!(
PyKeyError,
PyExc_KeyError,
"KeyError",
native_doc!("KeyError")
);
impl_native_exception!(
PyKeyboardInterrupt,
PyExc_KeyboardInterrupt,
"KeyboardInterrupt",
native_doc!("KeyboardInterrupt")
);
impl_native_exception!(
PyMemoryError,
PyExc_MemoryError,
"MemoryError",
native_doc!("MemoryError")
);
impl_native_exception!(
PyNameError,
PyExc_NameError,
"NameError",
native_doc!("NameError")
);
impl_native_exception!(
PyOverflowError,
PyExc_OverflowError,
"OverflowError",
native_doc!("OverflowError")
);
impl_native_exception!(
PyRuntimeError,
PyExc_RuntimeError,
"RuntimeError",
native_doc!("RuntimeError")
);
impl_native_exception!(
PyRecursionError,
PyExc_RecursionError,
"RecursionError",
native_doc!("RecursionError")
);
impl_native_exception!(
PyNotImplementedError,
PyExc_NotImplementedError,
"NotImplementedError",
native_doc!("NotImplementedError")
);
#[cfg(not(any(PyPy, GraalPy)))]
impl_native_exception!(
PySyntaxError,
PyExc_SyntaxError,
"SyntaxError",
native_doc!("SyntaxError"),
ffi::PySyntaxErrorObject
);
#[cfg(any(PyPy, GraalPy))]
impl_native_exception!(
PySyntaxError,
PyExc_SyntaxError,
"SyntaxError",
native_doc!("SyntaxError")
);
impl_native_exception!(
PyReferenceError,
PyExc_ReferenceError,
"ReferenceError",
native_doc!("ReferenceError")
);
impl_native_exception!(
PySystemError,
PyExc_SystemError,
"SystemError",
native_doc!("SystemError")
);
#[cfg(not(any(PyPy, GraalPy)))]
impl_native_exception!(
PySystemExit,
PyExc_SystemExit,
"SystemExit",
native_doc!("SystemExit"),
ffi::PySystemExitObject
);
#[cfg(any(PyPy, GraalPy))]
impl_native_exception!(
PySystemExit,
PyExc_SystemExit,
"SystemExit",
native_doc!("SystemExit")
);
impl_native_exception!(
PyTypeError,
PyExc_TypeError,
"TypeError",
native_doc!("TypeError")
);
impl_native_exception!(
PyUnboundLocalError,
PyExc_UnboundLocalError,
"UnboundLocalError",
native_doc!("UnboundLocalError")
);
#[cfg(not(any(PyPy, GraalPy)))]
impl_native_exception!(
PyUnicodeError,
PyExc_UnicodeError,
"UnicodeError",
native_doc!("UnicodeError"),
ffi::PyUnicodeErrorObject
);
#[cfg(any(PyPy, GraalPy))]
impl_native_exception!(
PyUnicodeError,
PyExc_UnicodeError,
"UnicodeError",
native_doc!("UnicodeError")
);
impl_native_exception!(
PyUnicodeDecodeError,
PyExc_UnicodeDecodeError,
"UnicodeDecodeError",
native_doc!("UnicodeDecodeError", "")
);
impl_native_exception!(
PyUnicodeEncodeError,
PyExc_UnicodeEncodeError,
"UnicodeEncodeError",
native_doc!("UnicodeEncodeError", "")
);
impl_native_exception!(
PyUnicodeTranslateError,
PyExc_UnicodeTranslateError,
"UnicodeTranslateError",
native_doc!("UnicodeTranslateError", "")
);
#[cfg(Py_3_11)]
impl_native_exception!(
PyBaseExceptionGroup,
PyExc_BaseExceptionGroup,
"BaseExceptionGroup",
native_doc!("BaseExceptionGroup", "")
);
impl_native_exception!(
PyValueError,
PyExc_ValueError,
"ValueError",
native_doc!("ValueError")
);
impl_native_exception!(
PyZeroDivisionError,
PyExc_ZeroDivisionError,
"ZeroDivisionError",
native_doc!("ZeroDivisionError")
);
impl_native_exception!(
PyBlockingIOError,
PyExc_BlockingIOError,
"BlockingIOError",
native_doc!("BlockingIOError")
);
impl_native_exception!(
PyBrokenPipeError,
PyExc_BrokenPipeError,
"BrokenPipeError",
native_doc!("BrokenPipeError")
);
impl_native_exception!(
PyChildProcessError,
PyExc_ChildProcessError,
"ChildProcessError",
native_doc!("ChildProcessError")
);
impl_native_exception!(
PyConnectionError,
PyExc_ConnectionError,
"ConnectionError",
native_doc!("ConnectionError")
);
impl_native_exception!(
PyConnectionAbortedError,
PyExc_ConnectionAbortedError,
"ConnectionAbortedError",
native_doc!("ConnectionAbortedError")
);
impl_native_exception!(
PyConnectionRefusedError,
PyExc_ConnectionRefusedError,
"ConnectionRefusedError",
native_doc!("ConnectionRefusedError")
);
impl_native_exception!(
PyConnectionResetError,
PyExc_ConnectionResetError,
"ConnectionResetError",
native_doc!("ConnectionResetError")
);
impl_native_exception!(
PyFileExistsError,
PyExc_FileExistsError,
"FileExistsError",
native_doc!("FileExistsError")
);
impl_native_exception!(
PyFileNotFoundError,
PyExc_FileNotFoundError,
"FileNotFoundError",
native_doc!("FileNotFoundError")
);
impl_native_exception!(
PyInterruptedError,
PyExc_InterruptedError,
"InterruptedError",
native_doc!("InterruptedError")
);
impl_native_exception!(
PyIsADirectoryError,
PyExc_IsADirectoryError,
"IsADirectoryError",
native_doc!("IsADirectoryError")
);
impl_native_exception!(
PyNotADirectoryError,
PyExc_NotADirectoryError,
"NotADirectoryError",
native_doc!("NotADirectoryError")
);
impl_native_exception!(
PyPermissionError,
PyExc_PermissionError,
"PermissionError",
native_doc!("PermissionError")
);
impl_native_exception!(
PyProcessLookupError,
PyExc_ProcessLookupError,
"ProcessLookupError",
native_doc!("ProcessLookupError")
);
impl_native_exception!(
PyTimeoutError,
PyExc_TimeoutError,
"TimeoutError",
native_doc!("TimeoutError")
);
impl_native_exception!(
PyEnvironmentError,
PyExc_EnvironmentError,
"EnvironmentError",
native_doc!("EnvironmentError")
);
impl_native_exception!(PyIOError, PyExc_IOError, "IOError", native_doc!("IOError"));
#[cfg(windows)]
impl_windows_native_exception!(
PyWindowsError,
PyExc_WindowsError,
"WindowsError",
native_doc!("WindowsError")
);
impl PyUnicodeDecodeError {
pub fn new<'py>(
py: Python<'py>,
encoding: &CStr,
input: &[u8],
range: ops::Range<usize>,
reason: &CStr,
) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::py_result_ext::PyResultExt;
unsafe {
ffi::PyUnicodeDecodeError_Create(
encoding.as_ptr(),
input.as_ptr().cast(),
input.len() as ffi::Py_ssize_t,
range.start as ffi::Py_ssize_t,
range.end as ffi::Py_ssize_t,
reason.as_ptr(),
)
.assume_owned_or_err(py)
}
.cast_into()
}
pub fn new_utf8<'py>(
py: Python<'py>,
input: &[u8],
err: std::str::Utf8Error,
) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
let pos = err.valid_up_to();
PyUnicodeDecodeError::new(py, c"utf-8", input, pos..(pos + 1), c"invalid utf-8")
}
}
impl_native_exception!(PyWarning, PyExc_Warning, "Warning", native_doc!("Warning"));
impl_native_exception!(
PyUserWarning,
PyExc_UserWarning,
"UserWarning",
native_doc!("UserWarning")
);
impl_native_exception!(
PyDeprecationWarning,
PyExc_DeprecationWarning,
"DeprecationWarning",
native_doc!("DeprecationWarning")
);
impl_native_exception!(
PyPendingDeprecationWarning,
PyExc_PendingDeprecationWarning,
"PendingDeprecationWarning",
native_doc!("PendingDeprecationWarning")
);
impl_native_exception!(
PySyntaxWarning,
PyExc_SyntaxWarning,
"SyntaxWarning",
native_doc!("SyntaxWarning")
);
impl_native_exception!(
PyRuntimeWarning,
PyExc_RuntimeWarning,
"RuntimeWarning",
native_doc!("RuntimeWarning")
);
impl_native_exception!(
PyFutureWarning,
PyExc_FutureWarning,
"FutureWarning",
native_doc!("FutureWarning")
);
impl_native_exception!(
PyImportWarning,
PyExc_ImportWarning,
"ImportWarning",
native_doc!("ImportWarning")
);
impl_native_exception!(
PyUnicodeWarning,
PyExc_UnicodeWarning,
"UnicodeWarning",
native_doc!("UnicodeWarning")
);
impl_native_exception!(
PyBytesWarning,
PyExc_BytesWarning,
"BytesWarning",
native_doc!("BytesWarning")
);
impl_native_exception!(
PyResourceWarning,
PyExc_ResourceWarning,
"ResourceWarning",
native_doc!("ResourceWarning")
);
#[cfg(Py_3_10)]
impl_native_exception!(
PyEncodingWarning,
PyExc_EncodingWarning,
"EncodingWarning",
native_doc!("EncodingWarning")
);
#[cfg(test)]
macro_rules! test_exception {
($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => {
#[allow(non_snake_case, reason = "test matches exception name")]
#[test]
fn $exc_ty () {
use super::$exc_ty;
$crate::Python::attach(|py| {
let err: $crate::PyErr = {
None
$(
.or(Some({ let $py = py; $constructor }))
)?
.unwrap_or($exc_ty::new_err("a test exception"))
};
assert!(err.is_instance_of::<$exc_ty>(py));
let value = err.value(py).as_any().cast::<$exc_ty>().unwrap();
assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
})
}
};
}
pub mod asyncio {
import_exception!(asyncio, CancelledError);
import_exception!(asyncio, InvalidStateError);
import_exception!(asyncio, TimeoutError);
import_exception!(asyncio, IncompleteReadError);
import_exception!(asyncio, LimitOverrunError);
import_exception!(asyncio, QueueEmpty);
import_exception!(asyncio, QueueFull);
#[cfg(test)]
mod tests {
test_exception!(CancelledError);
test_exception!(InvalidStateError);
test_exception!(TimeoutError);
test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err((
"partial", "expected"
)));
test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err((
"message", "consumed"
)));
test_exception!(QueueEmpty);
test_exception!(QueueFull);
}
}
pub mod socket {
import_exception!(socket, herror);
import_exception!(socket, gaierror);
import_exception!(socket, timeout);
#[cfg(test)]
mod tests {
test_exception!(herror);
test_exception!(gaierror);
test_exception!(timeout);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::any::PyAnyMethods;
use crate::types::{IntoPyDict, PyDict};
use crate::PyErr;
import_exception!(socket, gaierror);
import_exception!(email.errors, MessageError);
#[test]
fn test_check_exception() {
Python::attach(|py| {
let err: PyErr = gaierror::new_err(());
let socket = py
.import("socket")
.map_err(|e| e.display(py))
.expect("could not import socket");
let d = PyDict::new(py);
d.set_item("socket", socket)
.map_err(|e| e.display(py))
.expect("could not setitem");
d.set_item("exc", err)
.map_err(|e| e.display(py))
.expect("could not setitem");
py.run(c"assert isinstance(exc, socket.gaierror)", None, Some(&d))
.map_err(|e| e.display(py))
.expect("assertion failed");
});
}
#[test]
fn test_check_exception_nested() {
Python::attach(|py| {
let err: PyErr = MessageError::new_err(());
let email = py
.import("email")
.map_err(|e| e.display(py))
.expect("could not import email");
let d = PyDict::new(py);
d.set_item("email", email)
.map_err(|e| e.display(py))
.expect("could not setitem");
d.set_item("exc", err)
.map_err(|e| e.display(py))
.expect("could not setitem");
py.run(
c"assert isinstance(exc, email.errors.MessageError)",
None,
Some(&d),
)
.map_err(|e| e.display(py))
.expect("assertion failed");
});
}
#[test]
fn custom_exception() {
create_exception!(mymodule, CustomError, PyException);
Python::attach(|py| {
let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
let type_description: String = py
.eval(c"str(CustomError)", None, Some(&ctx))
.unwrap()
.extract()
.unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
py.run(
c"assert CustomError('oops').args == ('oops',)",
None,
Some(&ctx),
)
.unwrap();
py.run(c"assert CustomError.__doc__ is None", None, Some(&ctx))
.unwrap();
});
}
#[test]
fn custom_exception_dotted_module() {
create_exception!(mymodule.exceptions, CustomError, PyException);
Python::attach(|py| {
let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
let type_description: String = py
.eval(c"str(CustomError)", None, Some(&ctx))
.unwrap()
.extract()
.unwrap();
assert_eq!(
type_description,
"<class 'mymodule.exceptions.CustomError'>"
);
});
}
#[test]
fn custom_exception_doc() {
create_exception!(mymodule, CustomError, PyException, "Some docs");
Python::attach(|py| {
let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
let type_description: String = py
.eval(c"str(CustomError)", None, Some(&ctx))
.unwrap()
.extract()
.unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
py.run(
c"assert CustomError('oops').args == ('oops',)",
None,
Some(&ctx),
)
.unwrap();
py.run(
c"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::attach(|py| {
let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
let type_description: String = py
.eval(c"str(CustomError)", None, Some(&ctx))
.unwrap()
.extract()
.unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
py.run(
c"assert CustomError('oops').args == ('oops',)",
None,
Some(&ctx),
)
.unwrap();
py.run(
c"assert CustomError.__doc__ == 'Some more docs'",
None,
Some(&ctx),
)
.unwrap();
});
}
#[test]
fn native_exception_debug() {
Python::attach(|py| {
let exc = py
.run(c"raise Exception('banana')", None, None)
.expect_err("raising should have given us an error")
.into_value(py)
.into_bound(py);
assert_eq!(
format!("{exc:?}"),
exc.repr().unwrap().extract::<String>().unwrap()
);
});
}
#[test]
fn native_exception_display() {
Python::attach(|py| {
let exc = py
.run(c"raise Exception('banana')", None, None)
.expect_err("raising should have given us an error")
.into_value(py)
.into_bound(py);
assert_eq!(
exc.to_string(),
exc.str().unwrap().extract::<String>().unwrap()
);
});
}
#[test]
fn unicode_decode_error() {
let invalid_utf8 = b"fo\xd8o";
#[expect(invalid_from_utf8)]
let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
Python::attach(|py| {
let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
assert_eq!(
format!("{decode_err:?}"),
"UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
);
let e: PyErr = decode_err.into();
e.restore(py);
assert_eq!(
PyErr::fetch(py).to_string(),
"UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd8 in position 2: invalid utf-8"
);
});
}
#[cfg(Py_3_11)]
test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err((
"msg",
vec![PyValueError::new_err("err")]
)));
test_exception!(PyBaseException);
test_exception!(PyException);
test_exception!(PyStopAsyncIteration);
test_exception!(PyStopIteration);
test_exception!(PyGeneratorExit);
test_exception!(PyArithmeticError);
test_exception!(PyLookupError);
test_exception!(PyAssertionError);
test_exception!(PyAttributeError);
test_exception!(PyBufferError);
test_exception!(PyEOFError);
test_exception!(PyFloatingPointError);
test_exception!(PyOSError);
test_exception!(PyImportError);
test_exception!(PyModuleNotFoundError);
test_exception!(PyIndexError);
test_exception!(PyKeyError);
test_exception!(PyKeyboardInterrupt);
test_exception!(PyMemoryError);
test_exception!(PyNameError);
test_exception!(PyOverflowError);
test_exception!(PyRuntimeError);
test_exception!(PyRecursionError);
test_exception!(PyNotImplementedError);
test_exception!(PySyntaxError);
test_exception!(PyReferenceError);
test_exception!(PySystemError);
test_exception!(PySystemExit);
test_exception!(PyTypeError);
test_exception!(PyUnboundLocalError);
test_exception!(PyUnicodeError);
test_exception!(PyUnicodeDecodeError, |py| {
let invalid_utf8 = b"fo\xd8o";
#[expect(invalid_from_utf8)]
let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
PyErr::from_value(
PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)
.unwrap()
.into_any(),
)
});
test_exception!(PyUnicodeEncodeError, |py| py
.eval(c"chr(40960).encode('ascii')", None, None)
.unwrap_err());
test_exception!(PyUnicodeTranslateError, |_| {
PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))
});
test_exception!(PyValueError);
test_exception!(PyZeroDivisionError);
test_exception!(PyBlockingIOError);
test_exception!(PyBrokenPipeError);
test_exception!(PyChildProcessError);
test_exception!(PyConnectionError);
test_exception!(PyConnectionAbortedError);
test_exception!(PyConnectionRefusedError);
test_exception!(PyConnectionResetError);
test_exception!(PyFileExistsError);
test_exception!(PyFileNotFoundError);
test_exception!(PyInterruptedError);
test_exception!(PyIsADirectoryError);
test_exception!(PyNotADirectoryError);
test_exception!(PyPermissionError);
test_exception!(PyProcessLookupError);
test_exception!(PyTimeoutError);
test_exception!(PyEnvironmentError);
test_exception!(PyIOError);
#[cfg(windows)]
test_exception!(PyWindowsError);
test_exception!(PyWarning);
test_exception!(PyUserWarning);
test_exception!(PyDeprecationWarning);
test_exception!(PyPendingDeprecationWarning);
test_exception!(PySyntaxWarning);
test_exception!(PyRuntimeWarning);
test_exception!(PyFutureWarning);
test_exception!(PyImportWarning);
test_exception!(PyUnicodeWarning);
test_exception!(PyBytesWarning);
#[cfg(Py_3_10)]
test_exception!(PyEncodingWarning);
}