Skip to content

Commit

Permalink
migrate many FromPyObject implementations to Bound API
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Feb 1, 2024
1 parent d35a6a1 commit 8f4c441
Show file tree
Hide file tree
Showing 27 changed files with 202 additions and 155 deletions.
10 changes: 5 additions & 5 deletions pyo3-benches/benches/bench_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down Expand Up @@ -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());
});
}

Expand Down
9 changes: 4 additions & 5 deletions pyo3-benches/benches/bench_list.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::hint::black_box;

use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion};

use pyo3::prelude::*;
Expand Down Expand Up @@ -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());
});
}

Expand Down
2 changes: 1 addition & 1 deletion pyo3-benches/benches/bench_tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
}

Expand Down
5 changes: 3 additions & 2 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -182,8 +183,8 @@ pub unsafe trait Element: Copy {
}

impl<'source, T: Element> FromPyObject<'source> for PyBuffer<T> {
fn extract(obj: &PyAny) -> PyResult<PyBuffer<T>> {
Self::get(obj)
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
Self::get(obj.as_gil_ref())
}
}

Expand Down
39 changes: 19 additions & 20 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::{
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand All @@ -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")?;

Expand Down Expand Up @@ -277,9 +276,9 @@ 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>> {
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")?;

Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()?,
Expand All @@ -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
Expand All @@ -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(())
}
Expand Down
7 changes: 5 additions & 2 deletions src/conversions/chrono_tz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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()))
}
Expand Down
5 changes: 3 additions & 2 deletions src/conversions/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -87,7 +88,7 @@ where
R: FromPyObject<'source>,
{
#[inline]
fn extract(obj: &'source PyAny) -> PyResult<Self> {
fn extract_bound(obj: &Bound<'source, PyAny>) -> PyResult<Self> {
if let Ok(l) = obj.extract::<L>() {
Ok(Either::Left(l))
} else if let Ok(r) = obj.extract::<R>() {
Expand Down
21 changes: 12 additions & 9 deletions src/conversions/hashbrown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -54,11 +57,11 @@ where
V: FromPyObject<'source>,
S: hash::BuildHasher + Default,
{
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
let dict: &PyDict = ob.downcast()?;
fn extract_bound(ob: &Bound<'source, 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)
}
Expand Down Expand Up @@ -92,12 +95,12 @@ where
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
S: hash::BuildHasher + Default,
{
fn extract(ob: &'source PyAny) -> PyResult<Self> {
fn extract_bound(ob: &Bound<'source, 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))
}
Expand Down
12 changes: 7 additions & 5 deletions src/conversions/indexmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -122,11 +124,11 @@ where
V: FromPyObject<'source>,
S: hash::BuildHasher + Default,
{
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
let dict: &PyDict = ob.downcast()?;
fn extract_bound(ob: &Bound<'source, 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)
}
Expand Down
Loading

0 comments on commit 8f4c441

Please sign in to comment.