Merge pull request #1890 from mejrs/err2

Document imported exceptions
This commit is contained in:
David Hewitt 2021-09-29 19:32:19 +01:00 committed by GitHub
commit 3adacaed2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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);