diff --git a/help.html b/help.html index 4e65179..8905567 100644 --- a/help.html +++ b/help.html @@ -1 +1 @@ -Help

Rustdoc help

Back
\ No newline at end of file +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/index.html b/index.html index e7a1dfd..5dd9a51 100644 --- a/index.html +++ b/index.html @@ -1,2 +1,2 @@ -Index of crates

List of all crates

+Index of crates

List of all crates

\ No newline at end of file diff --git a/pyo3_error/all.html b/pyo3_error/all.html index 8f14988..06b38ff 100644 --- a/pyo3_error/all.html +++ b/pyo3_error/all.html @@ -1 +1 @@ -List of all items in this crate

List of all items

Functions

\ No newline at end of file +List of all items in this crate

List of all items

Structs

Traits

Functions

\ No newline at end of file diff --git a/pyo3_error/fn.add.html b/pyo3_error/fn.add.html deleted file mode 100644 index b9ee170..0000000 --- a/pyo3_error/fn.add.html +++ /dev/null @@ -1 +0,0 @@ -add in pyo3_error - Rust

Function pyo3_error::add

source ·
pub fn add(left: u64, right: u64) -> u64
\ No newline at end of file diff --git a/pyo3_error/fn.err_with_location.html b/pyo3_error/fn.err_with_location.html new file mode 100644 index 0000000..e55fac2 --- /dev/null +++ b/pyo3_error/fn.err_with_location.html @@ -0,0 +1,11 @@ +err_with_location in pyo3_error - Rust

Function pyo3_error::err_with_location

source ·
pub fn err_with_location(
+    py: Python<'_>,
+    err: PyErr,
+    file: &str,
+    line: u32,
+    column: u32,
+) -> PyErr
Expand description

Utility function to add a traceback with the error’s file, line, and +column location information to the err.

+

This function may be used when implementing AnyErrorToPyErr or +MapErrorToPyErr to pythonize any available error location information.

+
\ No newline at end of file diff --git a/pyo3_error/index.html b/pyo3_error/index.html index 5375abf..373addd 100644 --- a/pyo3_error/index.html +++ b/pyo3_error/index.html @@ -1,2 +1,7 @@ -pyo3_error - Rust

Crate pyo3_error

source ·
Expand description

CI Status MSRV Latest Version Rust Doc Crate Rust Doc Main

-

Functions§

\ No newline at end of file +pyo3_error - Rust

Crate pyo3_error

source ·
Expand description

CI Status MSRV Latest Version Rust Doc Crate Rust Doc Main

+

Unified error causality chains across Rust and Python using PyErrChain.

+

Structs§

Traits§

Functions§

  • Utility function to add a traceback with the error’s file, line, and +column location information to the err.
\ No newline at end of file diff --git a/pyo3_error/sidebar-items.js b/pyo3_error/sidebar-items.js index 8496288..99070d1 100644 --- a/pyo3_error/sidebar-items.js +++ b/pyo3_error/sidebar-items.js @@ -1 +1 @@ -window.SIDEBAR_ITEMS = {"fn":["add"]}; \ No newline at end of file +window.SIDEBAR_ITEMS = {"fn":["err_with_location"],"struct":["DowncastToPyErr","ErrorNoPyErr","IoErrorToPyErr","PyErrChain"],"trait":["AnyErrorToPyErr","MapErrorToPyErr"]}; \ No newline at end of file diff --git a/pyo3_error/struct.DowncastToPyErr.html b/pyo3_error/struct.DowncastToPyErr.html new file mode 100644 index 0000000..836533b --- /dev/null +++ b/pyo3_error/struct.DowncastToPyErr.html @@ -0,0 +1,24 @@ +DowncastToPyErr in pyo3_error - Rust

Struct pyo3_error::DowncastToPyErr

source ·
pub struct DowncastToPyErr;
Expand description

Try to map a std::error::Error via a specific error type T to a +PyErr by downcasting when used as MapErrorToPyErr;

+

Trait Implementations§

source§

impl MapErrorToPyErr for DowncastToPyErr

source§

fn try_map<T: Error + 'static>( + _py: Python<'_>, + err: Box<dyn Error + 'static>, + map: impl FnOnce(Box<T>) -> PyErr, +) -> Result<PyErr, Box<dyn Error + 'static>>

Try to map from a boxed err via the specific error type T or wrapped +errors such as MyError<E> to a PyErr, or return the err. Read more
source§

fn try_map_ref<T: Error + 'static>( + _py: Python<'_>, + err: &(dyn Error + 'static), + map: impl FnOnce(&T) -> PyErr, +) -> Option<PyErr>

Try to map from an err reference via the specific error type T or +wrapped errors such as MyError<E> to a PyErr, or return None. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Ungil for T
where + T: Send,

\ No newline at end of file diff --git a/pyo3_error/struct.ErrorNoPyErr.html b/pyo3_error/struct.ErrorNoPyErr.html new file mode 100644 index 0000000..89564bf --- /dev/null +++ b/pyo3_error/struct.ErrorNoPyErr.html @@ -0,0 +1,21 @@ +ErrorNoPyErr in pyo3_error - Rust

Struct pyo3_error::ErrorNoPyErr

source ·
pub struct ErrorNoPyErr;
Expand description

Never attempt to translate any std::error::Error to PyErr when used +as AnyErrorToPyErr.

+

Trait Implementations§

source§

impl AnyErrorToPyErr for ErrorNoPyErr

source§

fn try_from_err<T: MapErrorToPyErr>( + _py: Python<'_>, + err: Box<dyn Error + 'static>, +) -> Result<PyErr, Box<dyn Error + 'static>>

Try to translate from a boxed err to a PyErr, or return the err. Read more
source§

fn try_from_err_ref<T: MapErrorToPyErr>( + _py: Python<'_>, + _err: &(dyn Error + 'static), +) -> Option<PyErr>

Try to translate from an err reference to a PyErr, or return +None. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Ungil for T
where + T: Send,

\ No newline at end of file diff --git a/pyo3_error/struct.IoErrorToPyErr.html b/pyo3_error/struct.IoErrorToPyErr.html new file mode 100644 index 0000000..f830acd --- /dev/null +++ b/pyo3_error/struct.IoErrorToPyErr.html @@ -0,0 +1,20 @@ +IoErrorToPyErr in pyo3_error - Rust

Struct pyo3_error::IoErrorToPyErr

source ·
pub struct IoErrorToPyErr;
Expand description

Translate std::io::Error to PyErr when used as AnyErrorToPyErr.

+

Trait Implementations§

source§

impl AnyErrorToPyErr for IoErrorToPyErr

source§

fn try_from_err<T: MapErrorToPyErr>( + py: Python<'_>, + err: Box<dyn Error + 'static>, +) -> Result<PyErr, Box<dyn Error + 'static>>

Try to translate from a boxed err to a PyErr, or return the err. Read more
source§

fn try_from_err_ref<T: MapErrorToPyErr>( + py: Python<'_>, + err: &(dyn Error + 'static), +) -> Option<PyErr>

Try to translate from an err reference to a PyErr, or return +None. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Ungil for T
where + T: Send,

\ No newline at end of file diff --git a/pyo3_error/struct.PyErrChain.html b/pyo3_error/struct.PyErrChain.html new file mode 100644 index 0000000..7b61e1c --- /dev/null +++ b/pyo3_error/struct.PyErrChain.html @@ -0,0 +1,56 @@ +PyErrChain in pyo3_error - Rust

Struct pyo3_error::PyErrChain

source ·
pub struct PyErrChain { /* private fields */ }
Expand description

PyErrChain wraps a PyErr together with its causality chain.

+

Unlike PyErr, PyErrChain’s implementation of std::error::Error +provides access to the error cause through the std::error::Error::source +method.

+

Note that since PyErrs can be readily cloned, the PyErrChain only +captures the causality chain at the time of construction. Calling +PyErr::set_cause on a clone of the wrapped error after construction will +thus not update the chain as captured by this PyErrChain.

+

Implementations§

source§

impl PyErrChain

source

pub fn new<T: Error + 'static>(py: Python<'_>, err: T) -> Self

Create a new PyErrChain from err.

+

The error’s causality chain, as expressed by +std::error::Error::source, is translated into a PyErr::cause +chain.

+

If any error in the chain is a PyErrChain or a PyErr, it is +extracted directly. All other error types are translated into PyErrs +using PyException::new_err with format!("{}", err).

+

If you want to customize the translation from std::error::Error into +PyErr, please use Self::new_with_downcaster instead.

+
source

pub fn new_with_downcaster<E: Error + 'static, T: AnyErrorToPyErr, M: MapErrorToPyErr>( + py: Python<'_>, + err: E, +) -> Self

Create a new PyErrChain from err using a custom translator from +std::error::Error to PyErr.

+

The error’s causality chain, as expressed by +std::error::Error::source, is translated into a PyErr::cause +chain.

+

If any error in the chain is a PyErrChain or a PyErr, it is +extracted directly. All other error types first attempt to be translated +into PyErrs using the [PyErrDowncaster] and [PyErrMapper]. As a +fallback, all remaining errors are translated into PyErrs using +PyException::new_err with format!("{}", err).

+
source

pub fn clone_ref(&self, py: Python<'_>) -> Self

Clone the PyErrChain.

+

This requires the GIL, which is why PyErrChain does not implement +Clone.

+

Note that all elements of the cloned PyErrChain will be shared using +reference counting in Python with the existing PyErrChain self.

+
source

pub const fn as_err(&self) -> &PyErr

Get a reference to the wrapped PyErr.

+

Note that while PyErr::set_cause can be called on the returned +PyErr, the change in causality chain will not be reflected in +this PyErrChain.

+
source

pub fn cause(&self) -> Option<&PyErr>

Get a reference to the cause of the wrapped PyErr.

+

Note that while PyErr::set_cause can be called on the returned +PyErr, the change in causality chain will not be reflected in +this PyErrChain.

+

Trait Implementations§

source§

impl Debug for PyErrChain

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for PyErrChain

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for PyErrChain

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
source§

impl From<PyErr> for PyErrChain

source§

fn from(err: PyErr) -> Self

Converts to this type from the input type.
source§

impl From<PyErrChain> for PyErr

source§

fn from(err: PyErrChain) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Ungil for T
where + T: Send,

\ No newline at end of file diff --git a/pyo3_error/trait.AnyErrorToPyErr.html b/pyo3_error/trait.AnyErrorToPyErr.html new file mode 100644 index 0000000..82b1fb5 --- /dev/null +++ b/pyo3_error/trait.AnyErrorToPyErr.html @@ -0,0 +1,33 @@ +AnyErrorToPyErr in pyo3_error - Rust

Trait pyo3_error::AnyErrorToPyErr

source ·
pub trait AnyErrorToPyErr {
+    // Required methods
+    fn try_from_err<T: MapErrorToPyErr>(
+        py: Python<'_>,
+        err: Box<dyn Error + 'static>,
+    ) -> Result<PyErr, Box<dyn Error + 'static>>;
+    fn try_from_err_ref<T: MapErrorToPyErr>(
+        py: Python<'_>,
+        err: &(dyn Error + 'static),
+    ) -> Option<PyErr>;
+}
Expand description

Utility trait to try to translate from std::error::Error to PyErr.

+

ErrorNoPyErr may be used to always fail at translating.

+

IoErrorToPyErr may be used to translate std::io::Error to PyErr.

+

Required Methods§

source

fn try_from_err<T: MapErrorToPyErr>( + py: Python<'_>, + err: Box<dyn Error + 'static>, +) -> Result<PyErr, Box<dyn Error + 'static>>

Try to translate from a boxed err to a PyErr, or return the err.

+

When a strongly typed translation from some specific error type E to a +PyErr is attempted, MapErrorToPyErr::try_map should be used to allow +the mapper to test for E in addition to wrapped errors such as +MyError<E>.

+
§Errors
+

Returns the original err if translating to a PyErr failed.

+
source

fn try_from_err_ref<T: MapErrorToPyErr>( + py: Python<'_>, + err: &(dyn Error + 'static), +) -> Option<PyErr>

Try to translate from an err reference to a PyErr, or return +None.

+

When a strongly typed translation from some specific error type E to a +PyErr is attempted, MapErrorToPyErr::try_map_ref should be used to +allow the mapper to test for E in addition to wrapped errors such as +MyError<E>.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/pyo3_error/trait.MapErrorToPyErr.html b/pyo3_error/trait.MapErrorToPyErr.html new file mode 100644 index 0000000..5ca45b9 --- /dev/null +++ b/pyo3_error/trait.MapErrorToPyErr.html @@ -0,0 +1,35 @@ +MapErrorToPyErr in pyo3_error - Rust

Trait pyo3_error::MapErrorToPyErr

source ·
pub trait MapErrorToPyErr {
+    // Required methods
+    fn try_map<T: Error + 'static>(
+        py: Python<'_>,
+        err: Box<dyn Error + 'static>,
+        map: impl FnOnce(Box<T>) -> PyErr,
+    ) -> Result<PyErr, Box<dyn Error + 'static>>;
+    fn try_map_ref<T: Error + 'static>(
+        py: Python<'_>,
+        err: &(dyn Error + 'static),
+        map: impl FnOnce(&T) -> PyErr,
+    ) -> Option<PyErr>;
+}
Expand description

Utility trait to try to translate via specific error types E implementing +std::error::Error and wrapped errors such as MyError<E> to PyErrs.

+

DowncastToPyErr may be used to only try to translate via E using +downcasting.

+

Required Methods§

source

fn try_map<T: Error + 'static>( + py: Python<'_>, + err: Box<dyn Error + 'static>, + map: impl FnOnce(Box<T>) -> PyErr, +) -> Result<PyErr, Box<dyn Error + 'static>>

Try to map from a boxed err via the specific error type T or wrapped +errors such as MyError<E> to a PyErr, or return the err.

+

The map function should be used to access the provided mapping from +T to PyErr.

+
§Errors
+

Returns the original err if mapping to a PyErr failed.

+
source

fn try_map_ref<T: Error + 'static>( + py: Python<'_>, + err: &(dyn Error + 'static), + map: impl FnOnce(&T) -> PyErr, +) -> Option<PyErr>

Try to map from an err reference via the specific error type T or +wrapped errors such as MyError<E> to a PyErr, or return None.

+

The map function should be used to access the provided mapping from +&T to PyErr.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/search-index.js b/search-index.js index b711b57..1a157c8 100644 --- a/search-index.js +++ b/search-index.js @@ -1,4 +1,4 @@ -var searchIndex = new Map(JSON.parse('[["pyo3_error",{"t":"H","n":["add"],"q":[[0,"pyo3_error"]],"i":"`","f":"{{bb}b}","D":"b","p":[[1,"u64"]],"r":[],"b":[],"c":"OjAAAAAAAAA=","e":"OjAAAAEAAAAAAAAAEAAAAAEA"}]]')); +var searchIndex = new Map(JSON.parse('[["pyo3_error",{"t":"KFFFKFNNNNNNNNNNNHNNNNNNNNNNNNNNNNNNNMNNMNNNNNNMNMNNNNN","n":["AnyErrorToPyErr","DowncastToPyErr","ErrorNoPyErr","IoErrorToPyErr","MapErrorToPyErr","PyErrChain","as_err","borrow","","","","borrow_mut","","","","cause","clone_ref","err_with_location","fmt","","from","","","","","into","","","","new","new_with_downcaster","source","to_string","try_from","","","","try_from_err","","","try_from_err_ref","","","try_into","","","","try_map","","try_map_ref","","type_id","","",""],"q":[[0,"pyo3_error"],[55,"pyo3::err"],[56,"core::option"],[57,"pyo3::marker"],[58,"core::fmt"],[59,"core::error"],[60,"alloc::string"],[61,"core::result"],[62,"alloc::boxed"],[63,"core::ops::function"],[64,"core::any"]],"i":"``````b0BdBfBh321033`3333210321033333210Bj320324321Bl2025432","f":"``````{{{d{b}}}{{d{f}}}}{d{{d{c}}}{}}000{{{d{h}}}{{d{hc}}}{}}000{{{d{b}}}{{j{{d{f}}}}}}{{{d{b}}l}b}{{lf{d{n}}A`A`}f}{{{d{b}}{d{hAb}}}Ad}0{fb}{cc{}}000{{}c{}}000{{lc}bAf}0{{{d{b}}}{{j{{d{Af}}}}}}{dAh}{c{{Aj{e}}}{}{}}000{{l{Al{Af}}}{{Aj{f{Al{Af}}}}}}00{{l{d{Af}}}{{j{f}}}}00{{}{{Aj{c}}}{}}000{{l{Al{Af}}e}{{Aj{f{Al{Af}}}}}Af{{B`{{Al{c}}}{{An{f}}}}}}0{{l{d{Af}}e}{{j{f}}}Af{{B`{{d{c}}}{{An{f}}}}}}0{dBb}000","D":"Cb","p":[[5,"PyErrChain",0],[1,"reference"],[5,"PyErr",55],[0,"mut"],[6,"Option",56],[5,"Python",57],[1,"str"],[1,"u32"],[5,"Formatter",58],[8,"Result",58],[10,"Error",59],[5,"String",60],[6,"Result",61],[5,"Box",62],[17,"Output"],[10,"FnOnce",63],[5,"TypeId",64],[5,"ErrorNoPyErr",0],[5,"IoErrorToPyErr",0],[5,"DowncastToPyErr",0],[10,"AnyErrorToPyErr",0],[10,"MapErrorToPyErr",0]],"r":[],"b":[[18,"impl-Display-for-PyErrChain"],[19,"impl-Debug-for-PyErrChain"]],"c":"OjAAAAAAAAA=","e":"OzAAAAEAAB4ABwAIAAcAEwACACAABQAnAAEAKgAFADEAAAAzAAQA"}]]')); if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; else if (window.initSearch) window.initSearch(searchIndex); -//{"start":39,"fragment_lengths":[169]} \ No newline at end of file +//{"start":39,"fragment_lengths":[1745]} \ No newline at end of file diff --git a/search.desc/pyo3_error/pyo3_error-desc-0-.js b/search.desc/pyo3_error/pyo3_error-desc-0-.js index f3e5e36..98a0a54 100644 --- a/search.desc/pyo3_error/pyo3_error-desc-0-.js +++ b/search.desc/pyo3_error/pyo3_error-desc-0-.js @@ -1 +1 @@ -searchState.loadedDescShard("pyo3_error", 0, "CI Status MSRV Latest Version Rust Doc Crate Rust Doc Main") \ No newline at end of file +searchState.loadedDescShard("pyo3_error", 0, "CI Status MSRV Latest Version Rust Doc Crate Rust Doc Main\nUtility trait to try to translate from std::error::Error …\nTry to map a std::error::Error via a specific error type T …\nNever attempt to translate any std::error::Error to PyErr …\nTranslate std::io::Error to PyErr when used as …\nUtility trait to try to translate via specific error types …\nPyErrChain wraps a PyErr together with its causality chain.\nGet a reference to the wrapped PyErr.\nGet a reference to the cause of the wrapped PyErr.\nClone the PyErrChain.\nUtility function to add a traceback with the error’s file…\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nCreate a new PyErrChain from err.\nCreate a new PyErrChain from err using a custom translator …\nTry to translate from a boxed err to a PyErr, or return …\nTry to translate from an err reference to a PyErr, or …\nTry to map from a boxed err via the specific error type T …\nTry to map from an err reference via the specific error …") \ No newline at end of file diff --git a/settings.html b/settings.html index 370f3ce..b31b116 100644 --- a/settings.html +++ b/settings.html @@ -1 +1 @@ -Settings

Rustdoc settings

Back
\ No newline at end of file +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/src/pyo3_error/lib.rs.html b/src/pyo3_error/lib.rs.html index ab910e4..e60094a 100644 --- a/src/pyo3_error/lib.rs.html +++ b/src/pyo3_error/lib.rs.html @@ -1,4 +1,4 @@ -lib.rs - source
1
+lib.rs - source
1
 2
 3
 4
@@ -29,6 +29,525 @@
 29
 30
 31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
 
//! [![CI Status]][workflow] [![MSRV]][repo] [![Latest Version]][crates.io] [![Rust Doc Crate]][docs.rs] [![Rust Doc Main]][docs]
 //!
 //! [CI Status]: https://img.shields.io/github/actions/workflow/status/juntyr/pyo3-error/ci.yml?branch=main
@@ -45,9 +564,430 @@
 //!
 //! [Rust Doc Main]: https://img.shields.io/badge/docs-main-blue
 //! [docs]: https://juntyr.github.io/pyo3-error/pyo3_error
+//!
+//! Unified error causality chains across Rust and Python using [`PyErrChain`].
+
+use std::{borrow::Cow, error::Error, fmt, io};
+
+use pyo3::{exceptions::PyException, intern, prelude::*, sync::GILOnceCell, types::IntoPyDict};
+
+/// [`PyErrChain`] wraps a [`PyErr`] together with its causality chain.
+///
+/// Unlike [`PyErr`], [`PyErrChain`]'s implementation of [`std::error::Error`]
+/// provides access to the error cause through the [`std::error::Error::source`]
+/// method.
+///
+/// Note that since [`PyErr`]s can be readily cloned, the [`PyErrChain`] only
+/// captures the causality chain at the time of construction. Calling
+/// [`PyErr::set_cause`] on a clone of the wrapped error after construction will
+/// thus not update the chain as captured by this [`PyErrChain`].
+pub struct PyErrChain {
+    err: PyErr,
+    cause: Option<Box<Self>>,
+}
+
+impl PyErrChain {
+    /// Create a new [`PyErrChain`] from `err`.
+    ///
+    /// The error's causality chain, as expressed by
+    /// [`std::error::Error::source`], is translated into a [`PyErr::cause`]
+    /// chain.
+    ///
+    /// If any error in the chain is a [`PyErrChain`] or a [`PyErr`], it is
+    /// extracted directly. All other error types are translated into [`PyErr`]s
+    /// using [`PyException::new_err`] with `format!("{}", err)`.
+    ///
+    /// If you want to customize the translation from [`std::error::Error`] into
+    /// [`PyErr`], please use [`Self::new_with_downcaster`] instead.
+    #[must_use]
+    pub fn new<T: Error + 'static>(py: Python, err: T) -> Self {
+        Self::new_with_downcaster::<T, ErrorNoPyErr, DowncastToPyErr>(py, err)
+    }
+
+    /// Create a new [`PyErrChain`] from `err` using a custom translator from
+    /// [`std::error::Error`] to [`PyErr`].
+    ///
+    /// The error's causality chain, as expressed by
+    /// [`std::error::Error::source`], is translated into a [`PyErr::cause`]
+    /// chain.
+    ///
+    /// If any error in the chain is a [`PyErrChain`] or a [`PyErr`], it is
+    /// extracted directly. All other error types first attempt to be translated
+    /// into [`PyErr`]s using the [`PyErrDowncaster`] and [`PyErrMapper`]. As a
+    /// fallback, all remaining errors are translated into [`PyErr`]s using
+    /// [`PyException::new_err`] with `format!("{}", err)`.
+    #[must_use]
+    pub fn new_with_downcaster<E: Error + 'static, T: AnyErrorToPyErr, M: MapErrorToPyErr>(
+        py: Python,
+        err: E,
+    ) -> Self {
+        let err: Box<dyn Error + 'static> = Box::new(err);
+
+        let err = match err.downcast::<Self>() {
+            Ok(err) => return *err,
+            Err(err) => err,
+        };
+
+        let err = match err.downcast::<PyErr>() {
+            Ok(err) => return Self::from(*err),
+            Err(err) => err,
+        };
+
+        let mut stack = Vec::new();
+
+        let mut source = err.source();
+        let mut cause = None;
+
+        while let Some(err) = source.take() {
+            if let Some(err) = err.downcast_ref::<Self>() {
+                let mut err = err.clone_ref(py);
+                cause = err.cause.take();
+                stack.push(err);
+                break;
+            }
+
+            if let Some(err) = err.downcast_ref::<PyErr>() {
+                let mut err = Self::from(err.clone_ref(py));
+                cause = err.cause.take();
+                stack.push(err);
+                break;
+            }
+
+            source = err.source();
+            stack.push(Self {
+                #[allow(clippy::option_if_let_else)]
+                err: match T::try_from_err_ref::<M>(py, err) {
+                    Some(err) => err,
+                    None => PyException::new_err(format!("{err}")),
+                },
+                cause: None,
+            });
+        }
+
+        while let Some(mut err) = stack.pop() {
+            err.cause = cause.take();
+            err.err.set_cause(
+                py,
+                err.cause
+                    .as_deref()
+                    .map(|cause| cause.as_err().clone_ref(py)),
+            );
+            cause = Some(Box::new(err));
+        }
+
+        let err = match T::try_from_err::<M>(py, err) {
+            Ok(err) => err,
+            Err(err) => PyException::new_err(format!("{err}")),
+        };
+        err.set_cause(
+            py,
+            cause.as_deref().map(|cause| cause.as_err().clone_ref(py)),
+        );
+
+        Self { err, cause }
+    }
+
+    /// Clone the [`PyErrChain`].
+    ///
+    /// This requires the GIL, which is why [`PyErrChain`] does not implement
+    /// [`Clone`].
+    ///
+    /// Note that all elements of the cloned [`PyErrChain`] will be shared using
+    /// reference counting in Python with the existing [`PyErrChain`] `self`.
+    #[must_use]
+    pub fn clone_ref(&self, py: Python) -> Self {
+        Self {
+            err: self.err.clone_ref(py),
+            cause: self
+                .cause
+                .as_ref()
+                .map(|cause| Box::new(cause.clone_ref(py))),
+        }
+    }
+
+    /// Get a reference to the wrapped [`PyErr`].
+    ///
+    /// Note that while [`PyErr::set_cause`] can be called on the returned
+    /// [`PyErr`], the change in causality chain will not be reflected in
+    /// this [`PyErrChain`].
+    #[must_use]
+    pub const fn as_err(&self) -> &PyErr {
+        &self.err
+    }
+
+    /// Get a reference to the cause of the wrapped [`PyErr`].
+    ///
+    /// Note that while [`PyErr::set_cause`] can be called on the returned
+    /// [`PyErr`], the change in causality chain will not be reflected in
+    /// this [`PyErrChain`].
+    #[must_use]
+    pub fn cause(&self) -> Option<&PyErr> {
+        self.cause.as_deref().map(Self::as_err)
+    }
+}
+
+impl fmt::Debug for PyErrChain {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        Python::with_gil(|py| {
+            let traceback = self.err.traceback_bound(py).map(|tb| {
+                tb.format()
+                    .map_or(Cow::Borrowed("<traceback str() failed>"), |tb| {
+                        Cow::Owned(tb)
+                    })
+            });
+
+            fmt.debug_struct("PyErrChain")
+                .field("type", &self.err.get_type_bound(py))
+                .field("value", self.err.value_bound(py))
+                .field("traceback", &traceback)
+                .field("cause", &self.cause)
+                .finish()
+        })
+    }
+}
+
+impl fmt::Display for PyErrChain {
+    #[inline]
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Display::fmt(&self.err, fmt)
+    }
+}
+
+impl Error for PyErrChain {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        self.cause.as_deref().map(|cause| cause as &dyn Error)
+    }
+}
+
+impl From<PyErr> for PyErrChain {
+    fn from(err: PyErr) -> Self {
+        Python::with_gil(|py| {
+            let mut stack = Vec::new();
+
+            let mut cause = err.cause(py);
+
+            while let Some(err) = cause.take() {
+                cause = err.cause(py);
+                stack.push(Self { err, cause: None });
+            }
+
+            let mut cause = None;
+
+            while let Some(mut err) = stack.pop() {
+                err.cause = cause.take();
+                cause = Some(Box::new(err));
+            }
+
+            Self { err, cause }
+        })
+    }
+}
+
+impl From<PyErrChain> for PyErr {
+    fn from(err: PyErrChain) -> Self {
+        err.err
+    }
+}
+
+/// Utility trait to try to translate from [`std::error::Error`] to [`PyErr`].
+///
+/// [`ErrorNoPyErr`] may be used to always fail at translating.
+///
+/// [`IoErrorToPyErr`] may be used to translate [`std::io::Error`] to [`PyErr`].
+pub trait AnyErrorToPyErr {
+    /// Try to translate from a boxed `err` to a [`PyErr`], or return the `err`.
+    ///
+    /// When a strongly typed translation from some specific error type `E` to a
+    /// [`PyErr`] is attempted, [`MapErrorToPyErr::try_map`] should be used to allow
+    /// the mapper to test for `E` in addition to wrapped errors such as
+    /// `MyError<E>`.
+    ///
+    /// # Errors
+    ///
+    /// Returns the original `err` if translating to a [`PyErr`] failed.
+    fn try_from_err<T: MapErrorToPyErr>(
+        py: Python,
+        err: Box<dyn Error + 'static>,
+    ) -> Result<PyErr, Box<dyn Error + 'static>>;
+
+    /// Try to translate from an `err` reference to a [`PyErr`], or return
+    /// [`None`].
+    ///
+    /// When a strongly typed translation from some specific error type `E` to a
+    /// [`PyErr`] is attempted, [`MapErrorToPyErr::try_map_ref`] should be used to
+    /// allow the mapper to test for `E` in addition to wrapped errors such as
+    /// `MyError<E>`.
+    ///
+    fn try_from_err_ref<T: MapErrorToPyErr>(
+        py: Python,
+        err: &(dyn Error + 'static),
+    ) -> Option<PyErr>;
+}
 
-pub fn add(left: u64, right: u64) -> u64 {
-    left + right
+/// Utility trait to try to translate via specific error types `E` implementing
+/// [`std::error::Error`] and wrapped errors such as `MyError<E>` to [`PyErr`]s.
+///
+/// [`DowncastToPyErr`] may be used to only try to translate via `E` using
+/// downcasting.
+pub trait MapErrorToPyErr {
+    /// Try to map from a boxed `err` via the specific error type `T` or wrapped
+    /// errors such as `MyError<E>` to a [`PyErr`], or return the `err`.
+    ///
+    /// The `map` function should be used to access the provided mapping from
+    /// `T` to [`PyErr`].
+    ///
+    /// # Errors
+    ///
+    /// Returns the original `err` if mapping to a [`PyErr`] failed.
+    fn try_map<T: Error + 'static>(
+        py: Python,
+        err: Box<dyn Error + 'static>,
+        map: impl FnOnce(Box<T>) -> PyErr,
+    ) -> Result<PyErr, Box<dyn Error + 'static>>;
+
+    /// Try to map from an `err` reference via the specific error type `T` or
+    /// wrapped errors such as `MyError<E>` to a [`PyErr`], or return [`None`].
+    ///
+    /// The `map` function should be used to access the provided mapping from
+    /// `&T` to [`PyErr`].
+    fn try_map_ref<T: Error + 'static>(
+        py: Python,
+        err: &(dyn Error + 'static),
+        map: impl FnOnce(&T) -> PyErr,
+    ) -> Option<PyErr>;
+}
+
+/// Never attempt to translate any [`std::error::Error`] to [`PyErr`] when used
+/// as [`AnyErrorToPyErr`].
+pub struct ErrorNoPyErr;
+
+impl AnyErrorToPyErr for ErrorNoPyErr {
+    #[inline]
+    fn try_from_err<T: MapErrorToPyErr>(
+        _py: Python,
+        err: Box<dyn Error + 'static>,
+    ) -> Result<PyErr, Box<dyn Error + 'static>> {
+        Err(err)
+    }
+
+    #[inline]
+    fn try_from_err_ref<T: MapErrorToPyErr>(
+        _py: Python,
+        _err: &(dyn Error + 'static),
+    ) -> Option<PyErr> {
+        None
+    }
+}
+
+/// Translate [`std::io::Error`] to [`PyErr`] when used as [`AnyErrorToPyErr`].
+pub struct IoErrorToPyErr;
+
+impl AnyErrorToPyErr for IoErrorToPyErr {
+    fn try_from_err<T: MapErrorToPyErr>(
+        py: Python,
+        err: Box<dyn Error + 'static>,
+    ) -> Result<PyErr, Box<dyn Error + 'static>> {
+        T::try_map(py, err, |err: Box<io::Error>| {
+            // TODO: replace with io::Error::downcast once MSRV >= 1.79
+            #[allow(clippy::redundant_closure_for_method_calls)]
+            if err.get_ref().map_or(false, |err| err.is::<PyErrChain>()) {
+                #[allow(clippy::unwrap_used)] // we have just checked that all unwraps succeed
+                let err: Box<PyErrChain> = err.into_inner().unwrap().downcast().unwrap();
+                return PyErr::from(*err);
+            }
+
+            PyErr::from(*err)
+        })
+    }
+
+    fn try_from_err_ref<T: MapErrorToPyErr>(
+        py: Python,
+        err: &(dyn Error + 'static),
+    ) -> Option<PyErr> {
+        T::try_map_ref(py, err, |err: &io::Error| {
+            if let Some(err) = err.get_ref() {
+                if let Some(err) = err.downcast_ref::<PyErr>() {
+                    return err.clone_ref(py);
+                }
+
+                if let Some(err) = err.downcast_ref::<PyErrChain>() {
+                    return err.as_err().clone_ref(py);
+                }
+            }
+
+            PyErr::from(io::Error::new(err.kind(), format!("{err}")))
+        })
+    }
+}
+
+/// Try to map a [`std::error::Error`] via a specific error type `T` to a
+/// [`PyErr`] by downcasting when used as [`MapErrorToPyErr`];
+pub struct DowncastToPyErr;
+
+impl MapErrorToPyErr for DowncastToPyErr {
+    fn try_map<T: Error + 'static>(
+        _py: Python,
+        err: Box<dyn Error + 'static>,
+        map: impl FnOnce(Box<T>) -> PyErr,
+    ) -> Result<PyErr, Box<dyn Error + 'static>> {
+        err.downcast().map(map)
+    }
+
+    fn try_map_ref<T: Error + 'static>(
+        _py: Python,
+        err: &(dyn Error + 'static),
+        map: impl FnOnce(&T) -> PyErr,
+    ) -> Option<PyErr> {
+        err.downcast_ref().map(map)
+    }
+}
+
+#[allow(clippy::missing_panics_doc)]
+/// Utility function to add a traceback with the error's `file`, `line`, and
+/// `column` location information to the `err`.
+///
+/// This function may be used when implementing [`AnyErrorToPyErr`] or
+/// [`MapErrorToPyErr`] to pythonize any available error location information.
+#[must_use]
+pub fn err_with_location(py: Python, err: PyErr, file: &str, line: u32, column: u32) -> PyErr {
+    const RAISE: &str = "raise err";
+
+    static COMPILE: GILOnceCell<Py<PyAny>> = GILOnceCell::new();
+    static EXEC: GILOnceCell<Py<PyAny>> = GILOnceCell::new();
+
+    let _ = column;
+
+    #[allow(clippy::expect_used)] // failure is a Python bug
+    let compile = COMPILE
+        .get_or_try_init(py, || -> Result<Py<PyAny>, PyErr> {
+            Ok(py.import_bound("builtins")?.getattr("compile")?.unbind())
+        })
+        .expect("Python does not provide a compile() function")
+        .bind(py);
+
+    #[allow(clippy::expect_used)] // failure is a Python bug
+    let exec = EXEC
+        .get_or_try_init(py, || -> Result<Py<PyAny>, PyErr> {
+            Ok(py.import_bound("builtins")?.getattr("exec")?.unbind())
+        })
+        .expect("Python does not provide an exec() function")
+        .bind(py);
+
+    let mut code = String::with_capacity((line as usize) + RAISE.len());
+    for _ in 1..line {
+        code.push('\n');
+    }
+    code.push_str(RAISE);
+
+    #[allow(clippy::expect_used)] // failure is a Python bug
+    let code = compile
+        .call1((code, file, intern!(py, "exec")))
+        .expect("failed to compile PyErr location helper");
+    let globals = [(intern!(py, "err"), err)].into_py_dict_bound(py);
+
+    #[allow(clippy::expect_used)] // failure is a Python bug
+    let err = exec.call1((code, globals)).expect_err("raise must raise");
+    err
 }
 
 #[cfg(test)]
@@ -55,9 +995,107 @@
     use super::*;
 
     #[test]
-    fn it_works() {
-        let result = add(2, 2);
-        assert_eq!(result, 4);
+    fn python_cause() {
+        Python::with_gil(|py| {
+            let err = py
+                .run_bound(
+                    r#"
+try:
+    try:
+        raise Exception("source")
+    except Exception as err:
+        raise IndexError("middle") from err
+except Exception as err:
+    raise LookupError("top") from err
+"#,
+                    None,
+                    None,
+                )
+                .expect_err("raise must raise");
+
+            let err = PyErrChain::new(py, err);
+            assert_eq!(format!("{err}"), "LookupError: top");
+
+            let err = err.source().expect("must have source");
+            assert_eq!(format!("{err}"), "IndexError: middle");
+
+            let err = err.source().expect("must have source");
+            assert_eq!(format!("{err}"), "Exception: source");
+
+            assert!(err.source().is_none());
+        })
+    }
+
+    #[test]
+    fn rust_source() {
+        #[derive(Debug)]
+        struct MyErr {
+            msg: &'static str,
+            source: Option<Box<Self>>,
+        }
+
+        impl fmt::Display for MyErr {
+            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+                fmt.write_str(self.msg)
+            }
+        }
+
+        impl Error for MyErr {
+            fn source(&self) -> Option<&(dyn Error + 'static)> {
+                match &self.source {
+                    None => None,
+                    Some(source) => Some(&**source as &dyn Error),
+                }
+            }
+        }
+
+        Python::with_gil(|py| {
+            let err = PyErrChain::new(
+                py,
+                MyErr {
+                    msg: "top",
+                    source: Some(Box::new(MyErr {
+                        msg: "middle",
+                        source: Some(Box::new(MyErr {
+                            msg: "source",
+                            source: None,
+                        })),
+                    })),
+                },
+            );
+
+            let source = err.source().expect("must have source");
+            let source = source.source().expect("must have source");
+            assert!(source.source().is_none());
+
+            let err = PyErr::from(err);
+            assert_eq!(format!("{err}"), "Exception: top");
+
+            let err = err.cause(py).expect("must have cause");
+            assert_eq!(format!("{err}"), "Exception: middle");
+
+            let err = err.cause(py).expect("must have cause");
+            assert_eq!(format!("{err}"), "Exception: source");
+
+            assert!(err.cause(py).is_none());
+        })
+    }
+
+    #[test]
+    fn err_location() {
+        Python::with_gil(|py| {
+            let err = err_with_location(py, PyException::new_err("oh no"), "foo.rs", 27, 15);
+
+            assert_eq!(format!("{err}"), "Exception: oh no");
+            assert_eq!(
+                err.traceback_bound(py)
+                    .expect("must have traceback")
+                    .format()
+                    .expect("traceback must be formattable"),
+                "Traceback (most recent call last):\n  File \"foo.rs\", line 27, in <module>\n",
+            );
+            assert!(err.cause(py).is_none());
+        })
     }
 }
 
\ No newline at end of file diff --git a/trait.impl/core/convert/trait.From.js b/trait.impl/core/convert/trait.From.js new file mode 100644 index 0000000..a98680a --- /dev/null +++ b/trait.impl/core/convert/trait.From.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl From<PyErr> for PyErrChain"],["impl From<PyErrChain> for PyErr"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[816]} \ No newline at end of file diff --git a/trait.impl/core/error/trait.Error.js b/trait.impl/core/error/trait.Error.js new file mode 100644 index 0000000..b35d6e5 --- /dev/null +++ b/trait.impl/core/error/trait.Error.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Error for PyErrChain"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[282]} \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Debug.js b/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 0000000..298b291 --- /dev/null +++ b/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Debug for PyErrChain"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[278]} \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Display.js b/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 0000000..f89e921 --- /dev/null +++ b/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Display for PyErrChain"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[284]} \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Freeze.js b/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 0000000..baf550b --- /dev/null +++ b/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl !Freeze for PyErrChain",1,["pyo3_error::PyErrChain"]],["impl Freeze for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl Freeze for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl Freeze for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1261]} \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Send.js b/trait.impl/core/marker/trait.Send.js new file mode 100644 index 0000000..30da0c7 --- /dev/null +++ b/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Send for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl Send for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl Send for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]],["impl Send for PyErrChain",1,["pyo3_error::PyErrChain"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1236]} \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Sync.js b/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 0000000..45abcba --- /dev/null +++ b/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Sync for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl Sync for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl Sync for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]],["impl Sync for PyErrChain",1,["pyo3_error::PyErrChain"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1236]} \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Unpin.js b/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 0000000..1562b95 --- /dev/null +++ b/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl Unpin for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl Unpin for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl Unpin for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]],["impl Unpin for PyErrChain",1,["pyo3_error::PyErrChain"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1248]} \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 0000000..a1a929d --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl !RefUnwindSafe for PyErrChain",1,["pyo3_error::PyErrChain"]],["impl RefUnwindSafe for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl RefUnwindSafe for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl RefUnwindSafe for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1437]} \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 0000000..b51ef77 --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[["impl !UnwindSafe for PyErrChain",1,["pyo3_error::PyErrChain"]],["impl UnwindSafe for DowncastToPyErr",1,["pyo3_error::DowncastToPyErr"]],["impl UnwindSafe for ErrorNoPyErr",1,["pyo3_error::ErrorNoPyErr"]],["impl UnwindSafe for IoErrorToPyErr",1,["pyo3_error::IoErrorToPyErr"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1401]} \ No newline at end of file diff --git a/trait.impl/pyo3_error/trait.AnyErrorToPyErr.js b/trait.impl/pyo3_error/trait.AnyErrorToPyErr.js new file mode 100644 index 0000000..aa064c2 --- /dev/null +++ b/trait.impl/pyo3_error/trait.AnyErrorToPyErr.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file diff --git a/trait.impl/pyo3_error/trait.MapErrorToPyErr.js b/trait.impl/pyo3_error/trait.MapErrorToPyErr.js new file mode 100644 index 0000000..aa064c2 --- /dev/null +++ b/trait.impl/pyo3_error/trait.MapErrorToPyErr.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["pyo3_error",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file