1 | //! Exception and warning types defined by Python. |
2 | //! |
3 | //! The structs in this module represent Python's built-in exceptions and |
4 | //! warnings, while the modules comprise structs representing errors defined in |
5 | //! Python code. |
6 | //! |
7 | //! The latter are created with the |
8 | //! [`import_exception`](crate::import_exception) macro, which you can use |
9 | //! yourself to import Python classes that are ultimately derived from |
10 | //! `BaseException`. |
11 | |
12 | use crate::{ffi, PyResult, Python}; |
13 | use std::ffi::CStr; |
14 | use std::ops; |
15 | use std::os::raw::c_char; |
16 | |
17 | /// The boilerplate to convert between a Rust type and a Python exception. |
18 | #[doc (hidden)] |
19 | #[macro_export ] |
20 | macro_rules! impl_exception_boilerplate { |
21 | ($name: ident) => { |
22 | impl ::std::convert::From<&$name> for $crate::PyErr { |
23 | #[inline] |
24 | fn from(err: &$name) -> $crate::PyErr { |
25 | $crate::PyErr::from_value(err) |
26 | } |
27 | } |
28 | |
29 | impl $name { |
30 | /// Creates a new [`PyErr`] of this type. |
31 | /// |
32 | /// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3" |
33 | #[inline] |
34 | pub fn new_err<A>(args: A) -> $crate::PyErr |
35 | where |
36 | A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static, |
37 | { |
38 | $crate::PyErr::new::<$name, A>(args) |
39 | } |
40 | } |
41 | |
42 | impl ::std::error::Error for $name { |
43 | fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> { |
44 | unsafe { |
45 | let cause: &$crate::exceptions::PyBaseException = self |
46 | .py() |
47 | .from_owned_ptr_or_opt($crate::ffi::PyException_GetCause(self.as_ptr()))?; |
48 | |
49 | ::std::option::Option::Some(cause) |
50 | } |
51 | } |
52 | } |
53 | }; |
54 | } |
55 | |
56 | /// Defines a Rust type for an exception defined in Python code. |
57 | /// |
58 | /// # Syntax |
59 | /// |
60 | /// ```import_exception!(module, MyError)``` |
61 | /// |
62 | /// * `module` is the name of the containing module. |
63 | /// * `MyError` is the name of the new exception type. |
64 | /// |
65 | /// # Examples |
66 | /// ``` |
67 | /// use pyo3::import_exception; |
68 | /// use pyo3::types::IntoPyDict; |
69 | /// use pyo3::Python; |
70 | /// |
71 | /// import_exception!(socket, gaierror); |
72 | /// |
73 | /// Python::with_gil(|py| { |
74 | /// let ctx = [("gaierror", py.get_type::<gaierror>())].into_py_dict(py); |
75 | /// pyo3::py_run!(py, *ctx, "import socket; assert gaierror is socket.gaierror"); |
76 | /// }); |
77 | /// |
78 | /// ``` |
79 | #[macro_export ] |
80 | macro_rules! import_exception { |
81 | ($module: expr, $name: ident) => { |
82 | /// A Rust type representing an exception defined in Python code. |
83 | /// |
84 | /// This type was created by the [`pyo3::import_exception!`] macro - see its documentation |
85 | /// for more information. |
86 | /// |
87 | /// [`pyo3::import_exception!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3" |
88 | #[repr(transparent)] |
89 | #[allow(non_camel_case_types)] // E.g. `socket.herror` |
90 | pub struct $name($crate::PyAny); |
91 | |
92 | $crate::impl_exception_boilerplate!($name); |
93 | |
94 | $crate::pyobject_native_type_core!( |
95 | $name, |
96 | $name::type_object_raw, |
97 | #module=::std::option::Option::Some(stringify!($module)) |
98 | ); |
99 | |
100 | impl $name { |
101 | fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { |
102 | use $crate::sync::GILOnceCell; |
103 | static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> = |
104 | GILOnceCell::new(); |
105 | |
106 | TYPE_OBJECT |
107 | .get_or_init(py, || { |
108 | let imp = py |
109 | .import(stringify!($module)) |
110 | .unwrap_or_else(|err| { |
111 | let traceback = err |
112 | .traceback(py) |
113 | .map(|tb| tb.format().expect("raised exception will have a traceback" )) |
114 | .unwrap_or_default(); |
115 | ::std::panic!("Can not import module {}: {} \n{}" , stringify!($module), err, traceback); |
116 | }); |
117 | let cls = imp.getattr(stringify!($name)).expect(concat!( |
118 | "Can not load exception class: {}.{}" , |
119 | stringify!($module), |
120 | "." , |
121 | stringify!($name) |
122 | )); |
123 | |
124 | cls.extract() |
125 | .expect("Imported exception should be a type object" ) |
126 | }) |
127 | .as_ptr() as *mut _ |
128 | } |
129 | } |
130 | }; |
131 | } |
132 | |
133 | /// Defines a new exception type. |
134 | /// |
135 | /// # Syntax |
136 | /// |
137 | /// * `module` is the name of the containing module. |
138 | /// * `name` is the name of the new exception type. |
139 | /// * `base` is the base class of `MyError`, usually [`PyException`]. |
140 | /// * `doc` (optional) is the docstring visible to users (with `.__doc__` and `help()`) and |
141 | /// accompanies your error type in your crate's documentation. |
142 | /// |
143 | /// # Examples |
144 | /// |
145 | /// ``` |
146 | /// use pyo3::prelude::*; |
147 | /// use pyo3::create_exception; |
148 | /// use pyo3::exceptions::PyException; |
149 | /// |
150 | /// create_exception!(my_module, MyError, PyException, "Some description." ); |
151 | /// |
152 | /// #[pyfunction] |
153 | /// fn raise_myerror() -> PyResult<()> { |
154 | /// let err = MyError::new_err("Some error happened." ); |
155 | /// Err(err) |
156 | /// } |
157 | /// |
158 | /// #[pymodule] |
159 | /// fn my_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { |
160 | /// m.add("MyError" , py.get_type::<MyError>())?; |
161 | /// m.add_function(wrap_pyfunction!(raise_myerror, py)?)?; |
162 | /// Ok(()) |
163 | /// } |
164 | /// # fn main() -> PyResult<()> { |
165 | /// # Python::with_gil(|py| -> PyResult<()> { |
166 | /// # let fun = wrap_pyfunction!(raise_myerror, py)?; |
167 | /// # let locals = pyo3::types::PyDict::new(py); |
168 | /// # locals.set_item("MyError" , py.get_type::<MyError>())?; |
169 | /// # locals.set_item("raise_myerror" , fun)?; |
170 | /// # |
171 | /// # py.run( |
172 | /// # "try: |
173 | /// # raise_myerror() |
174 | /// # except MyError as e: |
175 | /// # assert e.__doc__ == 'Some description.' |
176 | /// # assert str(e) == 'Some error happened.'" , |
177 | /// # None, |
178 | /// # Some(locals), |
179 | /// # )?; |
180 | /// # |
181 | /// # Ok(()) |
182 | /// # }) |
183 | /// # } |
184 | /// ``` |
185 | /// |
186 | /// Python code can handle this exception like any other exception: |
187 | /// |
188 | /// ```python |
189 | /// from my_module import MyError, raise_myerror |
190 | /// |
191 | /// try: |
192 | /// raise_myerror() |
193 | /// except MyError as e: |
194 | /// assert e.__doc__ == 'Some description.' |
195 | /// assert str(e) == 'Some error happened.' |
196 | /// ``` |
197 | /// |
198 | #[macro_export ] |
199 | macro_rules! create_exception { |
200 | ($module: expr, $name: ident, $base: ty) => { |
201 | #[repr(transparent)] |
202 | #[allow(non_camel_case_types)] // E.g. `socket.herror` |
203 | pub struct $name($crate::PyAny); |
204 | |
205 | $crate::impl_exception_boilerplate!($name); |
206 | |
207 | $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::None); |
208 | }; |
209 | ($module: expr, $name: ident, $base: ty, $doc: expr) => { |
210 | #[repr(transparent)] |
211 | #[allow(non_camel_case_types)] // E.g. `socket.herror` |
212 | #[doc = $doc] |
213 | pub struct $name($crate::PyAny); |
214 | |
215 | $crate::impl_exception_boilerplate!($name); |
216 | |
217 | $crate::create_exception_type_object!( |
218 | $module, |
219 | $name, |
220 | $base, |
221 | ::std::option::Option::Some($doc) |
222 | ); |
223 | }; |
224 | } |
225 | |
226 | /// `impl PyTypeInfo for $name` where `$name` is an |
227 | /// exception newly defined in Rust code. |
228 | #[doc (hidden)] |
229 | #[macro_export ] |
230 | macro_rules! create_exception_type_object { |
231 | ($module: expr, $name: ident, $base: ty, $doc: expr) => { |
232 | $crate::pyobject_native_type_core!( |
233 | $name, |
234 | $name::type_object_raw, |
235 | #module=::std::option::Option::Some(stringify!($module)) |
236 | ); |
237 | |
238 | impl $name { |
239 | fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { |
240 | use $crate::sync::GILOnceCell; |
241 | static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> = |
242 | GILOnceCell::new(); |
243 | |
244 | TYPE_OBJECT |
245 | .get_or_init(py, || |
246 | $crate::PyErr::new_type( |
247 | py, |
248 | concat!(stringify!($module), "." , stringify!($name)), |
249 | $doc, |
250 | ::std::option::Option::Some(py.get_type::<$base>()), |
251 | ::std::option::Option::None, |
252 | ).expect("Failed to initialize new exception type." ) |
253 | ).as_ptr() as *mut $crate::ffi::PyTypeObject |
254 | } |
255 | } |
256 | }; |
257 | } |
258 | |
259 | macro_rules! impl_native_exception ( |
260 | ($name:ident, $exc_name:ident, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => ( |
261 | #[doc = $doc] |
262 | #[allow(clippy::upper_case_acronyms)] |
263 | pub struct $name($crate::PyAny); |
264 | |
265 | $crate::impl_exception_boilerplate!($name); |
266 | $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject } $(, #checkfunction=$checkfunction)?); |
267 | ); |
268 | ($name:ident, $exc_name:ident, $doc:expr) => ( |
269 | impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject); |
270 | ) |
271 | ); |
272 | |
273 | #[cfg (windows)] |
274 | macro_rules! impl_windows_native_exception ( |
275 | ($name:ident, $exc_name:ident, $doc:expr, $layout:path) => ( |
276 | #[cfg(windows)] |
277 | #[doc = $doc] |
278 | #[allow(clippy::upper_case_acronyms)] |
279 | pub struct $name($crate::PyAny); |
280 | |
281 | $crate::impl_exception_boilerplate!($name); |
282 | $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }); |
283 | ); |
284 | ($name:ident, $exc_name:ident, $doc:expr) => ( |
285 | impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject); |
286 | ) |
287 | ); |
288 | |
289 | macro_rules! native_doc( |
290 | ($name: literal, $alt: literal) => ( |
291 | concat!( |
292 | "Represents Python's [`" , $name, "`](https://docs.python.org/3/library/exceptions.html#" , $name, ") exception. |
293 | |
294 | " , $alt |
295 | ) |
296 | ); |
297 | ($name: literal) => ( |
298 | concat!( |
299 | " |
300 | Represents Python's [`" , $name, "`](https://docs.python.org/3/library/exceptions.html#" , $name, ") exception. |
301 | |
302 | # Example: Raising " , $name, " from Rust |
303 | |
304 | This exception can be sent to Python code by converting it into a |
305 | [`PyErr`](crate::PyErr), where Python code can then catch it. |
306 | ``` |
307 | use pyo3::prelude::*; |
308 | use pyo3::exceptions::Py" , $name, "; |
309 | |
310 | #[pyfunction] |
311 | fn always_throws() -> PyResult<()> { |
312 | let message = \"I'm " , $name ,", and I was raised from Rust. \"; |
313 | Err(Py" , $name, "::new_err(message)) |
314 | } |
315 | # |
316 | # Python::with_gil(|py| { |
317 | # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap(); |
318 | # let err = fun.call0().expect_err( \"called a function that should always return an error but the return value was Ok \"); |
319 | # assert!(err.is_instance_of::<Py" , $name, ">(py)) |
320 | # }); |
321 | ``` |
322 | |
323 | Python code: |
324 | ```python |
325 | from my_module import always_throws |
326 | |
327 | try: |
328 | always_throws() |
329 | except " , $name, " as e: |
330 | print(f \"Caught an exception: {e} \") |
331 | ``` |
332 | |
333 | # Example: Catching " , $name, " in Rust |
334 | |
335 | ``` |
336 | use pyo3::prelude::*; |
337 | use pyo3::exceptions::Py" , $name, "; |
338 | |
339 | Python::with_gil(|py| { |
340 | let result: PyResult<()> = py.run( \"raise " , $name, " \", None, None); |
341 | |
342 | let error_type = match result { |
343 | Ok(_) => \"Not an error \", |
344 | Err(error) if error.is_instance_of::<Py" , $name, ">(py) => \"" , $name, " \", |
345 | Err(_) => \"Some other error \", |
346 | }; |
347 | |
348 | assert_eq!(error_type, \"" , $name, " \"); |
349 | }); |
350 | ``` |
351 | " |
352 | ) |
353 | ); |
354 | ); |
355 | |
356 | impl_native_exception!( |
357 | PyBaseException, |
358 | PyExc_BaseException, |
359 | native_doc!("BaseException" ), |
360 | ffi::PyBaseExceptionObject, |
361 | #checkfunction=ffi::PyExceptionInstance_Check |
362 | ); |
363 | impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception" )); |
364 | impl_native_exception!( |
365 | PyStopAsyncIteration, |
366 | PyExc_StopAsyncIteration, |
367 | native_doc!("StopAsyncIteration" ) |
368 | ); |
369 | impl_native_exception!( |
370 | PyStopIteration, |
371 | PyExc_StopIteration, |
372 | native_doc!("StopIteration" ), |
373 | ffi::PyStopIterationObject |
374 | ); |
375 | impl_native_exception!( |
376 | PyGeneratorExit, |
377 | PyExc_GeneratorExit, |
378 | native_doc!("GeneratorExit" ) |
379 | ); |
380 | impl_native_exception!( |
381 | PyArithmeticError, |
382 | PyExc_ArithmeticError, |
383 | native_doc!("ArithmeticError" ) |
384 | ); |
385 | impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError" )); |
386 | |
387 | impl_native_exception!( |
388 | PyAssertionError, |
389 | PyExc_AssertionError, |
390 | native_doc!("AssertionError" ) |
391 | ); |
392 | impl_native_exception!( |
393 | PyAttributeError, |
394 | PyExc_AttributeError, |
395 | native_doc!("AttributeError" ) |
396 | ); |
397 | impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError" )); |
398 | impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError" )); |
399 | impl_native_exception!( |
400 | PyFloatingPointError, |
401 | PyExc_FloatingPointError, |
402 | native_doc!("FloatingPointError" ) |
403 | ); |
404 | #[cfg (not(PyPy))] |
405 | impl_native_exception!( |
406 | PyOSError, |
407 | PyExc_OSError, |
408 | native_doc!("OSError" ), |
409 | ffi::PyOSErrorObject |
410 | ); |
411 | #[cfg (PyPy)] |
412 | impl_native_exception!(PyOSError, PyExc_OSError, native_doc!("OSError" )); |
413 | impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError" )); |
414 | |
415 | impl_native_exception!( |
416 | PyModuleNotFoundError, |
417 | PyExc_ModuleNotFoundError, |
418 | native_doc!("ModuleNotFoundError" ) |
419 | ); |
420 | |
421 | impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError" )); |
422 | impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError" )); |
423 | impl_native_exception!( |
424 | PyKeyboardInterrupt, |
425 | PyExc_KeyboardInterrupt, |
426 | native_doc!("KeyboardInterrupt" ) |
427 | ); |
428 | impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError" )); |
429 | impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError" )); |
430 | impl_native_exception!( |
431 | PyOverflowError, |
432 | PyExc_OverflowError, |
433 | native_doc!("OverflowError" ) |
434 | ); |
435 | impl_native_exception!( |
436 | PyRuntimeError, |
437 | PyExc_RuntimeError, |
438 | native_doc!("RuntimeError" ) |
439 | ); |
440 | impl_native_exception!( |
441 | PyRecursionError, |
442 | PyExc_RecursionError, |
443 | native_doc!("RecursionError" ) |
444 | ); |
445 | impl_native_exception!( |
446 | PyNotImplementedError, |
447 | PyExc_NotImplementedError, |
448 | native_doc!("NotImplementedError" ) |
449 | ); |
450 | #[cfg (not(PyPy))] |
451 | impl_native_exception!( |
452 | PySyntaxError, |
453 | PyExc_SyntaxError, |
454 | native_doc!("SyntaxError" ), |
455 | ffi::PySyntaxErrorObject |
456 | ); |
457 | #[cfg (PyPy)] |
458 | impl_native_exception!(PySyntaxError, PyExc_SyntaxError, native_doc!("SyntaxError" )); |
459 | impl_native_exception!( |
460 | PyReferenceError, |
461 | PyExc_ReferenceError, |
462 | native_doc!("ReferenceError" ) |
463 | ); |
464 | impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError" )); |
465 | #[cfg (not(PyPy))] |
466 | impl_native_exception!( |
467 | PySystemExit, |
468 | PyExc_SystemExit, |
469 | native_doc!("SystemExit" ), |
470 | ffi::PySystemExitObject |
471 | ); |
472 | #[cfg (PyPy)] |
473 | impl_native_exception!(PySystemExit, PyExc_SystemExit, native_doc!("SystemExit" )); |
474 | impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError" )); |
475 | impl_native_exception!( |
476 | PyUnboundLocalError, |
477 | PyExc_UnboundLocalError, |
478 | native_doc!("UnboundLocalError" ) |
479 | ); |
480 | #[cfg (not(PyPy))] |
481 | impl_native_exception!( |
482 | PyUnicodeError, |
483 | PyExc_UnicodeError, |
484 | native_doc!("UnicodeError" ), |
485 | ffi::PyUnicodeErrorObject |
486 | ); |
487 | #[cfg (PyPy)] |
488 | impl_native_exception!( |
489 | PyUnicodeError, |
490 | PyExc_UnicodeError, |
491 | native_doc!("UnicodeError" ) |
492 | ); |
493 | // these four errors need arguments, so they're too annoying to write tests for using macros... |
494 | impl_native_exception!( |
495 | PyUnicodeDecodeError, |
496 | PyExc_UnicodeDecodeError, |
497 | native_doc!("UnicodeDecodeError" , "" ) |
498 | ); |
499 | impl_native_exception!( |
500 | PyUnicodeEncodeError, |
501 | PyExc_UnicodeEncodeError, |
502 | native_doc!("UnicodeEncodeError" , "" ) |
503 | ); |
504 | impl_native_exception!( |
505 | PyUnicodeTranslateError, |
506 | PyExc_UnicodeTranslateError, |
507 | native_doc!("UnicodeTranslateError" , "" ) |
508 | ); |
509 | #[cfg (Py_3_11)] |
510 | impl_native_exception!( |
511 | PyBaseExceptionGroup, |
512 | PyExc_BaseExceptionGroup, |
513 | native_doc!("BaseExceptionGroup" , "" ) |
514 | ); |
515 | impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError" )); |
516 | impl_native_exception!( |
517 | PyZeroDivisionError, |
518 | PyExc_ZeroDivisionError, |
519 | native_doc!("ZeroDivisionError" ) |
520 | ); |
521 | |
522 | impl_native_exception!( |
523 | PyBlockingIOError, |
524 | PyExc_BlockingIOError, |
525 | native_doc!("BlockingIOError" ) |
526 | ); |
527 | impl_native_exception!( |
528 | PyBrokenPipeError, |
529 | PyExc_BrokenPipeError, |
530 | native_doc!("BrokenPipeError" ) |
531 | ); |
532 | impl_native_exception!( |
533 | PyChildProcessError, |
534 | PyExc_ChildProcessError, |
535 | native_doc!("ChildProcessError" ) |
536 | ); |
537 | impl_native_exception!( |
538 | PyConnectionError, |
539 | PyExc_ConnectionError, |
540 | native_doc!("ConnectionError" ) |
541 | ); |
542 | impl_native_exception!( |
543 | PyConnectionAbortedError, |
544 | PyExc_ConnectionAbortedError, |
545 | native_doc!("ConnectionAbortedError" ) |
546 | ); |
547 | impl_native_exception!( |
548 | PyConnectionRefusedError, |
549 | PyExc_ConnectionRefusedError, |
550 | native_doc!("ConnectionRefusedError" ) |
551 | ); |
552 | impl_native_exception!( |
553 | PyConnectionResetError, |
554 | PyExc_ConnectionResetError, |
555 | native_doc!("ConnectionResetError" ) |
556 | ); |
557 | impl_native_exception!( |
558 | PyFileExistsError, |
559 | PyExc_FileExistsError, |
560 | native_doc!("FileExistsError" ) |
561 | ); |
562 | impl_native_exception!( |
563 | PyFileNotFoundError, |
564 | PyExc_FileNotFoundError, |
565 | native_doc!("FileNotFoundError" ) |
566 | ); |
567 | impl_native_exception!( |
568 | PyInterruptedError, |
569 | PyExc_InterruptedError, |
570 | native_doc!("InterruptedError" ) |
571 | ); |
572 | impl_native_exception!( |
573 | PyIsADirectoryError, |
574 | PyExc_IsADirectoryError, |
575 | native_doc!("IsADirectoryError" ) |
576 | ); |
577 | impl_native_exception!( |
578 | PyNotADirectoryError, |
579 | PyExc_NotADirectoryError, |
580 | native_doc!("NotADirectoryError" ) |
581 | ); |
582 | impl_native_exception!( |
583 | PyPermissionError, |
584 | PyExc_PermissionError, |
585 | native_doc!("PermissionError" ) |
586 | ); |
587 | impl_native_exception!( |
588 | PyProcessLookupError, |
589 | PyExc_ProcessLookupError, |
590 | native_doc!("ProcessLookupError" ) |
591 | ); |
592 | impl_native_exception!( |
593 | PyTimeoutError, |
594 | PyExc_TimeoutError, |
595 | native_doc!("TimeoutError" ) |
596 | ); |
597 | |
598 | impl_native_exception!( |
599 | PyEnvironmentError, |
600 | PyExc_EnvironmentError, |
601 | native_doc!("EnvironmentError" ) |
602 | ); |
603 | impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError" )); |
604 | |
605 | #[cfg (windows)] |
606 | impl_windows_native_exception!( |
607 | PyWindowsError, |
608 | PyExc_WindowsError, |
609 | native_doc!("WindowsError" ) |
610 | ); |
611 | |
612 | impl PyUnicodeDecodeError { |
613 | /// Creates a Python `UnicodeDecodeError`. |
614 | pub fn new<'p>( |
615 | py: Python<'p>, |
616 | encoding: &CStr, |
617 | input: &[u8], |
618 | range: ops::Range<usize>, |
619 | reason: &CStr, |
620 | ) -> PyResult<&'p PyUnicodeDecodeError> { |
621 | unsafe { |
622 | py.from_owned_ptr_or_err(ffi::PyUnicodeDecodeError_Create( |
623 | encoding.as_ptr(), |
624 | input.as_ptr() as *const c_char, |
625 | input.len() as ffi::Py_ssize_t, |
626 | range.start as ffi::Py_ssize_t, |
627 | range.end as ffi::Py_ssize_t, |
628 | reason.as_ptr(), |
629 | )) |
630 | } |
631 | } |
632 | |
633 | /// Creates a Python `UnicodeDecodeError` from a Rust UTF-8 decoding error. |
634 | /// |
635 | /// # Examples |
636 | /// |
637 | /// ``` |
638 | /// #![cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))] |
639 | /// use pyo3::prelude::*; |
640 | /// use pyo3::exceptions::PyUnicodeDecodeError; |
641 | /// |
642 | /// # fn main() -> PyResult<()> { |
643 | /// Python::with_gil(|py| { |
644 | /// let invalid_utf8 = b"fo \xd8o" ; |
645 | /// let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8" ); |
646 | /// let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)?; |
647 | /// assert_eq!( |
648 | /// decode_err.to_string(), |
649 | /// "'utf-8' codec can't decode byte 0xd8 in position 2: invalid utf-8" |
650 | /// ); |
651 | /// Ok(()) |
652 | /// }) |
653 | /// # } |
654 | pub fn new_utf8<'p>( |
655 | py: Python<'p>, |
656 | input: &[u8], |
657 | err: std::str::Utf8Error, |
658 | ) -> PyResult<&'p PyUnicodeDecodeError> { |
659 | let pos = err.valid_up_to(); |
660 | PyUnicodeDecodeError::new( |
661 | py, |
662 | CStr::from_bytes_with_nul(b"utf-8 \0" ).unwrap(), |
663 | input, |
664 | pos..(pos + 1), |
665 | CStr::from_bytes_with_nul(b"invalid utf-8 \0" ).unwrap(), |
666 | ) |
667 | } |
668 | } |
669 | |
670 | impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning" )); |
671 | impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning" )); |
672 | impl_native_exception!( |
673 | PyDeprecationWarning, |
674 | PyExc_DeprecationWarning, |
675 | native_doc!("DeprecationWarning" ) |
676 | ); |
677 | impl_native_exception!( |
678 | PyPendingDeprecationWarning, |
679 | PyExc_PendingDeprecationWarning, |
680 | native_doc!("PendingDeprecationWarning" ) |
681 | ); |
682 | impl_native_exception!( |
683 | PySyntaxWarning, |
684 | PyExc_SyntaxWarning, |
685 | native_doc!("SyntaxWarning" ) |
686 | ); |
687 | impl_native_exception!( |
688 | PyRuntimeWarning, |
689 | PyExc_RuntimeWarning, |
690 | native_doc!("RuntimeWarning" ) |
691 | ); |
692 | impl_native_exception!( |
693 | PyFutureWarning, |
694 | PyExc_FutureWarning, |
695 | native_doc!("FutureWarning" ) |
696 | ); |
697 | impl_native_exception!( |
698 | PyImportWarning, |
699 | PyExc_ImportWarning, |
700 | native_doc!("ImportWarning" ) |
701 | ); |
702 | impl_native_exception!( |
703 | PyUnicodeWarning, |
704 | PyExc_UnicodeWarning, |
705 | native_doc!("UnicodeWarning" ) |
706 | ); |
707 | impl_native_exception!( |
708 | PyBytesWarning, |
709 | PyExc_BytesWarning, |
710 | native_doc!("BytesWarning" ) |
711 | ); |
712 | impl_native_exception!( |
713 | PyResourceWarning, |
714 | PyExc_ResourceWarning, |
715 | native_doc!("ResourceWarning" ) |
716 | ); |
717 | |
718 | #[cfg (Py_3_10)] |
719 | impl_native_exception!( |
720 | PyEncodingWarning, |
721 | PyExc_EncodingWarning, |
722 | native_doc!("EncodingWarning" ) |
723 | ); |
724 | |
725 | #[cfg (test)] |
726 | macro_rules! test_exception { |
727 | ($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => { |
728 | #[allow(non_snake_case)] |
729 | #[test] |
730 | fn $exc_ty () { |
731 | use super::$exc_ty; |
732 | |
733 | $crate::Python::with_gil(|py| { |
734 | use std::error::Error; |
735 | let err: $crate::PyErr = { |
736 | None |
737 | $( |
738 | .or(Some({ let $py = py; $constructor })) |
739 | )? |
740 | .unwrap_or($exc_ty::new_err("a test exception" )) |
741 | }; |
742 | |
743 | assert!(err.is_instance_of::<$exc_ty>(py)); |
744 | |
745 | let value: &$exc_ty = err.value(py).downcast().unwrap(); |
746 | assert!(value.source().is_none()); |
747 | |
748 | err.set_cause(py, Some($crate::exceptions::PyValueError::new_err("a cause" ))); |
749 | assert!(value.source().is_some()); |
750 | |
751 | assert!($crate::PyErr::from(value).is_instance_of::<$exc_ty>(py)); |
752 | }) |
753 | } |
754 | }; |
755 | } |
756 | |
757 | /// Exceptions defined in Python's [`asyncio`](https://docs.python.org/3/library/asyncio.html) |
758 | /// module. |
759 | pub mod asyncio { |
760 | import_exception!(asyncio, CancelledError); |
761 | import_exception!(asyncio, InvalidStateError); |
762 | import_exception!(asyncio, TimeoutError); |
763 | import_exception!(asyncio, IncompleteReadError); |
764 | import_exception!(asyncio, LimitOverrunError); |
765 | import_exception!(asyncio, QueueEmpty); |
766 | import_exception!(asyncio, QueueFull); |
767 | |
768 | #[cfg (test)] |
769 | mod tests { |
770 | test_exception!(CancelledError); |
771 | test_exception!(InvalidStateError); |
772 | test_exception!(TimeoutError); |
773 | test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err(( |
774 | "partial" , "expected" |
775 | ))); |
776 | test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err(( |
777 | "message" , "consumed" |
778 | ))); |
779 | test_exception!(QueueEmpty); |
780 | test_exception!(QueueFull); |
781 | } |
782 | } |
783 | |
784 | /// Exceptions defined in Python's [`socket`](https://docs.python.org/3/library/socket.html) |
785 | /// module. |
786 | pub mod socket { |
787 | import_exception!(socket, herror); |
788 | import_exception!(socket, gaierror); |
789 | import_exception!(socket, timeout); |
790 | |
791 | #[cfg (test)] |
792 | mod tests { |
793 | test_exception!(herror); |
794 | test_exception!(gaierror); |
795 | test_exception!(timeout); |
796 | } |
797 | } |
798 | |
799 | #[cfg (test)] |
800 | mod tests { |
801 | use super::*; |
802 | use crate::types::{IntoPyDict, PyDict}; |
803 | use crate::{PyErr, Python}; |
804 | |
805 | import_exception!(socket, gaierror); |
806 | import_exception!(email.errors, MessageError); |
807 | |
808 | #[test ] |
809 | fn test_check_exception() { |
810 | Python::with_gil(|py| { |
811 | let err: PyErr = gaierror::new_err(()); |
812 | let socket = py |
813 | .import("socket" ) |
814 | .map_err(|e| e.display(py)) |
815 | .expect("could not import socket" ); |
816 | |
817 | let d = PyDict::new(py); |
818 | d.set_item("socket" , socket) |
819 | .map_err(|e| e.display(py)) |
820 | .expect("could not setitem" ); |
821 | |
822 | d.set_item("exc" , err) |
823 | .map_err(|e| e.display(py)) |
824 | .expect("could not setitem" ); |
825 | |
826 | py.run("assert isinstance(exc, socket.gaierror)" , None, Some(d)) |
827 | .map_err(|e| e.display(py)) |
828 | .expect("assertion failed" ); |
829 | }); |
830 | } |
831 | |
832 | #[test ] |
833 | fn test_check_exception_nested() { |
834 | Python::with_gil(|py| { |
835 | let err: PyErr = MessageError::new_err(()); |
836 | let email = py |
837 | .import("email" ) |
838 | .map_err(|e| e.display(py)) |
839 | .expect("could not import email" ); |
840 | |
841 | let d = PyDict::new(py); |
842 | d.set_item("email" , email) |
843 | .map_err(|e| e.display(py)) |
844 | .expect("could not setitem" ); |
845 | d.set_item("exc" , err) |
846 | .map_err(|e| e.display(py)) |
847 | .expect("could not setitem" ); |
848 | |
849 | py.run( |
850 | "assert isinstance(exc, email.errors.MessageError)" , |
851 | None, |
852 | Some(d), |
853 | ) |
854 | .map_err(|e| e.display(py)) |
855 | .expect("assertion failed" ); |
856 | }); |
857 | } |
858 | |
859 | #[test ] |
860 | fn custom_exception() { |
861 | create_exception!(mymodule, CustomError, PyException); |
862 | |
863 | Python::with_gil(|py| { |
864 | let error_type = py.get_type::<CustomError>(); |
865 | let ctx = [("CustomError" , error_type)].into_py_dict(py); |
866 | let type_description: String = py |
867 | .eval("str(CustomError)" , None, Some(ctx)) |
868 | .unwrap() |
869 | .extract() |
870 | .unwrap(); |
871 | assert_eq!(type_description, "<class 'mymodule.CustomError'>" ); |
872 | py.run( |
873 | "assert CustomError('oops').args == ('oops',)" , |
874 | None, |
875 | Some(ctx), |
876 | ) |
877 | .unwrap(); |
878 | py.run("assert CustomError.__doc__ is None" , None, Some(ctx)) |
879 | .unwrap(); |
880 | }); |
881 | } |
882 | |
883 | #[test ] |
884 | fn custom_exception_dotted_module() { |
885 | create_exception!(mymodule.exceptions, CustomError, PyException); |
886 | Python::with_gil(|py| { |
887 | let error_type = py.get_type::<CustomError>(); |
888 | let ctx = [("CustomError" , error_type)].into_py_dict(py); |
889 | let type_description: String = py |
890 | .eval("str(CustomError)" , None, Some(ctx)) |
891 | .unwrap() |
892 | .extract() |
893 | .unwrap(); |
894 | assert_eq!( |
895 | type_description, |
896 | "<class 'mymodule.exceptions.CustomError'>" |
897 | ); |
898 | }); |
899 | } |
900 | |
901 | #[test ] |
902 | fn custom_exception_doc() { |
903 | create_exception!(mymodule, CustomError, PyException, "Some docs" ); |
904 | |
905 | Python::with_gil(|py| { |
906 | let error_type = py.get_type::<CustomError>(); |
907 | let ctx = [("CustomError" , error_type)].into_py_dict(py); |
908 | let type_description: String = py |
909 | .eval("str(CustomError)" , None, Some(ctx)) |
910 | .unwrap() |
911 | .extract() |
912 | .unwrap(); |
913 | assert_eq!(type_description, "<class 'mymodule.CustomError'>" ); |
914 | py.run( |
915 | "assert CustomError('oops').args == ('oops',)" , |
916 | None, |
917 | Some(ctx), |
918 | ) |
919 | .unwrap(); |
920 | py.run("assert CustomError.__doc__ == 'Some docs'" , None, Some(ctx)) |
921 | .unwrap(); |
922 | }); |
923 | } |
924 | |
925 | #[test ] |
926 | fn custom_exception_doc_expr() { |
927 | create_exception!( |
928 | mymodule, |
929 | CustomError, |
930 | PyException, |
931 | concat!("Some" , " more " , stringify!(docs)) |
932 | ); |
933 | |
934 | Python::with_gil(|py| { |
935 | let error_type = py.get_type::<CustomError>(); |
936 | let ctx = [("CustomError" , error_type)].into_py_dict(py); |
937 | let type_description: String = py |
938 | .eval("str(CustomError)" , None, Some(ctx)) |
939 | .unwrap() |
940 | .extract() |
941 | .unwrap(); |
942 | assert_eq!(type_description, "<class 'mymodule.CustomError'>" ); |
943 | py.run( |
944 | "assert CustomError('oops').args == ('oops',)" , |
945 | None, |
946 | Some(ctx), |
947 | ) |
948 | .unwrap(); |
949 | py.run( |
950 | "assert CustomError.__doc__ == 'Some more docs'" , |
951 | None, |
952 | Some(ctx), |
953 | ) |
954 | .unwrap(); |
955 | }); |
956 | } |
957 | |
958 | #[test ] |
959 | fn native_exception_debug() { |
960 | Python::with_gil(|py| { |
961 | let exc = py |
962 | .run("raise Exception('banana')" , None, None) |
963 | .expect_err("raising should have given us an error" ) |
964 | .into_value(py) |
965 | .into_ref(py); |
966 | assert_eq!( |
967 | format!(" {:?}" , exc), |
968 | exc.repr().unwrap().extract::<String>().unwrap() |
969 | ); |
970 | }); |
971 | } |
972 | |
973 | #[test ] |
974 | fn native_exception_display() { |
975 | Python::with_gil(|py| { |
976 | let exc = py |
977 | .run("raise Exception('banana')" , None, None) |
978 | .expect_err("raising should have given us an error" ) |
979 | .into_value(py) |
980 | .into_ref(py); |
981 | assert_eq!( |
982 | exc.to_string(), |
983 | exc.str().unwrap().extract::<String>().unwrap() |
984 | ); |
985 | }); |
986 | } |
987 | |
988 | #[test ] |
989 | fn native_exception_chain() { |
990 | use std::error::Error; |
991 | |
992 | Python::with_gil(|py| { |
993 | let exc = py |
994 | .run( |
995 | "raise Exception('banana') from TypeError('peach')" , |
996 | None, |
997 | None, |
998 | ) |
999 | .expect_err("raising should have given us an error" ) |
1000 | .into_value(py) |
1001 | .into_ref(py); |
1002 | |
1003 | assert_eq!(format!(" {:?}" , exc), "Exception('banana')" ); |
1004 | |
1005 | let source = exc.source().expect("cause should exist" ); |
1006 | |
1007 | assert_eq!(format!(" {:?}" , source), "TypeError('peach')" ); |
1008 | |
1009 | let source_source = source.source(); |
1010 | assert!(source_source.is_none(), "source_source should be None" ); |
1011 | }); |
1012 | } |
1013 | |
1014 | #[test ] |
1015 | fn unicode_decode_error() { |
1016 | let invalid_utf8 = b"fo \xd8o" ; |
1017 | #[cfg_attr (invalid_from_utf8_lint, allow(invalid_from_utf8))] |
1018 | let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8" ); |
1019 | Python::with_gil(|py| { |
1020 | let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap(); |
1021 | assert_eq!( |
1022 | format!(" {:?}" , decode_err), |
1023 | "UnicodeDecodeError('utf-8', b'fo \\xd8o', 2, 3, 'invalid utf-8')" |
1024 | ); |
1025 | |
1026 | // Restoring should preserve the same error |
1027 | let e: PyErr = decode_err.into(); |
1028 | e.restore(py); |
1029 | |
1030 | assert_eq!( |
1031 | PyErr::fetch(py).to_string(), |
1032 | "UnicodeDecodeError: \'utf-8 \' codec can \'t decode byte 0xd8 in position 2: invalid utf-8" |
1033 | ); |
1034 | }); |
1035 | } |
1036 | #[cfg (Py_3_11)] |
1037 | test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err(( |
1038 | "msg" , |
1039 | vec![PyValueError::new_err("err" )] |
1040 | ))); |
1041 | test_exception!(PyBaseException); |
1042 | test_exception!(PyException); |
1043 | test_exception!(PyStopAsyncIteration); |
1044 | test_exception!(PyStopIteration); |
1045 | test_exception!(PyGeneratorExit); |
1046 | test_exception!(PyArithmeticError); |
1047 | test_exception!(PyLookupError); |
1048 | test_exception!(PyAssertionError); |
1049 | test_exception!(PyAttributeError); |
1050 | test_exception!(PyBufferError); |
1051 | test_exception!(PyEOFError); |
1052 | test_exception!(PyFloatingPointError); |
1053 | test_exception!(PyOSError); |
1054 | test_exception!(PyImportError); |
1055 | test_exception!(PyModuleNotFoundError); |
1056 | test_exception!(PyIndexError); |
1057 | test_exception!(PyKeyError); |
1058 | test_exception!(PyKeyboardInterrupt); |
1059 | test_exception!(PyMemoryError); |
1060 | test_exception!(PyNameError); |
1061 | test_exception!(PyOverflowError); |
1062 | test_exception!(PyRuntimeError); |
1063 | test_exception!(PyRecursionError); |
1064 | test_exception!(PyNotImplementedError); |
1065 | test_exception!(PySyntaxError); |
1066 | test_exception!(PyReferenceError); |
1067 | test_exception!(PySystemError); |
1068 | test_exception!(PySystemExit); |
1069 | test_exception!(PyTypeError); |
1070 | test_exception!(PyUnboundLocalError); |
1071 | test_exception!(PyUnicodeError); |
1072 | test_exception!(PyUnicodeDecodeError, |py| { |
1073 | let invalid_utf8 = b"fo \xd8o" ; |
1074 | #[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))] |
1075 | let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8" ); |
1076 | PyErr::from_value(PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap()) |
1077 | }); |
1078 | test_exception!(PyUnicodeEncodeError, |py| py |
1079 | .eval("chr(40960).encode('ascii')" , None, None) |
1080 | .unwrap_err()); |
1081 | test_exception!(PyUnicodeTranslateError, |_| { |
1082 | PyUnicodeTranslateError::new_err((" \u{3042}" , 0, 1, "ouch" )) |
1083 | }); |
1084 | test_exception!(PyValueError); |
1085 | test_exception!(PyZeroDivisionError); |
1086 | test_exception!(PyBlockingIOError); |
1087 | test_exception!(PyBrokenPipeError); |
1088 | test_exception!(PyChildProcessError); |
1089 | test_exception!(PyConnectionError); |
1090 | test_exception!(PyConnectionAbortedError); |
1091 | test_exception!(PyConnectionRefusedError); |
1092 | test_exception!(PyConnectionResetError); |
1093 | test_exception!(PyFileExistsError); |
1094 | test_exception!(PyFileNotFoundError); |
1095 | test_exception!(PyInterruptedError); |
1096 | test_exception!(PyIsADirectoryError); |
1097 | test_exception!(PyNotADirectoryError); |
1098 | test_exception!(PyPermissionError); |
1099 | test_exception!(PyProcessLookupError); |
1100 | test_exception!(PyTimeoutError); |
1101 | test_exception!(PyEnvironmentError); |
1102 | test_exception!(PyIOError); |
1103 | #[cfg (windows)] |
1104 | test_exception!(PyWindowsError); |
1105 | |
1106 | test_exception!(PyWarning); |
1107 | test_exception!(PyUserWarning); |
1108 | test_exception!(PyDeprecationWarning); |
1109 | test_exception!(PyPendingDeprecationWarning); |
1110 | test_exception!(PySyntaxWarning); |
1111 | test_exception!(PyRuntimeWarning); |
1112 | test_exception!(PyFutureWarning); |
1113 | test_exception!(PyImportWarning); |
1114 | test_exception!(PyUnicodeWarning); |
1115 | test_exception!(PyBytesWarning); |
1116 | #[cfg (Py_3_10)] |
1117 | test_exception!(PyEncodingWarning); |
1118 | } |
1119 | |