Skip to content

Commit 8f4c441

Browse files
committed
migrate many FromPyObject implementations to Bound API
1 parent d35a6a1 commit 8f4c441

27 files changed

+202
-155
lines changed

pyo3-benches/benches/bench_dict.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criter
33
use pyo3::types::IntoPyDict;
44
use pyo3::{prelude::*, types::PyMapping};
55
use std::collections::{BTreeMap, HashMap};
6+
use std::hint::black_box;
67

78
fn iter_dict(b: &mut Bencher<'_>) {
89
Python::with_gil(|py| {
@@ -71,13 +72,12 @@ fn extract_hashbrown_map(b: &mut Bencher<'_>) {
7172
fn mapping_from_dict(b: &mut Bencher<'_>) {
7273
Python::with_gil(|py| {
7374
const LEN: usize = 100_000;
74-
let dict = (0..LEN as u64)
75+
let dict = &(0..LEN as u64)
7576
.map(|i| (i, i * 2))
7677
.into_py_dict(py)
77-
.to_object(py);
78-
b.iter(|| {
79-
let _: &PyMapping = dict.extract(py).unwrap();
80-
});
78+
.to_object(py)
79+
.into_bound(py);
80+
b.iter(|| black_box(dict).downcast::<PyMapping>().unwrap());
8181
});
8282
}
8383

pyo3-benches/benches/bench_list.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::hint::black_box;
2+
13
use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion};
24

35
use pyo3::prelude::*;
@@ -56,11 +58,8 @@ fn list_get_item_unchecked(b: &mut Bencher<'_>) {
5658
fn sequence_from_list(b: &mut Bencher<'_>) {
5759
Python::with_gil(|py| {
5860
const LEN: usize = 50_000;
59-
let list = PyList::new_bound(py, 0..LEN).to_object(py);
60-
b.iter(|| {
61-
let seq: &PySequence = list.extract(py).unwrap();
62-
seq
63-
});
61+
let list = &PyList::new_bound(py, 0..LEN);
62+
b.iter(|| black_box(list).downcast::<PySequence>().unwrap());
6463
});
6564
}
6665

pyo3-benches/benches/bench_tuple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ fn sequence_from_tuple(b: &mut Bencher<'_>) {
9393
Python::with_gil(|py| {
9494
const LEN: usize = 50_000;
9595
let tuple = PyTuple::new_bound(py, 0..LEN).to_object(py);
96-
b.iter(|| tuple.extract::<&PySequence>(py).unwrap());
96+
b.iter(|| tuple.downcast::<PySequence>(py).unwrap());
9797
});
9898
}
9999

src/buffer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// DEALINGS IN THE SOFTWARE.
1919

2020
//! `PyBuffer` implementation
21+
use crate::instance::Bound;
2122
use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
2223
use std::marker::PhantomData;
2324
use std::os::raw;
@@ -182,8 +183,8 @@ pub unsafe trait Element: Copy {
182183
}
183184

184185
impl<'source, T: Element> FromPyObject<'source> for PyBuffer<T> {
185-
fn extract(obj: &PyAny) -> PyResult<PyBuffer<T>> {
186-
Self::get(obj)
186+
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
187+
Self::get(obj.as_gil_ref())
187188
}
188189
}
189190

src/conversions/chrono.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError};
4343
#[cfg(Py_LIMITED_API)]
4444
use crate::sync::GILOnceCell;
45-
#[cfg(not(Py_LIMITED_API))]
4645
use crate::types::any::PyAnyMethods;
4746
#[cfg(not(Py_LIMITED_API))]
4847
use crate::types::datetime::timezone_from_offset;
@@ -52,9 +51,9 @@ use crate::types::{
5251
PyTzInfo, PyTzInfoAccess,
5352
};
5453
#[cfg(Py_LIMITED_API)]
55-
use crate::{intern, PyDowncastError};
54+
use crate::{intern, DowncastError};
5655
use crate::{
57-
FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
56+
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
5857
};
5958
use chrono::offset::{FixedOffset, Utc};
6059
use chrono::{
@@ -109,14 +108,14 @@ impl IntoPy<PyObject> for Duration {
109108
}
110109

111110
impl FromPyObject<'_> for Duration {
112-
fn extract(ob: &PyAny) -> PyResult<Duration> {
111+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Duration> {
113112
// Python size are much lower than rust size so we do not need bound checks.
114113
// 0 <= microseconds < 1000000
115114
// 0 <= seconds < 3600*24
116115
// -999999999 <= days <= 999999999
117116
#[cfg(not(Py_LIMITED_API))]
118117
let (days, seconds, microseconds) = {
119-
let delta: &PyDelta = ob.downcast()?;
118+
let delta = ob.downcast::<PyDelta>()?;
120119
(
121120
delta.get_days().into(),
122121
delta.get_seconds().into(),
@@ -166,10 +165,10 @@ impl IntoPy<PyObject> for NaiveDate {
166165
}
167166

168167
impl FromPyObject<'_> for NaiveDate {
169-
fn extract(ob: &PyAny) -> PyResult<NaiveDate> {
168+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<NaiveDate> {
170169
#[cfg(not(Py_LIMITED_API))]
171170
{
172-
let date: &PyDate = ob.downcast()?;
171+
let date = ob.downcast::<PyDate>()?;
173172
py_date_to_naive_date(date)
174173
}
175174
#[cfg(Py_LIMITED_API)]
@@ -211,10 +210,10 @@ impl IntoPy<PyObject> for NaiveTime {
211210
}
212211

213212
impl FromPyObject<'_> for NaiveTime {
214-
fn extract(ob: &PyAny) -> PyResult<NaiveTime> {
213+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<NaiveTime> {
215214
#[cfg(not(Py_LIMITED_API))]
216215
{
217-
let time: &PyTime = ob.downcast()?;
216+
let time = ob.downcast::<PyTime>()?;
218217
py_time_to_naive_time(time)
219218
}
220219
#[cfg(Py_LIMITED_API)]
@@ -238,9 +237,9 @@ impl IntoPy<PyObject> for NaiveDateTime {
238237
}
239238

240239
impl FromPyObject<'_> for NaiveDateTime {
241-
fn extract(dt: &PyAny) -> PyResult<NaiveDateTime> {
240+
fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult<NaiveDateTime> {
242241
#[cfg(not(Py_LIMITED_API))]
243-
let dt: &PyDateTime = dt.downcast()?;
242+
let dt = dt.downcast::<PyDateTime>()?;
244243
#[cfg(Py_LIMITED_API)]
245244
check_type(dt, &DatetimeTypes::get(dt.py()).datetime, "PyDateTime")?;
246245

@@ -277,9 +276,9 @@ impl<Tz: TimeZone> IntoPy<PyObject> for DateTime<Tz> {
277276
}
278277

279278
impl<Tz: TimeZone + for<'a> FromPyObject<'a>> FromPyObject<'_> for DateTime<Tz> {
280-
fn extract(dt: &PyAny) -> PyResult<DateTime<Tz>> {
279+
fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult<DateTime<Tz>> {
281280
#[cfg(not(Py_LIMITED_API))]
282-
let dt: &PyDateTime = dt.downcast()?;
281+
let dt = dt.downcast::<PyDateTime>()?;
283282
#[cfg(Py_LIMITED_API)]
284283
check_type(dt, &DatetimeTypes::get(dt.py()).datetime, "PyDateTime")?;
285284

@@ -339,7 +338,7 @@ impl FromPyObject<'_> for FixedOffset {
339338
///
340339
/// Note that the conversion will result in precision lost in microseconds as chrono offset
341340
/// does not supports microseconds.
342-
fn extract(ob: &PyAny) -> PyResult<FixedOffset> {
341+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<FixedOffset> {
343342
#[cfg(not(Py_LIMITED_API))]
344343
let ob: &PyTzInfo = ob.extract()?;
345344
#[cfg(Py_LIMITED_API)]
@@ -378,7 +377,7 @@ impl IntoPy<PyObject> for Utc {
378377
}
379378

380379
impl FromPyObject<'_> for Utc {
381-
fn extract(ob: &PyAny) -> PyResult<Utc> {
380+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Utc> {
382381
let py_utc = timezone_utc(ob.py());
383382
if ob.eq(py_utc)? {
384383
Ok(Utc)
@@ -480,7 +479,7 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult<NaiveDate> {
480479
}
481480

482481
#[cfg(Py_LIMITED_API)]
483-
fn py_date_to_naive_date(py_date: &PyAny) -> PyResult<NaiveDate> {
482+
fn py_date_to_naive_date(py_date: &Bound<'_, PyAny>) -> PyResult<NaiveDate> {
484483
NaiveDate::from_ymd_opt(
485484
py_date.getattr(intern!(py_date.py(), "year"))?.extract()?,
486485
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> {
501500
}
502501

503502
#[cfg(Py_LIMITED_API)]
504-
fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
503+
fn py_time_to_naive_time(py_time: &Bound<'_, PyAny>) -> PyResult<NaiveTime> {
505504
NaiveTime::from_hms_micro_opt(
506505
py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?,
507506
py_time
@@ -518,9 +517,9 @@ fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
518517
}
519518

520519
#[cfg(Py_LIMITED_API)]
521-
fn check_type(value: &PyAny, t: &PyObject, type_name: &'static str) -> PyResult<()> {
522-
if !value.is_instance(t.as_ref(value.py()))? {
523-
return Err(PyDowncastError::new(value, type_name).into());
520+
fn check_type(value: &Bound<'_, PyAny>, t: &PyObject, type_name: &'static str) -> PyResult<()> {
521+
if !value.is_instance(t.bind(value.py()))? {
522+
return Err(DowncastError::new(value, type_name).into());
524523
}
525524
Ok(())
526525
}

src/conversions/chrono_tz.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@
3535
//! ```
3636
use crate::exceptions::PyValueError;
3737
use crate::sync::GILOnceCell;
38+
use crate::types::any::PyAnyMethods;
3839
use crate::types::PyType;
39-
use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
40+
use crate::{
41+
intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
42+
};
4043
use chrono_tz::Tz;
4144
use std::str::FromStr;
4245

@@ -59,7 +62,7 @@ impl IntoPy<PyObject> for Tz {
5962
}
6063

6164
impl FromPyObject<'_> for Tz {
62-
fn extract(ob: &PyAny) -> PyResult<Tz> {
65+
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Tz> {
6366
Tz::from_str(ob.getattr(intern!(ob.py(), "key"))?.extract()?)
6467
.map_err(|e| PyValueError::new_err(e.to_string()))
6568
}

src/conversions/either.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
#[cfg(feature = "experimental-inspect")]
4747
use crate::inspect::types::TypeInfo;
4848
use crate::{
49-
exceptions::PyTypeError, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject,
49+
exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPy, PyAny,
50+
PyObject, PyResult, Python, ToPyObject,
5051
};
5152
use either::Either;
5253

@@ -87,7 +88,7 @@ where
8788
R: FromPyObject<'source>,
8889
{
8990
#[inline]
90-
fn extract(obj: &'source PyAny) -> PyResult<Self> {
91+
fn extract_bound(obj: &Bound<'source, PyAny>) -> PyResult<Self> {
9192
if let Ok(l) = obj.extract::<L>() {
9293
Ok(Either::Left(l))
9394
} else if let Ok(r) = obj.extract::<R>() {

src/conversions/hashbrown.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
//! Note that you must use compatible versions of hashbrown and PyO3.
1818
//! The required hashbrown version may vary based on the version of PyO3.
1919
use crate::{
20-
types::set::new_from_iter,
20+
types::any::PyAnyMethods,
21+
types::dict::PyDictMethods,
22+
types::frozenset::PyFrozenSetMethods,
23+
types::set::{new_from_iter, PySetMethods},
2124
types::{IntoPyDict, PyDict, PyFrozenSet, PySet},
22-
FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
25+
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
2326
};
2427
use std::{cmp, hash};
2528

@@ -54,11 +57,11 @@ where
5457
V: FromPyObject<'source>,
5558
S: hash::BuildHasher + Default,
5659
{
57-
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
58-
let dict: &PyDict = ob.downcast()?;
60+
fn extract_bound(ob: &Bound<'source, PyAny>) -> Result<Self, PyErr> {
61+
let dict = ob.downcast::<PyDict>()?;
5962
let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
60-
for (k, v) in dict {
61-
ret.insert(K::extract(k)?, V::extract(v)?);
63+
for (k, v) in dict.iter() {
64+
ret.insert(k.extract()?, v.extract()?);
6265
}
6366
Ok(ret)
6467
}
@@ -92,12 +95,12 @@ where
9295
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
9396
S: hash::BuildHasher + Default,
9497
{
95-
fn extract(ob: &'source PyAny) -> PyResult<Self> {
98+
fn extract_bound(ob: &Bound<'source, PyAny>) -> PyResult<Self> {
9699
match ob.downcast::<PySet>() {
97-
Ok(set) => set.iter().map(K::extract).collect(),
100+
Ok(set) => set.iter().map(|any| any.extract()).collect(),
98101
Err(err) => {
99102
if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
100-
frozen_set.iter().map(K::extract).collect()
103+
frozen_set.iter().map(|any| any.extract()).collect()
101104
} else {
102105
Err(PyErr::from(err))
103106
}

src/conversions/indexmap.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@
8787
//! # if another hash table was used, the order could be random
8888
//! ```
8989
90+
use crate::types::any::PyAnyMethods;
91+
use crate::types::dict::PyDictMethods;
9092
use crate::types::*;
91-
use crate::{FromPyObject, IntoPy, PyErr, PyObject, Python, ToPyObject};
93+
use crate::{Bound, FromPyObject, IntoPy, PyErr, PyObject, Python, ToPyObject};
9294
use std::{cmp, hash};
9395

9496
impl<K, V, H> ToPyObject for indexmap::IndexMap<K, V, H>
@@ -122,11 +124,11 @@ where
122124
V: FromPyObject<'source>,
123125
S: hash::BuildHasher + Default,
124126
{
125-
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
126-
let dict: &PyDict = ob.downcast()?;
127+
fn extract_bound(ob: &Bound<'source, PyAny>) -> Result<Self, PyErr> {
128+
let dict = ob.downcast::<PyDict>()?;
127129
let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default());
128-
for (k, v) in dict {
129-
ret.insert(K::extract(k)?, V::extract(v)?);
130+
for (k, v) in dict.iter() {
131+
ret.insert(k.extract()?, v.extract()?);
130132
}
131133
Ok(ret)
132134
}

0 commit comments

Comments
 (0)