Merge pull request #3784 from davidhewitt/more-extract-bound
migrate many `FromPyObject` implementations to `Bound` API
This commit is contained in:
commit
5dbb51b9ce
|
@ -3,6 +3,7 @@ use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criter
|
|||
use pyo3::types::IntoPyDict;
|
||||
use pyo3::{prelude::*, types::PyMapping};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::hint::black_box;
|
||||
|
||||
fn iter_dict(b: &mut Bencher<'_>) {
|
||||
Python::with_gil(|py| {
|
||||
|
@ -71,13 +72,12 @@ fn extract_hashbrown_map(b: &mut Bencher<'_>) {
|
|||
fn mapping_from_dict(b: &mut Bencher<'_>) {
|
||||
Python::with_gil(|py| {
|
||||
const LEN: usize = 100_000;
|
||||
let dict = (0..LEN as u64)
|
||||
let dict = &(0..LEN as u64)
|
||||
.map(|i| (i, i * 2))
|
||||
.into_py_dict(py)
|
||||
.to_object(py);
|
||||
b.iter(|| {
|
||||
let _: &PyMapping = dict.extract(py).unwrap();
|
||||
});
|
||||
.to_object(py)
|
||||
.into_bound(py);
|
||||
b.iter(|| black_box(dict).downcast::<PyMapping>().unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::hint::black_box;
|
||||
|
||||
use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion};
|
||||
|
||||
use pyo3::prelude::*;
|
||||
|
@ -56,11 +58,8 @@ fn list_get_item_unchecked(b: &mut Bencher<'_>) {
|
|||
fn sequence_from_list(b: &mut Bencher<'_>) {
|
||||
Python::with_gil(|py| {
|
||||
const LEN: usize = 50_000;
|
||||
let list = PyList::new_bound(py, 0..LEN).to_object(py);
|
||||
b.iter(|| {
|
||||
let seq: &PySequence = list.extract(py).unwrap();
|
||||
seq
|
||||
});
|
||||
let list = &PyList::new_bound(py, 0..LEN);
|
||||
b.iter(|| black_box(list).downcast::<PySequence>().unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ fn sequence_from_tuple(b: &mut Bencher<'_>) {
|
|||
Python::with_gil(|py| {
|
||||
const LEN: usize = 50_000;
|
||||
let tuple = PyTuple::new_bound(py, 0..LEN).to_object(py);
|
||||
b.iter(|| tuple.extract::<&PySequence>(py).unwrap());
|
||||
b.iter(|| tuple.downcast::<PySequence>(py).unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -568,8 +568,8 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
|
|||
let lt_param = if let Some(lt) = verify_and_get_lifetime(generics)? {
|
||||
lt.clone()
|
||||
} else {
|
||||
trait_generics.params.push(parse_quote!('source));
|
||||
parse_quote!('source)
|
||||
trait_generics.params.push(parse_quote!('py));
|
||||
parse_quote!('py)
|
||||
};
|
||||
let mut where_clause: syn::WhereClause = parse_quote!(where);
|
||||
for param in generics.type_params() {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! `PyBuffer` implementation
|
||||
use crate::instance::Bound;
|
||||
use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw;
|
||||
|
@ -181,9 +182,9 @@ pub unsafe trait Element: Copy {
|
|||
fn is_compatible_format(format: &CStr) -> bool;
|
||||
}
|
||||
|
||||
impl<'source, T: Element> FromPyObject<'source> for PyBuffer<T> {
|
||||
fn extract(obj: &PyAny) -> PyResult<PyBuffer<T>> {
|
||||
Self::get(obj)
|
||||
impl<'py, T: Element> FromPyObject<'py> for PyBuffer<T> {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
|
||||
Self::get(obj.as_gil_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ pub trait IntoPy<T>: Sized {
|
|||
/// depend on the lifetime of the `obj` or the `prepared` variable.
|
||||
///
|
||||
/// For example, when extracting `&str` from a Python byte string, the resulting string slice will
|
||||
/// point to the existing string data (lifetime: `'source`).
|
||||
/// point to the existing string data (lifetime: `'py`).
|
||||
/// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step
|
||||
/// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`.
|
||||
/// Since which case applies depends on the runtime type of the Python object,
|
||||
|
@ -219,12 +219,12 @@ pub trait IntoPy<T>: Sized {
|
|||
/// has two methods `extract` and `extract_bound` which are defaulted to call each other. To avoid
|
||||
/// infinite recursion, implementors must implement at least one of these methods. The recommendation
|
||||
/// is to implement `extract_bound` and leave `extract` as the default implementation.
|
||||
pub trait FromPyObject<'source>: Sized {
|
||||
pub trait FromPyObject<'py>: Sized {
|
||||
/// Extracts `Self` from the source GIL Ref `obj`.
|
||||
///
|
||||
/// Implementors are encouraged to implement `extract_bound` and leave this method as the
|
||||
/// default implementation, which will forward calls to `extract_bound`.
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract(ob: &'py PyAny) -> PyResult<Self> {
|
||||
Self::extract_bound(&ob.as_borrowed())
|
||||
}
|
||||
|
||||
|
@ -232,7 +232,7 @@ pub trait FromPyObject<'source>: Sized {
|
|||
///
|
||||
/// Implementors are encouraged to implement this method and leave `extract` defaulted, as
|
||||
/// this will be most compatible with PyO3's future API.
|
||||
fn extract_bound(ob: &Bound<'source, PyAny>) -> PyResult<Self> {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
Self::extract(ob.clone().into_gil_ref())
|
||||
}
|
||||
|
||||
|
@ -308,56 +308,56 @@ impl<T: Copy + IntoPy<PyObject>> IntoPy<PyObject> for Cell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: FromPyObject<'a>> FromPyObject<'a> for Cell<T> {
|
||||
fn extract(ob: &'a PyAny) -> PyResult<Self> {
|
||||
impl<'py, T: FromPyObject<'py>> FromPyObject<'py> for Cell<T> {
|
||||
fn extract(ob: &'py PyAny) -> PyResult<Self> {
|
||||
T::extract(ob).map(Cell::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for &'a PyCell<T>
|
||||
impl<'py, T> FromPyObject<'py> for &'py PyCell<T>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract(obj: &'py PyAny) -> PyResult<Self> {
|
||||
obj.downcast().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for T
|
||||
impl<T> FromPyObject<'_> for T
|
||||
where
|
||||
T: PyClass + Clone,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
let cell: &PyCell<Self> = obj.downcast()?;
|
||||
Ok(unsafe { cell.try_borrow_unguarded()?.clone() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for PyRef<'a, T>
|
||||
impl<'py, T> FromPyObject<'py> for PyRef<'py, T>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract(obj: &'py PyAny) -> PyResult<Self> {
|
||||
let cell: &PyCell<T> = obj.downcast()?;
|
||||
cell.try_borrow().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
|
||||
impl<'py, T> FromPyObject<'py> for PyRefMut<'py, T>
|
||||
where
|
||||
T: PyClass<Frozen = False>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract(obj: &'py PyAny) -> PyResult<Self> {
|
||||
let cell: &PyCell<T> = obj.downcast()?;
|
||||
cell.try_borrow_mut().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for Option<T>
|
||||
impl<'py, T> FromPyObject<'py> for Option<T>
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
T: FromPyObject<'py>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract(obj: &'py PyAny) -> PyResult<Self> {
|
||||
if obj.as_ptr() == unsafe { ffi::Py_None() } {
|
||||
Ok(None)
|
||||
} else {
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError};
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::sync::GILOnceCell;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::types::any::PyAnyMethods;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::types::datetime::timezone_from_offset;
|
||||
|
@ -52,9 +51,9 @@ use crate::types::{
|
|||
PyTzInfo, PyTzInfoAccess,
|
||||
};
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::{intern, PyDowncastError};
|
||||
use crate::{intern, DowncastError};
|
||||
use crate::{
|
||||
FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
|
||||
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use chrono::offset::{FixedOffset, Utc};
|
||||
use chrono::{
|
||||
|
@ -109,14 +108,14 @@ impl IntoPy<PyObject> for Duration {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for Duration {
|
||||
fn extract(ob: &PyAny) -> PyResult<Duration> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Duration> {
|
||||
// Python size are much lower than rust size so we do not need bound checks.
|
||||
// 0 <= microseconds < 1000000
|
||||
// 0 <= seconds < 3600*24
|
||||
// -999999999 <= days <= 999999999
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
let (days, seconds, microseconds) = {
|
||||
let delta: &PyDelta = ob.downcast()?;
|
||||
let delta = ob.downcast::<PyDelta>()?;
|
||||
(
|
||||
delta.get_days().into(),
|
||||
delta.get_seconds().into(),
|
||||
|
@ -166,10 +165,10 @@ impl IntoPy<PyObject> for NaiveDate {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for NaiveDate {
|
||||
fn extract(ob: &PyAny) -> PyResult<NaiveDate> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<NaiveDate> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
{
|
||||
let date: &PyDate = ob.downcast()?;
|
||||
let date = ob.downcast::<PyDate>()?;
|
||||
py_date_to_naive_date(date)
|
||||
}
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
|
@ -211,10 +210,10 @@ impl IntoPy<PyObject> for NaiveTime {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for NaiveTime {
|
||||
fn extract(ob: &PyAny) -> PyResult<NaiveTime> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<NaiveTime> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
{
|
||||
let time: &PyTime = ob.downcast()?;
|
||||
let time = ob.downcast::<PyTime>()?;
|
||||
py_time_to_naive_time(time)
|
||||
}
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
|
@ -238,9 +237,9 @@ impl IntoPy<PyObject> for NaiveDateTime {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for NaiveDateTime {
|
||||
fn extract(dt: &PyAny) -> PyResult<NaiveDateTime> {
|
||||
fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult<NaiveDateTime> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
let dt: &PyDateTime = dt.downcast()?;
|
||||
let dt = dt.downcast::<PyDateTime>()?;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
check_type(dt, &DatetimeTypes::get(dt.py()).datetime, "PyDateTime")?;
|
||||
|
||||
|
@ -276,10 +275,10 @@ impl<Tz: TimeZone> IntoPy<PyObject> for DateTime<Tz> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone + for<'a> FromPyObject<'a>> FromPyObject<'_> for DateTime<Tz> {
|
||||
fn extract(dt: &PyAny) -> PyResult<DateTime<Tz>> {
|
||||
impl<Tz: TimeZone + for<'py> FromPyObject<'py>> FromPyObject<'_> for DateTime<Tz> {
|
||||
fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult<DateTime<Tz>> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
let dt: &PyDateTime = dt.downcast()?;
|
||||
let dt = dt.downcast::<PyDateTime>()?;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
check_type(dt, &DatetimeTypes::get(dt.py()).datetime, "PyDateTime")?;
|
||||
|
||||
|
@ -339,7 +338,7 @@ impl FromPyObject<'_> for FixedOffset {
|
|||
///
|
||||
/// Note that the conversion will result in precision lost in microseconds as chrono offset
|
||||
/// does not supports microseconds.
|
||||
fn extract(ob: &PyAny) -> PyResult<FixedOffset> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<FixedOffset> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
let ob: &PyTzInfo = ob.extract()?;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
|
@ -378,7 +377,7 @@ impl IntoPy<PyObject> for Utc {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for Utc {
|
||||
fn extract(ob: &PyAny) -> PyResult<Utc> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Utc> {
|
||||
let py_utc = timezone_utc(ob.py());
|
||||
if ob.eq(py_utc)? {
|
||||
Ok(Utc)
|
||||
|
@ -480,7 +479,7 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult<NaiveDate> {
|
|||
}
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn py_date_to_naive_date(py_date: &PyAny) -> PyResult<NaiveDate> {
|
||||
fn py_date_to_naive_date(py_date: &Bound<'_, PyAny>) -> PyResult<NaiveDate> {
|
||||
NaiveDate::from_ymd_opt(
|
||||
py_date.getattr(intern!(py_date.py(), "year"))?.extract()?,
|
||||
py_date.getattr(intern!(py_date.py(), "month"))?.extract()?,
|
||||
|
@ -501,7 +500,7 @@ fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult<NaiveTime> {
|
|||
}
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
|
||||
fn py_time_to_naive_time(py_time: &Bound<'_, PyAny>) -> PyResult<NaiveTime> {
|
||||
NaiveTime::from_hms_micro_opt(
|
||||
py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?,
|
||||
py_time
|
||||
|
@ -518,9 +517,9 @@ fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
|
|||
}
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn check_type(value: &PyAny, t: &PyObject, type_name: &'static str) -> PyResult<()> {
|
||||
if !value.is_instance(t.as_ref(value.py()))? {
|
||||
return Err(PyDowncastError::new(value, type_name).into());
|
||||
fn check_type(value: &Bound<'_, PyAny>, t: &PyObject, type_name: &'static str) -> PyResult<()> {
|
||||
if !value.is_instance(t.bind(value.py()))? {
|
||||
return Err(DowncastError::new(value, type_name).into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -35,8 +35,11 @@
|
|||
//! ```
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::sync::GILOnceCell;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::PyType;
|
||||
use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use crate::{
|
||||
intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use chrono_tz::Tz;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -59,7 +62,7 @@ impl IntoPy<PyObject> for Tz {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for Tz {
|
||||
fn extract(ob: &PyAny) -> PyResult<Tz> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Tz> {
|
||||
Tz::from_str(ob.getattr(intern!(ob.py(), "key"))?.extract()?)
|
||||
.map_err(|e| PyValueError::new_err(e.to_string()))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
exceptions::PyTypeError, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPy, PyAny,
|
||||
PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use either::Either;
|
||||
|
||||
|
@ -81,13 +82,13 @@ where
|
|||
}
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
|
||||
impl<'source, L, R> FromPyObject<'source> for Either<L, R>
|
||||
impl<'py, L, R> FromPyObject<'py> for Either<L, R>
|
||||
where
|
||||
L: FromPyObject<'source>,
|
||||
R: FromPyObject<'source>,
|
||||
L: FromPyObject<'py>,
|
||||
R: FromPyObject<'py>,
|
||||
{
|
||||
#[inline]
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
if let Ok(l) = obj.extract::<L>() {
|
||||
Ok(Either::Left(l))
|
||||
} else if let Ok(r) = obj.extract::<R>() {
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
//! Note that you must use compatible versions of hashbrown and PyO3.
|
||||
//! The required hashbrown version may vary based on the version of PyO3.
|
||||
use crate::{
|
||||
types::set::new_from_iter,
|
||||
types::any::PyAnyMethods,
|
||||
types::dict::PyDictMethods,
|
||||
types::frozenset::PyFrozenSetMethods,
|
||||
types::set::{new_from_iter, PySetMethods},
|
||||
types::{IntoPyDict, PyDict, PyFrozenSet, PySet},
|
||||
FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use std::{cmp, hash};
|
||||
|
||||
|
@ -48,17 +51,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V, S> FromPyObject<'source> for hashbrown::HashMap<K, V, S>
|
||||
impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap<K, V, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'source>,
|
||||
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'py>,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict: &PyDict = ob.downcast()?;
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
|
||||
let dict = ob.downcast::<PyDict>()?;
|
||||
let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
|
||||
for (k, v) in dict {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(k.extract()?, v.extract()?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
@ -87,17 +90,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, S> FromPyObject<'source> for hashbrown::HashSet<K, S>
|
||||
impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet<K, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
match ob.downcast::<PySet>() {
|
||||
Ok(set) => set.iter().map(K::extract).collect(),
|
||||
Ok(set) => set.iter().map(|any| any.extract()).collect(),
|
||||
Err(err) => {
|
||||
if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
|
||||
frozen_set.iter().map(K::extract).collect()
|
||||
frozen_set.iter().map(|any| any.extract()).collect()
|
||||
} else {
|
||||
Err(PyErr::from(err))
|
||||
}
|
||||
|
|
|
@ -87,8 +87,10 @@
|
|||
//! # if another hash table was used, the order could be random
|
||||
//! ```
|
||||
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::dict::PyDictMethods;
|
||||
use crate::types::*;
|
||||
use crate::{FromPyObject, IntoPy, PyErr, PyObject, Python, ToPyObject};
|
||||
use crate::{Bound, FromPyObject, IntoPy, PyErr, PyObject, Python, ToPyObject};
|
||||
use std::{cmp, hash};
|
||||
|
||||
impl<K, V, H> ToPyObject for indexmap::IndexMap<K, V, H>
|
||||
|
@ -116,17 +118,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V, S> FromPyObject<'source> for indexmap::IndexMap<K, V, S>
|
||||
impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap<K, V, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'source>,
|
||||
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'py>,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict: &PyDict = ob.downcast()?;
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
|
||||
let dict = ob.downcast::<PyDict>()?;
|
||||
let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default());
|
||||
for (k, v) in dict {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(k.extract()?, v.extract()?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
|
|
@ -47,8 +47,13 @@
|
|||
//! assert n + 1 == value
|
||||
//! ```
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::types::bytes::PyBytesMethods;
|
||||
use crate::{
|
||||
ffi, types::*, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
ffi,
|
||||
instance::Bound,
|
||||
types::{any::PyAnyMethods, *},
|
||||
FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
|
@ -106,8 +111,8 @@ bigint_conversion!(BigUint, 0, BigUint::to_bytes_le);
|
|||
bigint_conversion!(BigInt, 1, BigInt::to_signed_bytes_le);
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
||||
impl<'source> FromPyObject<'source> for BigInt {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<BigInt> {
|
||||
impl<'py> FromPyObject<'py> for BigInt {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<BigInt> {
|
||||
let py = ob.py();
|
||||
// fast path - checking for subclass of `int` just checks a bit in the type object
|
||||
let num_owned: Py<PyLong>;
|
||||
|
@ -115,7 +120,7 @@ impl<'source> FromPyObject<'source> for BigInt {
|
|||
long
|
||||
} else {
|
||||
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
||||
num_owned.as_ref(py)
|
||||
num_owned.bind(py)
|
||||
};
|
||||
let n_bits = int_n_bits(num)?;
|
||||
if n_bits == 0 {
|
||||
|
@ -154,8 +159,8 @@ impl<'source> FromPyObject<'source> for BigInt {
|
|||
}
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
||||
impl<'source> FromPyObject<'source> for BigUint {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<BigUint> {
|
||||
impl<'py> FromPyObject<'py> for BigUint {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<BigUint> {
|
||||
let py = ob.py();
|
||||
// fast path - checking for subclass of `int` just checks a bit in the type object
|
||||
let num_owned: Py<PyLong>;
|
||||
|
@ -163,7 +168,7 @@ impl<'source> FromPyObject<'source> for BigUint {
|
|||
long
|
||||
} else {
|
||||
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
||||
num_owned.as_ref(py)
|
||||
num_owned.bind(py)
|
||||
};
|
||||
let n_bits = int_n_bits(num)?;
|
||||
if n_bits == 0 {
|
||||
|
@ -184,7 +189,11 @@ impl<'source> FromPyObject<'source> for BigUint {
|
|||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[inline]
|
||||
fn int_to_u32_vec(long: &PyLong, n_digits: usize, is_signed: bool) -> PyResult<Vec<u32>> {
|
||||
fn int_to_u32_vec(
|
||||
long: &Bound<'_, PyLong>,
|
||||
n_digits: usize,
|
||||
is_signed: bool,
|
||||
) -> PyResult<Vec<u32>> {
|
||||
let mut buffer = Vec::with_capacity(n_digits);
|
||||
unsafe {
|
||||
crate::err::error_on_minusone(
|
||||
|
@ -207,11 +216,15 @@ fn int_to_u32_vec(long: &PyLong, n_digits: usize, is_signed: bool) -> PyResult<V
|
|||
}
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn int_to_py_bytes(long: &PyLong, n_bytes: usize, is_signed: bool) -> PyResult<&PyBytes> {
|
||||
fn int_to_py_bytes<'py>(
|
||||
long: &Bound<'py, PyLong>,
|
||||
n_bytes: usize,
|
||||
is_signed: bool,
|
||||
) -> PyResult<Bound<'py, PyBytes>> {
|
||||
use crate::intern;
|
||||
let py = long.py();
|
||||
let kwargs = if is_signed {
|
||||
let kwargs = PyDict::new(py);
|
||||
let kwargs = PyDict::new_bound(py);
|
||||
kwargs.set_item(intern!(py, "signed"), true)?;
|
||||
Some(kwargs)
|
||||
} else {
|
||||
|
@ -220,13 +233,13 @@ fn int_to_py_bytes(long: &PyLong, n_bytes: usize, is_signed: bool) -> PyResult<&
|
|||
let bytes = long.call_method(
|
||||
intern!(py, "to_bytes"),
|
||||
(n_bytes, intern!(py, "little")),
|
||||
kwargs,
|
||||
kwargs.as_ref(),
|
||||
)?;
|
||||
Ok(bytes.downcast()?)
|
||||
Ok(bytes.downcast_into()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn int_n_bits(long: &PyLong) -> PyResult<usize> {
|
||||
fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult<usize> {
|
||||
let py = long.py();
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
{
|
||||
|
@ -242,7 +255,7 @@ fn int_n_bits(long: &PyLong) -> PyResult<usize> {
|
|||
{
|
||||
// slow path
|
||||
long.call_method0(crate::intern!(py, "bit_length"))
|
||||
.and_then(PyAny::extract)
|
||||
.and_then(|any| any.extract())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,7 +343,7 @@ mod tests {
|
|||
let locals = PyDict::new(py);
|
||||
locals.set_item("index", index).unwrap();
|
||||
let ob = py.eval("index.C(10)", None, Some(locals)).unwrap();
|
||||
let _: BigInt = FromPyObject::extract(ob).unwrap();
|
||||
let _: BigInt = ob.extract().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -51,18 +51,22 @@
|
|||
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::sync::GILOnceCell;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::string::PyStringMethods;
|
||||
use crate::types::PyType;
|
||||
use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use crate::{
|
||||
intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use rust_decimal::Decimal;
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromPyObject<'_> for Decimal {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
// use the string representation to not be lossy
|
||||
if let Ok(val) = obj.extract() {
|
||||
Ok(Decimal::new(val, 0))
|
||||
} else {
|
||||
Decimal::from_str(obj.str()?.to_str()?)
|
||||
Decimal::from_str(&obj.str()?.to_cow()?)
|
||||
.map_err(|e| PyValueError::new_err(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +135,7 @@ mod test_rust_decimal {
|
|||
.unwrap();
|
||||
// Checks if Python Decimal -> Rust Decimal conversion is correct
|
||||
let py_dec = locals.get_item("py_dec").unwrap().unwrap();
|
||||
let py_result: Decimal = FromPyObject::extract(py_dec).unwrap();
|
||||
let py_result: Decimal = py_dec.extract().unwrap();
|
||||
assert_eq!(rs_orig, py_result);
|
||||
})
|
||||
}
|
||||
|
@ -193,7 +197,7 @@ mod test_rust_decimal {
|
|||
)
|
||||
.unwrap();
|
||||
let py_dec = locals.get_item("py_dec").unwrap().unwrap();
|
||||
let roundtripped: Result<Decimal, PyErr> = FromPyObject::extract(py_dec);
|
||||
let roundtripped: Result<Decimal, PyErr> = py_dec.extract();
|
||||
assert!(roundtripped.is_err());
|
||||
})
|
||||
}
|
||||
|
@ -209,7 +213,7 @@ mod test_rust_decimal {
|
|||
)
|
||||
.unwrap();
|
||||
let py_dec = locals.get_item("py_dec").unwrap().unwrap();
|
||||
let roundtripped: Result<Decimal, PyErr> = FromPyObject::extract(py_dec);
|
||||
let roundtripped: Result<Decimal, PyErr> = py_dec.extract();
|
||||
assert!(roundtripped.is_err());
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,10 +18,12 @@
|
|||
use crate::exceptions::PyTypeError;
|
||||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::list::new_from_iter;
|
||||
use crate::types::{PySequence, PyString};
|
||||
use crate::{
|
||||
ffi, FromPyObject, IntoPy, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject,
|
||||
err::DowncastError, ffi, Bound, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use smallvec::{Array, SmallVec};
|
||||
|
||||
|
@ -52,12 +54,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, A> FromPyObject<'a> for SmallVec<A>
|
||||
impl<'py, A> FromPyObject<'py> for SmallVec<A>
|
||||
where
|
||||
A: Array,
|
||||
A::Item: FromPyObject<'a>,
|
||||
A::Item: FromPyObject<'py>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
if obj.is_instance_of::<PyString>() {
|
||||
return Err(PyTypeError::new_err("Can't extract `str` to `SmallVec`"));
|
||||
}
|
||||
|
@ -70,18 +72,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_sequence<'s, A>(obj: &'s PyAny) -> PyResult<SmallVec<A>>
|
||||
fn extract_sequence<'py, A>(obj: &Bound<'py, PyAny>) -> PyResult<SmallVec<A>>
|
||||
where
|
||||
A: Array,
|
||||
A::Item: FromPyObject<'s>,
|
||||
A::Item: FromPyObject<'py>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
let seq = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
obj.downcast_unchecked::<PySequence>()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
return Err(DowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use crate::instance::Bound;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::PySequence;
|
||||
use crate::{exceptions, PyErr};
|
||||
use crate::{
|
||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject,
|
||||
err::DowncastError, ffi, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use crate::{exceptions, PyErr};
|
||||
|
||||
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
||||
where
|
||||
|
@ -42,33 +45,33 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
||||
impl<'py, T, const N: usize> FromPyObject<'py> for [T; N]
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
T: FromPyObject<'py>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
||||
fn create_array_from_obj<'py, T, const N: usize>(obj: &Bound<'py, PyAny>) -> PyResult<[T; N]>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
T: FromPyObject<'py>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
let seq = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
obj.downcast_unchecked::<PySequence>()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
return Err(DowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
let seq_len = seq.len()?;
|
||||
if seq_len != N {
|
||||
return Err(invalid_sequence_length(N, seq_len));
|
||||
}
|
||||
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
|
||||
array_try_from_fn(|idx| seq.get_item(idx).and_then(|any| any.extract()))
|
||||
}
|
||||
|
||||
// TODO use std::array::try_from_fn, if that stabilises:
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::instance::Bound;
|
||||
use crate::sync::GILOnceCell;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::string::PyStringMethods;
|
||||
use crate::types::PyType;
|
||||
use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
|
||||
impl FromPyObject<'_> for IpAddr {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
match obj.getattr(intern!(obj.py(), "packed")) {
|
||||
Ok(packed) => {
|
||||
if let Ok(packed) = packed.extract::<[u8; 4]>() {
|
||||
|
@ -19,7 +22,7 @@ impl FromPyObject<'_> for IpAddr {
|
|||
}
|
||||
Err(_) => {
|
||||
// We don't have a .packed attribute, so we try to construct an IP from str().
|
||||
obj.str()?.to_str()?.parse().map_err(PyValueError::new_err)
|
||||
obj.str()?.to_cow()?.parse().map_err(PyValueError::new_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use std::{cmp, collections, hash};
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
types::{IntoPyDict, PyDict},
|
||||
instance::Bound,
|
||||
types::dict::PyDictMethods,
|
||||
types::{any::PyAnyMethods, IntoPyDict, PyDict},
|
||||
FromPyObject, IntoPy, PyAny, PyErr, PyObject, Python, ToPyObject,
|
||||
};
|
||||
|
||||
|
@ -65,17 +67,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V, S> FromPyObject<'source> for collections::HashMap<K, V, S>
|
||||
impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap<K, V, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'source>,
|
||||
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'py>,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict: &PyDict = ob.downcast()?;
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
|
||||
let dict = ob.downcast::<PyDict>()?;
|
||||
let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default());
|
||||
for (k, v) in dict {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(k.extract()?, v.extract()?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
@ -86,16 +88,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V> FromPyObject<'source> for collections::BTreeMap<K, V>
|
||||
impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap<K, V>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Ord,
|
||||
V: FromPyObject<'source>,
|
||||
K: FromPyObject<'py> + cmp::Ord,
|
||||
V: FromPyObject<'py>,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict: &PyDict = ob.downcast()?;
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
|
||||
let dict = ob.downcast::<PyDict>()?;
|
||||
let mut ret = collections::BTreeMap::new();
|
||||
for (k, v) in dict {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(k.extract()?, v.extract()?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::{
|
||||
exceptions, ffi, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
exceptions, ffi, Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
use std::num::{
|
||||
|
@ -29,8 +31,8 @@ macro_rules! int_fits_larger_int {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
impl FromPyObject<'_> for $rust_type {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let val: $larger_type = obj.extract()?;
|
||||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
|
||||
|
@ -94,8 +96,8 @@ macro_rules! int_convert_u64_or_i64 {
|
|||
TypeInfo::builtin("int")
|
||||
}
|
||||
}
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<$rust_type> {
|
||||
impl FromPyObject<'_> for $rust_type {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
||||
extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call)
|
||||
}
|
||||
|
||||
|
@ -125,8 +127,8 @@ macro_rules! int_fits_c_long {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
impl<'py> FromPyObject<'py> for $rust_type {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?;
|
||||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
|
||||
|
@ -210,8 +212,8 @@ mod fast_128bit_int_conversion {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
impl FromPyObject<'_> for $rust_type {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
||||
let num = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(ob.py(), ffi::PyNumber_Index(ob.as_ptr()))?
|
||||
};
|
||||
|
@ -279,8 +281,8 @@ mod slow_128bit_int_conversion {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
impl FromPyObject<'_> for $rust_type {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
||||
let py = ob.py();
|
||||
unsafe {
|
||||
let lower = err_if_invalid_value(
|
||||
|
@ -338,8 +340,8 @@ macro_rules! nonzero_int_impl {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $nonzero_type {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
impl FromPyObject<'_> for $nonzero_type {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let val: $primitive_type = obj.extract()?;
|
||||
<$nonzero_type>::try_from(val)
|
||||
.map_err(|_| exceptions::PyValueError::new_err("invalid zero value"))
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crate::instance::Bound;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::PyString;
|
||||
use crate::{ffi, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use std::borrow::Cow;
|
||||
|
@ -51,8 +53,8 @@ impl ToPyObject for OsStr {
|
|||
// be impossible to implement on Windows. Hence it's omitted entirely
|
||||
|
||||
impl FromPyObject<'_> for OsString {
|
||||
fn extract(ob: &PyAny) -> PyResult<Self> {
|
||||
let pystring: &PyString = ob.downcast()?;
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let pystring = ob.downcast::<PyString>()?;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
|
@ -79,9 +81,11 @@ impl FromPyObject<'_> for OsString {
|
|||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::types::string::PyStringMethods;
|
||||
|
||||
// Take the quick and easy shortcut if UTF-8
|
||||
if let Ok(utf8_string) = pystring.to_str() {
|
||||
return Ok(utf8_string.to_owned().into());
|
||||
if let Ok(utf8_string) = pystring.to_cow() {
|
||||
return Ok(utf8_string.into_owned().into());
|
||||
}
|
||||
|
||||
// Get an owned allocated wide char buffer from PyString, which we have to deallocate
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
ffi, FromPyObject, FromPyPointer, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
use crate::instance::Bound;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::{ffi, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -14,10 +15,10 @@ impl ToPyObject for Path {
|
|||
// See osstr.rs for why there's no FromPyObject impl for &Path
|
||||
|
||||
impl FromPyObject<'_> for PathBuf {
|
||||
fn extract(ob: &PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
// We use os.fspath to get the underlying path as bytes or str
|
||||
let path = unsafe { PyAny::from_owned_ptr_or_err(ob.py(), ffi::PyOS_FSPath(ob.as_ptr())) }?;
|
||||
Ok(OsString::extract(path)?.into())
|
||||
let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? };
|
||||
Ok(path.extract::<OsString>()?.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@ use std::{cmp, collections, hash};
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
types::set::new_from_iter,
|
||||
instance::Bound,
|
||||
types::any::PyAnyMethods,
|
||||
types::frozenset::PyFrozenSetMethods,
|
||||
types::set::{new_from_iter, PySetMethods},
|
||||
types::{PyFrozenSet, PySet},
|
||||
FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
|
@ -48,17 +51,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K, S> FromPyObject<'source> for collections::HashSet<K, S>
|
||||
impl<'py, K, S> FromPyObject<'py> for collections::HashSet<K, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
match ob.downcast::<PySet>() {
|
||||
Ok(set) => set.iter().map(K::extract).collect(),
|
||||
Ok(set) => set.iter().map(|any| any.extract()).collect(),
|
||||
Err(err) => {
|
||||
if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
|
||||
frozen_set.iter().map(K::extract).collect()
|
||||
frozen_set.iter().map(|any| any.extract()).collect()
|
||||
} else {
|
||||
Err(PyErr::from(err))
|
||||
}
|
||||
|
@ -88,16 +91,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source, K> FromPyObject<'source> for collections::BTreeSet<K>
|
||||
impl<'py, K> FromPyObject<'py> for collections::BTreeSet<K>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Ord,
|
||||
K: FromPyObject<'py> + cmp::Ord,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
match ob.downcast::<PySet>() {
|
||||
Ok(set) => set.iter().map(K::extract).collect(),
|
||||
Ok(set) => set.iter().map(|any| any.extract()).collect(),
|
||||
Err(err) => {
|
||||
if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
|
||||
frozen_set.iter().map(K::extract).collect()
|
||||
frozen_set.iter().map(|any| any.extract()).collect()
|
||||
} else {
|
||||
Err(PyErr::from(err))
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ impl<'a> IntoPy<PyObject> for &'a [u8] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPyObject<'a> for &'a [u8] {
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
impl<'py> FromPyObject<'py> for &'py [u8] {
|
||||
fn extract(obj: &'py PyAny) -> PyResult<Self> {
|
||||
Ok(obj.downcast::<PyBytes>()?.as_bytes())
|
||||
}
|
||||
|
||||
|
@ -26,14 +26,13 @@ impl<'a> FromPyObject<'a> for &'a [u8] {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::FromPyObject;
|
||||
use crate::Python;
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytes() {
|
||||
Python::with_gil(|py| {
|
||||
let py_bytes = py.eval("b'Hello Python'", None, None).unwrap();
|
||||
let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap();
|
||||
let bytes: &[u8] = py_bytes.extract().unwrap();
|
||||
assert_eq!(bytes, b"Hello Python");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use std::borrow::Cow;
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
types::PyString, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
instance::Bound,
|
||||
types::{any::PyAnyMethods, string::PyStringMethods, PyString},
|
||||
FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
|
||||
/// Converts a Rust `str` to a Python object.
|
||||
|
@ -112,22 +114,22 @@ impl<'a> IntoPy<PyObject> for &'a String {
|
|||
|
||||
/// Allows extracting strings from Python objects.
|
||||
/// Accepts Python `str` and `unicode` objects.
|
||||
impl<'source> FromPyObject<'source> for &'source str {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
impl<'py> FromPyObject<'py> for &'py str {
|
||||
fn extract(ob: &'py PyAny) -> PyResult<Self> {
|
||||
ob.downcast::<PyString>()?.to_str()
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-inspect")]
|
||||
fn type_input() -> TypeInfo {
|
||||
<String>::type_input()
|
||||
<String as crate::FromPyObject>::type_input()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows extracting strings from Python objects.
|
||||
/// Accepts Python `str` and `unicode` objects.
|
||||
impl FromPyObject<'_> for String {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
obj.downcast::<PyString>()?.to_str().map(ToOwned::to_owned)
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
obj.downcast::<PyString>()?.to_cow().map(Cow::into_owned)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-inspect")]
|
||||
|
@ -137,8 +139,8 @@ impl FromPyObject<'_> for String {
|
|||
}
|
||||
|
||||
impl FromPyObject<'_> for char {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
let s = obj.downcast::<PyString>()?.to_str()?;
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let s = obj.downcast::<PyString>()?.to_cow()?;
|
||||
let mut iter = s.chars();
|
||||
if let (Some(ch), None) = (iter.next(), iter.next()) {
|
||||
Ok(ch)
|
||||
|
@ -158,7 +160,7 @@ impl FromPyObject<'_> for char {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPy, PyObject, ToPyObject};
|
||||
use crate::{IntoPy, PyObject, ToPyObject};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[test]
|
||||
|
@ -198,7 +200,7 @@ mod tests {
|
|||
let s = "Hello Python";
|
||||
let py_string = s.to_object(py);
|
||||
|
||||
let s2: &str = FromPyObject::extract(py_string.as_ref(py)).unwrap();
|
||||
let s2: &str = py_string.as_ref(py).extract().unwrap();
|
||||
assert_eq!(s, s2);
|
||||
})
|
||||
}
|
||||
|
@ -208,7 +210,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let ch = '😃';
|
||||
let py_string = ch.to_object(py);
|
||||
let ch2: char = FromPyObject::extract(py_string.as_ref(py)).unwrap();
|
||||
let ch2: char = py_string.as_ref(py).extract().unwrap();
|
||||
assert_eq!(ch, ch2);
|
||||
})
|
||||
}
|
||||
|
@ -218,7 +220,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let s = "Hello Python";
|
||||
let py_string = s.to_object(py);
|
||||
let err: crate::PyResult<char> = FromPyObject::extract(py_string.as_ref(py));
|
||||
let err: crate::PyResult<char> = py_string.as_ref(py).extract();
|
||||
assert!(err
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
use crate::exceptions::{PyOverflowError, PyValueError};
|
||||
use crate::sync::GILOnceCell;
|
||||
use crate::types::any::PyAnyMethods;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::types::PyType;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::types::{timezone_utc, PyDateTime, PyDelta, PyDeltaAccess};
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::Py;
|
||||
use crate::{intern, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject};
|
||||
use crate::{
|
||||
intern, Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
const SECONDS_PER_DAY: u64 = 24 * 60 * 60;
|
||||
|
||||
impl FromPyObject<'_> for Duration {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
let (days, seconds, microseconds) = {
|
||||
let delta: &PyDelta = obj.downcast()?;
|
||||
let delta = obj.downcast::<PyDelta>()?;
|
||||
(
|
||||
delta.get_days(),
|
||||
delta.get_seconds(),
|
||||
|
@ -93,7 +96,7 @@ impl IntoPy<PyObject> for Duration {
|
|||
// TODO: it might be nice to investigate using timestamps anyway, at least when the datetime is a safe range.
|
||||
|
||||
impl FromPyObject<'_> for SystemTime {
|
||||
fn extract(obj: &PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
|
||||
let duration_since_unix_epoch: Duration = obj
|
||||
.call_method1(intern!(obj.py(), "__sub__"), (unix_epoch_py(obj.py()),))?
|
||||
.extract()?;
|
||||
|
|
|
@ -996,9 +996,9 @@ impl<T> Py<T> {
|
|||
/// Extracts some type from the Python object.
|
||||
///
|
||||
/// This is a wrapper function around `FromPyObject::extract()`.
|
||||
pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult<D>
|
||||
pub fn extract<'py, D>(&'py self, py: Python<'py>) -> PyResult<D>
|
||||
where
|
||||
D: FromPyObject<'p>,
|
||||
D: FromPyObject<'py>,
|
||||
{
|
||||
FromPyObject::extract(unsafe { py.from_borrowed_ptr(self.as_ptr()) })
|
||||
}
|
||||
|
|
|
@ -801,9 +801,9 @@ impl PyAny {
|
|||
///
|
||||
/// This is a wrapper function around [`FromPyObject::extract()`].
|
||||
#[inline]
|
||||
pub fn extract<'a, D>(&'a self) -> PyResult<D>
|
||||
pub fn extract<'py, D>(&'py self) -> PyResult<D>
|
||||
where
|
||||
D: FromPyObject<'a>,
|
||||
D: FromPyObject<'py>,
|
||||
{
|
||||
FromPyObject::extract(self)
|
||||
}
|
||||
|
|
|
@ -203,8 +203,8 @@ impl<I: SliceIndex<[u8]>> Index<I> for Bound<'_, PyBytes> {
|
|||
/// If the source object is a `bytes` object, the `Cow` will be borrowed and
|
||||
/// pointing into the source object, and no copying or heap allocations will happen.
|
||||
/// If it is a `bytearray`, its contents will be copied to an owned `Cow`.
|
||||
impl<'source> FromPyObject<'source> for Cow<'source, [u8]> {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
impl<'py> FromPyObject<'py> for Cow<'py, [u8]> {
|
||||
fn extract(ob: &'py PyAny) -> PyResult<Self> {
|
||||
if let Ok(bytes) = ob.downcast::<PyBytes>() {
|
||||
return Ok(Cow::Borrowed(bytes.as_bytes()));
|
||||
}
|
||||
|
|
|
@ -95,10 +95,10 @@ impl IntoPy<PyObject> for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for f64 {
|
||||
impl<'py> FromPyObject<'py> for f64 {
|
||||
// PyFloat_AsDouble returns -1.0 upon failure
|
||||
#![allow(clippy::float_cmp)]
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
// On non-limited API, .value() uses PyFloat_AS_DOUBLE which
|
||||
// allows us to have an optimized fast path for the case when
|
||||
// we have exactly a `float` object (it's not worth going through
|
||||
|
@ -142,8 +142,8 @@ impl IntoPy<PyObject> for f32 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for f32 {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
impl<'py> FromPyObject<'py> for f32 {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
Ok(obj.extract::<f64>()? as f32)
|
||||
}
|
||||
|
||||
|
|
|
@ -224,8 +224,8 @@ macro_rules! pyobject_native_type_extract {
|
|||
($name:ty $(;$generics:ident)*) => {
|
||||
impl<'py, $($generics,)*> $crate::FromPyObject<'py> for &'py $name {
|
||||
#[inline]
|
||||
fn extract(obj: &'py $crate::PyAny) -> $crate::PyResult<Self> {
|
||||
obj.downcast().map_err(::std::convert::Into::into)
|
||||
fn extract_bound(obj: &$crate::Bound<'py, $crate::PyAny>) -> $crate::PyResult<Self> {
|
||||
::std::clone::Clone::clone(obj).into_gil_ref().downcast().map_err(::std::convert::Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::pyclass::PyClass;
|
|||
use crate::types::{
|
||||
any::PyAnyMethods, list::PyListMethods, PyAny, PyCFunction, PyDict, PyList, PyString,
|
||||
};
|
||||
use crate::{exceptions, ffi, Bound, IntoPy, Py, PyNativeType, PyObject, Python};
|
||||
use crate::{exceptions, ffi, Bound, FromPyObject, IntoPy, Py, PyNativeType, PyObject, Python};
|
||||
use std::ffi::CString;
|
||||
use std::str;
|
||||
|
||||
|
@ -146,7 +146,7 @@ impl PyModule {
|
|||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
|
||||
<&PyModule as crate::FromPyObject>::extract(py.from_owned_ptr_or_err(mptr)?)
|
||||
<&PyModule as FromPyObject>::extract(py.from_owned_ptr_or_err(mptr)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
||||
use crate::err::{self, DowncastError, PyDowncastError, PyErr, PyResult};
|
||||
use crate::exceptions::PyTypeError;
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
#[cfg(feature = "experimental-inspect")]
|
||||
|
@ -11,6 +11,8 @@ use crate::type_object::PyTypeInfo;
|
|||
use crate::types::{PyAny, PyList, PyString, PyTuple, PyType};
|
||||
use crate::{ffi, FromPyObject, Py, PyNativeType, PyTypeCheck, Python, ToPyObject};
|
||||
|
||||
use super::any::PyAnyMethods;
|
||||
|
||||
/// Represents a reference to a Python object supporting the sequence protocol.
|
||||
#[repr(transparent)]
|
||||
pub struct PySequence(PyAny);
|
||||
|
@ -477,11 +479,11 @@ fn sequence_slice(seq: &PySequence, start: usize, end: usize) -> &PySequence {
|
|||
|
||||
index_impls!(PySequence, "sequence", sequence_len, sequence_slice);
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for Vec<T>
|
||||
impl<'py, T> FromPyObject<'py> for Vec<T>
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
T: FromPyObject<'py>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
if obj.is_instance_of::<PyString>() {
|
||||
return Err(PyTypeError::new_err("Can't extract `str` to `Vec`"));
|
||||
}
|
||||
|
@ -494,17 +496,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>>
|
||||
fn extract_sequence<'py, T>(obj: &Bound<'py, PyAny>) -> PyResult<Vec<T>>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
T: FromPyObject<'py>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
let seq = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
obj.downcast_unchecked::<PySequence>()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
return Err(DowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -602,7 +604,6 @@ mod tests {
|
|||
let v = "London Calling";
|
||||
let ob = v.to_object(py);
|
||||
|
||||
assert!(ob.extract::<Vec<&str>>(py).is_err());
|
||||
assert!(ob.extract::<Vec<String>>(py).is_err());
|
||||
assert!(ob.extract::<Vec<char>>(py).is_err());
|
||||
});
|
||||
|
|
|
@ -629,7 +629,7 @@ impl IntoPy<Py<PyTuple>> for Bound<'_, PyTuple> {
|
|||
}
|
||||
|
||||
#[cold]
|
||||
fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr {
|
||||
fn wrong_tuple_length(t: &Bound<'_, PyTuple>, expected_length: usize) -> PyErr {
|
||||
let msg = format!(
|
||||
"expected tuple of length {}, but got tuple of length {}",
|
||||
expected_length,
|
||||
|
@ -666,8 +666,8 @@ fn type_output() -> TypeInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
|
||||
fn extract(obj: &'s PyAny) -> PyResult<Self>
|
||||
impl<'py, $($T: FromPyObject<'py>),+> FromPyObject<'py> for ($($T,)+) {
|
||||
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self>
|
||||
{
|
||||
let t = obj.downcast::<PyTuple>()?;
|
||||
if t.len() == $length {
|
||||
|
|
Loading…
Reference in New Issue