commit
3adacaed2c
|
@ -1,6 +1,12 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Exception types defined by Python.
|
||||
//!
|
||||
//! The structs in this module represent Python's built-in exceptions, while the modules comprise
|
||||
//! structs representing errors defined in Python code.
|
||||
//!
|
||||
//! The latter are created with the [`import_exception`] macro, which you can use yourself
|
||||
//! to import Python exceptions.
|
||||
|
||||
use crate::{ffi, PyResult, Python};
|
||||
use std::ffi::CStr;
|
||||
|
@ -19,7 +25,9 @@ macro_rules! impl_exception_boilerplate {
|
|||
}
|
||||
|
||||
impl $name {
|
||||
/// Creates a new [PyErr](crate::PyErr) of this type.
|
||||
/// Creates a new [`PyErr`] of this type.
|
||||
///
|
||||
/// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
|
||||
pub fn new_err<A>(args: A) -> $crate::PyErr
|
||||
where
|
||||
A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
|
||||
|
@ -71,6 +79,12 @@ macro_rules! impl_exception_boilerplate {
|
|||
#[macro_export]
|
||||
macro_rules! import_exception {
|
||||
($module: expr, $name: ident) => {
|
||||
/// A Rust type representing an exception defined in Python code.
|
||||
///
|
||||
/// This type was created by the [`pyo3::import_exception!`] macro - see its documentation
|
||||
/// for more information.
|
||||
///
|
||||
/// [`pyo3::import_exception!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3"
|
||||
#[repr(transparent)]
|
||||
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||
pub struct $name($crate::PyAny);
|
||||
|
@ -196,86 +210,316 @@ macro_rules! create_exception_type_object {
|
|||
}
|
||||
|
||||
macro_rules! impl_native_exception (
|
||||
($name:ident, $exc_name:ident, $layout:path) => (
|
||||
($name:ident, $exc_name:ident, $doc:expr, $layout:path) => (
|
||||
#[doc = $doc]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct $name($crate::PyAny);
|
||||
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
$crate::pyobject_native_type!($name, $layout, *($crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject));
|
||||
);
|
||||
($name:ident, $exc_name:ident) => (
|
||||
impl_native_exception!($name, $exc_name, $crate::ffi::PyBaseExceptionObject);
|
||||
($name:ident, $exc_name:ident, $doc:expr) => (
|
||||
impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
|
||||
)
|
||||
);
|
||||
|
||||
impl_native_exception!(PyBaseException, PyExc_BaseException);
|
||||
impl_native_exception!(PyException, PyExc_Exception);
|
||||
impl_native_exception!(PyStopAsyncIteration, PyExc_StopAsyncIteration);
|
||||
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::with_gil(|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::<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, ";
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let result: PyResult<()> = py.run(\"raise ", $name, "\", None, None);
|
||||
|
||||
let error_type = match result {
|
||||
Ok(_) => \"Not an error\",
|
||||
Err(error) if error.is_instance::<Py", $name, ">(py) => \"" , $name, "\",
|
||||
Err(_) => \"Some other error\",
|
||||
};
|
||||
|
||||
assert_eq!(error_type, \"", $name, "\");
|
||||
});
|
||||
```
|
||||
"
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
impl_native_exception!(
|
||||
PyBaseException,
|
||||
PyExc_BaseException,
|
||||
native_doc!("BaseException")
|
||||
);
|
||||
impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception"));
|
||||
impl_native_exception!(
|
||||
PyStopAsyncIteration,
|
||||
PyExc_StopAsyncIteration,
|
||||
native_doc!("StopAsyncIteration")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyStopIteration,
|
||||
PyExc_StopIteration,
|
||||
native_doc!("StopIteration"),
|
||||
ffi::PyStopIterationObject
|
||||
);
|
||||
impl_native_exception!(PyGeneratorExit, PyExc_GeneratorExit);
|
||||
impl_native_exception!(PyArithmeticError, PyExc_ArithmeticError);
|
||||
impl_native_exception!(PyLookupError, PyExc_LookupError);
|
||||
impl_native_exception!(
|
||||
PyGeneratorExit,
|
||||
PyExc_GeneratorExit,
|
||||
native_doc!("GeneratorExit")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyArithmeticError,
|
||||
PyExc_ArithmeticError,
|
||||
native_doc!("ArithmeticError")
|
||||
);
|
||||
impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError"));
|
||||
|
||||
impl_native_exception!(PyAssertionError, PyExc_AssertionError);
|
||||
impl_native_exception!(PyAttributeError, PyExc_AttributeError);
|
||||
impl_native_exception!(PyBufferError, PyExc_BufferError);
|
||||
impl_native_exception!(PyEOFError, PyExc_EOFError);
|
||||
impl_native_exception!(PyFloatingPointError, PyExc_FloatingPointError);
|
||||
impl_native_exception!(PyOSError, PyExc_OSError, ffi::PyOSErrorObject);
|
||||
impl_native_exception!(PyImportError, PyExc_ImportError);
|
||||
impl_native_exception!(
|
||||
PyAssertionError,
|
||||
PyExc_AssertionError,
|
||||
native_doc!("AssertionError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyAttributeError,
|
||||
PyExc_AttributeError,
|
||||
native_doc!("AttributeError")
|
||||
);
|
||||
impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError"));
|
||||
impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError"));
|
||||
impl_native_exception!(
|
||||
PyFloatingPointError,
|
||||
PyExc_FloatingPointError,
|
||||
native_doc!("FloatingPointError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyOSError,
|
||||
PyExc_OSError,
|
||||
native_doc!("OSError"),
|
||||
ffi::PyOSErrorObject
|
||||
);
|
||||
impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError"));
|
||||
|
||||
impl_native_exception!(PyModuleNotFoundError, PyExc_ModuleNotFoundError);
|
||||
impl_native_exception!(
|
||||
PyModuleNotFoundError,
|
||||
PyExc_ModuleNotFoundError,
|
||||
native_doc!("ModuleNotFoundError")
|
||||
);
|
||||
|
||||
impl_native_exception!(PyIndexError, PyExc_IndexError);
|
||||
impl_native_exception!(PyKeyError, PyExc_KeyError);
|
||||
impl_native_exception!(PyKeyboardInterrupt, PyExc_KeyboardInterrupt);
|
||||
impl_native_exception!(PyMemoryError, PyExc_MemoryError);
|
||||
impl_native_exception!(PyNameError, PyExc_NameError);
|
||||
impl_native_exception!(PyOverflowError, PyExc_OverflowError);
|
||||
impl_native_exception!(PyRuntimeError, PyExc_RuntimeError);
|
||||
impl_native_exception!(PyRecursionError, PyExc_RecursionError);
|
||||
impl_native_exception!(PyNotImplementedError, PyExc_NotImplementedError);
|
||||
impl_native_exception!(PySyntaxError, PyExc_SyntaxError, ffi::PySyntaxErrorObject);
|
||||
impl_native_exception!(PyReferenceError, PyExc_ReferenceError);
|
||||
impl_native_exception!(PySystemError, PyExc_SystemError);
|
||||
impl_native_exception!(PySystemExit, PyExc_SystemExit, ffi::PySystemExitObject);
|
||||
impl_native_exception!(PyTypeError, PyExc_TypeError);
|
||||
impl_native_exception!(PyUnboundLocalError, PyExc_UnboundLocalError);
|
||||
impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError"));
|
||||
impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError"));
|
||||
impl_native_exception!(
|
||||
PyKeyboardInterrupt,
|
||||
PyExc_KeyboardInterrupt,
|
||||
native_doc!("KeyboardInterrupt")
|
||||
);
|
||||
impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError"));
|
||||
impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError"));
|
||||
impl_native_exception!(
|
||||
PyOverflowError,
|
||||
PyExc_OverflowError,
|
||||
native_doc!("OverflowError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyRuntimeError,
|
||||
PyExc_RuntimeError,
|
||||
native_doc!("RuntimeError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyRecursionError,
|
||||
PyExc_RecursionError,
|
||||
native_doc!("RecursionError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyNotImplementedError,
|
||||
PyExc_NotImplementedError,
|
||||
native_doc!("NotImplementedError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PySyntaxError,
|
||||
PyExc_SyntaxError,
|
||||
native_doc!("SyntaxError"),
|
||||
ffi::PySyntaxErrorObject
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyReferenceError,
|
||||
PyExc_ReferenceError,
|
||||
native_doc!("ReferenceError")
|
||||
);
|
||||
impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError"));
|
||||
impl_native_exception!(
|
||||
PySystemExit,
|
||||
PyExc_SystemExit,
|
||||
native_doc!("SystemExit"),
|
||||
ffi::PySystemExitObject
|
||||
);
|
||||
impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError"));
|
||||
impl_native_exception!(
|
||||
PyUnboundLocalError,
|
||||
PyExc_UnboundLocalError,
|
||||
native_doc!("UnboundLocalError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyUnicodeError,
|
||||
PyExc_UnicodeError,
|
||||
native_doc!("UnicodeError"),
|
||||
ffi::PyUnicodeErrorObject
|
||||
);
|
||||
impl_native_exception!(PyUnicodeDecodeError, PyExc_UnicodeDecodeError);
|
||||
impl_native_exception!(PyUnicodeEncodeError, PyExc_UnicodeEncodeError);
|
||||
impl_native_exception!(PyUnicodeTranslateError, PyExc_UnicodeTranslateError);
|
||||
impl_native_exception!(PyValueError, PyExc_ValueError);
|
||||
impl_native_exception!(PyZeroDivisionError, PyExc_ZeroDivisionError);
|
||||
// these three errors need arguments, so they're too annoying to write tests for using macros...
|
||||
impl_native_exception!(
|
||||
PyUnicodeDecodeError,
|
||||
PyExc_UnicodeDecodeError,
|
||||
native_doc!("UnicodeDecodeError", "")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyUnicodeEncodeError,
|
||||
PyExc_UnicodeEncodeError,
|
||||
native_doc!("UnicodeEncodeError", "")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyUnicodeTranslateError,
|
||||
PyExc_UnicodeTranslateError,
|
||||
native_doc!("UnicodeTranslateError", "")
|
||||
);
|
||||
impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError"));
|
||||
impl_native_exception!(
|
||||
PyZeroDivisionError,
|
||||
PyExc_ZeroDivisionError,
|
||||
native_doc!("ZeroDivisionError")
|
||||
);
|
||||
|
||||
impl_native_exception!(PyBlockingIOError, PyExc_BlockingIOError);
|
||||
impl_native_exception!(PyBrokenPipeError, PyExc_BrokenPipeError);
|
||||
impl_native_exception!(PyChildProcessError, PyExc_ChildProcessError);
|
||||
impl_native_exception!(PyConnectionError, PyExc_ConnectionError);
|
||||
impl_native_exception!(PyConnectionAbortedError, PyExc_ConnectionAbortedError);
|
||||
impl_native_exception!(PyConnectionRefusedError, PyExc_ConnectionRefusedError);
|
||||
impl_native_exception!(PyConnectionResetError, PyExc_ConnectionResetError);
|
||||
impl_native_exception!(PyFileExistsError, PyExc_FileExistsError);
|
||||
impl_native_exception!(PyFileNotFoundError, PyExc_FileNotFoundError);
|
||||
impl_native_exception!(PyInterruptedError, PyExc_InterruptedError);
|
||||
impl_native_exception!(PyIsADirectoryError, PyExc_IsADirectoryError);
|
||||
impl_native_exception!(PyNotADirectoryError, PyExc_NotADirectoryError);
|
||||
impl_native_exception!(PyPermissionError, PyExc_PermissionError);
|
||||
impl_native_exception!(PyProcessLookupError, PyExc_ProcessLookupError);
|
||||
impl_native_exception!(PyTimeoutError, PyExc_TimeoutError);
|
||||
impl_native_exception!(
|
||||
PyBlockingIOError,
|
||||
PyExc_BlockingIOError,
|
||||
native_doc!("BlockingIOError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyBrokenPipeError,
|
||||
PyExc_BrokenPipeError,
|
||||
native_doc!("BrokenPipeError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyChildProcessError,
|
||||
PyExc_ChildProcessError,
|
||||
native_doc!("ChildProcessError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyConnectionError,
|
||||
PyExc_ConnectionError,
|
||||
native_doc!("ConnectionError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyConnectionAbortedError,
|
||||
PyExc_ConnectionAbortedError,
|
||||
native_doc!("ConnectionAbortedError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyConnectionRefusedError,
|
||||
PyExc_ConnectionRefusedError,
|
||||
native_doc!("ConnectionRefusedError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyConnectionResetError,
|
||||
PyExc_ConnectionResetError,
|
||||
native_doc!("ConnectionResetError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyFileExistsError,
|
||||
PyExc_FileExistsError,
|
||||
native_doc!("FileExistsError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyFileNotFoundError,
|
||||
PyExc_FileNotFoundError,
|
||||
native_doc!("FileNotFoundError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyInterruptedError,
|
||||
PyExc_InterruptedError,
|
||||
native_doc!("InterruptedError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyIsADirectoryError,
|
||||
PyExc_IsADirectoryError,
|
||||
native_doc!("IsADirectoryError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyNotADirectoryError,
|
||||
PyExc_NotADirectoryError,
|
||||
native_doc!("NotADirectoryError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyPermissionError,
|
||||
PyExc_PermissionError,
|
||||
native_doc!("PermissionError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyProcessLookupError,
|
||||
PyExc_ProcessLookupError,
|
||||
native_doc!("ProcessLookupError")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyTimeoutError,
|
||||
PyExc_TimeoutError,
|
||||
native_doc!("TimeoutError")
|
||||
);
|
||||
|
||||
impl_native_exception!(PyEnvironmentError, PyExc_EnvironmentError);
|
||||
impl_native_exception!(PyIOError, PyExc_IOError);
|
||||
impl_native_exception!(
|
||||
PyEnvironmentError,
|
||||
PyExc_EnvironmentError,
|
||||
native_doc!("EnvironmentError")
|
||||
);
|
||||
impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError"));
|
||||
#[cfg(windows)]
|
||||
impl_native_exception!(PyWindowsError, PyExc_WindowsError);
|
||||
impl_native_exception!(
|
||||
PyWindowsError,
|
||||
PyExc_WindowsError,
|
||||
native_doc!("WindowsError")
|
||||
);
|
||||
|
||||
impl PyUnicodeDecodeError {
|
||||
/// Creates a Python `UnicodeDecodeError`.
|
||||
|
@ -299,6 +543,25 @@ impl PyUnicodeDecodeError {
|
|||
}
|
||||
|
||||
/// Creates a Python `UnicodeDecodeError` from a Rust UTF-8 decoding error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::exceptions::PyUnicodeDecodeError;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py|{
|
||||
/// let invalid_utf8 = b"fo\xd8o";
|
||||
/// let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
|
||||
/// let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)?;
|
||||
/// assert_eq!(
|
||||
/// decode_err.to_string(),
|
||||
/// "'utf-8' codec can't decode byte 0xd8 in position 2: invalid utf-8"
|
||||
/// );
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
pub fn new_utf8<'p>(
|
||||
py: Python<'p>,
|
||||
input: &[u8],
|
||||
|
@ -315,7 +578,8 @@ impl PyUnicodeDecodeError {
|
|||
}
|
||||
}
|
||||
|
||||
/// Exceptions defined in `asyncio` module
|
||||
/// Exceptions defined in Python's [`asyncio`](https://docs.python.org/3/library/asyncio.html)
|
||||
/// module.
|
||||
pub mod asyncio {
|
||||
import_exception!(asyncio, CancelledError);
|
||||
import_exception!(asyncio, InvalidStateError);
|
||||
|
@ -326,7 +590,8 @@ pub mod asyncio {
|
|||
import_exception!(asyncio, QueueFull);
|
||||
}
|
||||
|
||||
/// Exceptions defined in `socket` module
|
||||
/// Exceptions defined in Python's [`socket`](https://docs.python.org/3/library/socket.html)
|
||||
/// module.
|
||||
pub mod socket {
|
||||
import_exception!(socket, herror);
|
||||
import_exception!(socket, gaierror);
|
||||
|
|
Loading…
Reference in a new issue