Skip to content

Commit c366603

Browse files
committed
add FromPyObject::Error
1 parent cb65346 commit c366603

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+361
-141
lines changed

guide/src/class.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,7 @@ impl pyo3::PyClass for MyClass {
13971397
impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder MyClass
13981398
{
13991399
type Holder = ::std::option::Option<pyo3::PyClassGuard<'a, MyClass>>;
1400+
type Error = pyo3::PyErr;
14001401
#[cfg(feature = "experimental-inspect")]
14011402
const INPUT_TYPE: &'static str = "MyClass";
14021403

@@ -1409,6 +1410,7 @@ impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'ho
14091410
impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut MyClass
14101411
{
14111412
type Holder = ::std::option::Option<pyo3::PyClassGuardMut<'a, MyClass>>;
1413+
type Error = pyo3::PyErr;
14121414
#[cfg(feature = "experimental-inspect")]
14131415
const INPUT_TYPE: &'static str = "MyClass";
14141416

guide/src/migration.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ impl<'a, 'py, T> FromPyObject<'a, 'py> for MyWrapper<T>
134134
where
135135
T: FromPyObject<'a, 'py>
136136
{
137-
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
137+
type Error = T::Error;
138+
139+
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
138140
obj.extract().map(MyWrapper)
139141
}
140142
}
@@ -172,10 +174,12 @@ where
172174
T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below
173175
// is a temporary short lived owned reference
174176
{
175-
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
177+
type Error = PyErr;
178+
179+
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
176180
let mut v = MyVec(Vec::new());
177181
for item in obj.try_iter()? {
178-
v.0.push(item?.extract::<T>()?);
182+
v.0.push(item?.extract::<T>().map_err(Into::into)?);
179183
}
180184
Ok(v)
181185
}

pyo3-macros-backend/src/frompyobject.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,8 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
507507
Ok(quote!(
508508
#[automatically_derived]
509509
impl #impl_generics #pyo3_path::FromPyObject<'_, #lt_param> for #ident #ty_generics #where_clause {
510-
fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult<Self> {
510+
type Error = #pyo3_path::PyErr;
511+
fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> ::std::result::Result<Self, Self::Error> {
511512
let obj: &#pyo3_path::Bound<'_, _> = &*obj;
512513
#derives
513514
}

pyo3-macros-backend/src/pyclass.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,7 @@ impl<'a> PyClassImplsBuilder<'a> {
21562156
impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls
21572157
{
21582158
type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>;
2159+
type Error = #pyo3_path::PyErr;
21592160

21602161
#input_type
21612162

@@ -2170,6 +2171,7 @@ impl<'a> PyClassImplsBuilder<'a> {
21702171
impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls
21712172
{
21722173
type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>;
2174+
type Error = #pyo3_path::PyErr;
21732175

21742176
#input_type
21752177

@@ -2182,6 +2184,7 @@ impl<'a> PyClassImplsBuilder<'a> {
21822184
impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut #cls
21832185
{
21842186
type Holder = ::std::option::Option<#pyo3_path::PyClassGuardMut<'a, #cls>>;
2187+
type Error =#pyo3_path::PyErr;
21852188

21862189
#input_type
21872190

src/buffer.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
//! `PyBuffer` implementation
2121
use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
22-
use crate::{Borrowed, Bound};
22+
use crate::{Borrowed, Bound, PyErr};
2323
use std::marker::PhantomData;
2424
use std::os::raw;
2525
use std::pin::Pin;
@@ -183,7 +183,9 @@ pub unsafe trait Element: Copy {
183183
}
184184

185185
impl<T: Element> FromPyObject<'_, '_> for PyBuffer<T> {
186-
fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<PyBuffer<T>> {
186+
type Error = PyErr;
187+
188+
fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<PyBuffer<T>, Self::Error> {
187189
Self::get(&obj)
188190
}
189191
}

src/conversion.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {}
290290
/// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed
291291
/// [`Cow::Owned`]: std::borrow::Cow::Owned
292292
pub trait FromPyObject<'a, 'py>: Sized {
293+
/// The type returned in the event of a conversion error.
294+
type Error: Into<PyErr>;
295+
293296
/// Provides the type hint information for this type when it appears as an argument.
294297
///
295298
/// For example, `Vec<u32>` would be `collections.abc.Sequence[int]`.
@@ -301,7 +304,7 @@ pub trait FromPyObject<'a, 'py>: Sized {
301304
///
302305
/// Users are advised against calling this method directly: instead, use this via
303306
/// [`Bound<'_, PyAny>::extract`](crate::types::any::PyAnyMethods::extract) or [`Py::extract`].
304-
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self>;
307+
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error>;
305308

306309
/// Extracts the type hint information for this type when it appears as an argument.
307310
///
@@ -338,7 +341,9 @@ pub trait FromPyObject<'a, 'py>: Sized {
338341
/// where
339342
/// T: FromPyObject<'a, 'py>
340343
/// {
341-
/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
344+
/// type Error = T::Error;
345+
///
346+
/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
342347
/// obj.extract().map(MyWrapper)
343348
/// }
344349
/// }
@@ -351,10 +356,12 @@ pub trait FromPyObject<'a, 'py>: Sized {
351356
/// T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below
352357
/// // is a temporary short lived owned reference
353358
/// {
354-
/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
359+
/// type Error = PyErr;
360+
///
361+
/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
355362
/// let mut v = MyVec(Vec::new());
356363
/// for item in obj.try_iter()? {
357-
/// v.0.push(item?.extract::<T>()?);
364+
/// v.0.push(item?.extract::<T>().map_err(Into::into)?);
358365
/// }
359366
/// Ok(v)
360367
/// }
@@ -370,7 +377,9 @@ impl<T> FromPyObject<'_, '_> for T
370377
where
371378
T: PyClass + Clone,
372379
{
373-
fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
380+
type Error = PyErr;
381+
382+
fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
374383
let bound = obj.cast::<Self>()?;
375384
Ok(bound.try_borrow()?.clone())
376385
}
@@ -380,7 +389,9 @@ impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T>
380389
where
381390
T: PyClass,
382391
{
383-
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
392+
type Error = PyErr;
393+
394+
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
384395
obj.cast::<T>()?.try_borrow().map_err(Into::into)
385396
}
386397
}
@@ -389,7 +400,9 @@ impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T>
389400
where
390401
T: PyClass<Frozen = False>,
391402
{
392-
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
403+
type Error = PyErr;
404+
405+
fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
393406
obj.cast::<T>()?.try_borrow_mut().map_err(Into::into)
394407
}
395408
}

src/conversions/bigdecimal.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ fn get_invalid_operation_error_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType
7272
}
7373

7474
impl FromPyObject<'_, '_> for BigDecimal {
75+
type Error = PyErr;
76+
7577
fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
7678
let py_str = &obj.str()?;
7779
let rs_str = &py_str.to_cow()?;

src/conversions/bytes.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ use crate::conversion::IntoPyObject;
6666
use crate::instance::Bound;
6767
use crate::pybacked::PyBackedBytes;
6868
use crate::types::PyBytes;
69-
use crate::{Borrowed, FromPyObject, PyAny, PyErr, PyResult, Python};
69+
use crate::{Borrowed, DowncastError, FromPyObject, PyAny, PyErr, Python};
7070

71-
impl FromPyObject<'_, '_> for Bytes {
72-
fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
71+
impl<'a, 'py> FromPyObject<'a, 'py> for Bytes {
72+
type Error = DowncastError<'a, 'py>;
73+
74+
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
7375
Ok(Bytes::from_owner(obj.extract::<PyBackedBytes>()?))
7476
}
7577
}

src/conversions/chrono.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ impl<'py> IntoPyObject<'py> for &Duration {
109109
}
110110

111111
impl FromPyObject<'_, '_> for Duration {
112-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<Duration> {
112+
type Error = PyErr;
113+
114+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
113115
let delta = ob.cast::<PyDelta>()?;
114116
// Python size are much lower than rust size so we do not need bound checks.
115117
// 0 <= microseconds < 1000000
@@ -163,7 +165,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDate {
163165
}
164166

165167
impl FromPyObject<'_, '_> for NaiveDate {
166-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<NaiveDate> {
168+
type Error = PyErr;
169+
170+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
167171
let date = &*ob.cast::<PyDate>()?;
168172
py_date_to_naive_date(date)
169173
}
@@ -205,7 +209,9 @@ impl<'py> IntoPyObject<'py> for &NaiveTime {
205209
}
206210

207211
impl FromPyObject<'_, '_> for NaiveTime {
208-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<NaiveTime> {
212+
type Error = PyErr;
213+
214+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
209215
let time = &*ob.cast::<PyTime>()?;
210216
py_time_to_naive_time(time)
211217
}
@@ -248,7 +254,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDateTime {
248254
}
249255

250256
impl FromPyObject<'_, '_> for NaiveDateTime {
251-
fn extract(dt: Borrowed<'_, '_, PyAny>) -> PyResult<NaiveDateTime> {
257+
type Error = PyErr;
258+
259+
fn extract(dt: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
252260
let dt = &*dt.cast::<PyDateTime>()?;
253261

254262
// If the user tries to convert a timezone aware datetime into a naive one,
@@ -328,12 +336,14 @@ impl<'py, Tz> FromPyObject<'_, 'py> for DateTime<Tz>
328336
where
329337
Tz: TimeZone + FromPyObjectOwned<'py>,
330338
{
331-
fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult<DateTime<Tz>> {
339+
type Error = PyErr;
340+
341+
fn extract(dt: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
332342
let dt = &*dt.cast::<PyDateTime>()?;
333343
let tzinfo = dt.get_tzinfo();
334344

335345
let tz = if let Some(tzinfo) = tzinfo {
336-
tzinfo.extract()?
346+
tzinfo.extract().map_err(Into::into)?
337347
} else {
338348
return Err(PyTypeError::new_err(
339349
"expected a datetime with non-None tzinfo",
@@ -386,11 +396,13 @@ impl<'py> IntoPyObject<'py> for &FixedOffset {
386396
}
387397

388398
impl FromPyObject<'_, '_> for FixedOffset {
399+
type Error = PyErr;
400+
389401
/// Convert python tzinfo to rust [`FixedOffset`].
390402
///
391403
/// Note that the conversion will result in precision lost in microseconds as chrono offset
392404
/// does not supports microseconds.
393-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<FixedOffset> {
405+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
394406
let ob = ob.cast::<PyTzInfo>()?;
395407

396408
// Passing Python's None to the `utcoffset` function will only
@@ -435,7 +447,9 @@ impl<'py> IntoPyObject<'py> for &Utc {
435447
}
436448

437449
impl FromPyObject<'_, '_> for Utc {
438-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<Utc> {
450+
type Error = PyErr;
451+
452+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
439453
let py_utc = PyTzInfo::utc(ob.py())?;
440454
if ob.eq(py_utc)? {
441455
Ok(Utc)
@@ -479,6 +493,8 @@ impl<'py> IntoPyObject<'py> for &Local {
479493

480494
#[cfg(feature = "chrono-local")]
481495
impl FromPyObject<'_, '_> for Local {
496+
type Error = PyErr;
497+
482498
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<Local> {
483499
let local_tz = Local.into_pyobject(ob.py())?;
484500
if ob.eq(local_tz)? {

src/conversions/chrono_tz.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::conversion::IntoPyObject;
3838
use crate::exceptions::PyValueError;
3939
use crate::pybacked::PyBackedStr;
4040
use crate::types::{any::PyAnyMethods, PyTzInfo};
41-
use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python};
41+
use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, Python};
4242
use chrono_tz::Tz;
4343
use std::str::FromStr;
4444

@@ -64,7 +64,9 @@ impl<'py> IntoPyObject<'py> for &Tz {
6464
}
6565

6666
impl FromPyObject<'_, '_> for Tz {
67-
fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<Tz> {
67+
type Error = PyErr;
68+
69+
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
6870
Tz::from_str(
6971
&ob.getattr(intern!(ob.py(), "key"))?
7072
.extract::<PyBackedStr>()?,

0 commit comments

Comments
 (0)