Skip to content

Commit 635cb80

Browse files
authored
feature gate APIs using into_gil_ref (Part 1) (#4160)
1 parent d803f7f commit 635cb80

22 files changed

+336
-253
lines changed

src/conversions/std/array.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@ mod tests {
186186
Python::with_gil(|py| {
187187
let array: [f32; 4] = [0.0, -16.0, 16.0, 42.0];
188188
let pyobject = array.to_object(py);
189-
let pylist: &PyList = pyobject.extract(py).unwrap();
190-
assert_eq!(pylist[0].extract::<f32>().unwrap(), 0.0);
191-
assert_eq!(pylist[1].extract::<f32>().unwrap(), -16.0);
192-
assert_eq!(pylist[2].extract::<f32>().unwrap(), 16.0);
193-
assert_eq!(pylist[3].extract::<f32>().unwrap(), 42.0);
189+
let pylist = pyobject.downcast_bound::<PyList>(py).unwrap();
190+
assert_eq!(pylist.get_item(0).unwrap().extract::<f32>().unwrap(), 0.0);
191+
assert_eq!(pylist.get_item(1).unwrap().extract::<f32>().unwrap(), -16.0);
192+
assert_eq!(pylist.get_item(2).unwrap().extract::<f32>().unwrap(), 16.0);
193+
assert_eq!(pylist.get_item(3).unwrap().extract::<f32>().unwrap(), 42.0);
194194
});
195195
}
196196

@@ -213,11 +213,11 @@ mod tests {
213213
Python::with_gil(|py| {
214214
let array: [f32; 4] = [0.0, -16.0, 16.0, 42.0];
215215
let pyobject = array.into_py(py);
216-
let pylist: &PyList = pyobject.extract(py).unwrap();
217-
assert_eq!(pylist[0].extract::<f32>().unwrap(), 0.0);
218-
assert_eq!(pylist[1].extract::<f32>().unwrap(), -16.0);
219-
assert_eq!(pylist[2].extract::<f32>().unwrap(), 16.0);
220-
assert_eq!(pylist[3].extract::<f32>().unwrap(), 42.0);
216+
let pylist = pyobject.downcast_bound::<PyList>(py).unwrap();
217+
assert_eq!(pylist.get_item(0).unwrap().extract::<f32>().unwrap(), 0.0);
218+
assert_eq!(pylist.get_item(1).unwrap().extract::<f32>().unwrap(), -16.0);
219+
assert_eq!(pylist.get_item(2).unwrap().extract::<f32>().unwrap(), 16.0);
220+
assert_eq!(pylist.get_item(3).unwrap().extract::<f32>().unwrap(), 42.0);
221221
});
222222
}
223223

src/instance.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ impl<'py, T> Borrowed<'py, 'py, T>
667667
where
668668
T: HasPyGilRef,
669669
{
670+
#[cfg(feature = "gil-refs")]
670671
pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
671672
// Safety: self is a borrow over `'py`.
672673
#[allow(deprecated)]

src/internal_tricks.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub(crate) fn get_ssize_index(index: usize) -> Py_ssize_t {
4646
}
4747

4848
/// Implementations used for slice indexing PySequence, PyTuple, and PyList
49+
#[cfg(feature = "gil-refs")]
4950
macro_rules! index_impls {
5051
(
5152
$ty:ty,
@@ -154,6 +155,7 @@ macro_rules! index_impls {
154155
#[inline(never)]
155156
#[cold]
156157
#[track_caller]
158+
#[cfg(feature = "gil-refs")]
157159
pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
158160
panic!(
159161
"index {} out of range for {} of length {}",
@@ -164,6 +166,7 @@ pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
164166
#[inline(never)]
165167
#[cold]
166168
#[track_caller]
169+
#[cfg(feature = "gil-refs")]
167170
pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
168171
panic!(
169172
"range start index {} out of range for {} of length {}",
@@ -174,6 +177,7 @@ pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize
174177
#[inline(never)]
175178
#[cold]
176179
#[track_caller]
180+
#[cfg(feature = "gil-refs")]
177181
pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
178182
panic!(
179183
"range end index {} out of range for {} of length {}",
@@ -184,6 +188,7 @@ pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize)
184188
#[inline(never)]
185189
#[cold]
186190
#[track_caller]
191+
#[cfg(feature = "gil-refs")]
187192
pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
188193
panic!("slice index starts at {} but ends at {}", index, end);
189194
}

src/macros.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ macro_rules! py_run_impl {
120120

121121
/// Wraps a Rust function annotated with [`#[pyfunction]`](macro@crate::pyfunction).
122122
///
123-
/// This can be used with [`PyModule::add_function`](crate::types::PyModule::add_function) to add free
124-
/// functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
123+
/// This can be used with [`PyModule::add_function`](crate::types::PyModuleMethods::add_function) to
124+
/// add free functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
125125
/// information.
126126
///
127127
/// During the migration from the GIL Ref API to the Bound API, the return type of this macro will
@@ -157,8 +157,9 @@ macro_rules! wrap_pyfunction {
157157

158158
/// Wraps a Rust function annotated with [`#[pyfunction]`](macro@crate::pyfunction).
159159
///
160-
/// This can be used with [`PyModule::add_function`](crate::types::PyModule::add_function) to add free
161-
/// functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more information.
160+
/// This can be used with [`PyModule::add_function`](crate::types::PyModuleMethods::add_function) to
161+
/// add free functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
162+
/// information.
162163
#[macro_export]
163164
macro_rules! wrap_pyfunction_bound {
164165
($function:path) => {
@@ -183,7 +184,7 @@ macro_rules! wrap_pyfunction_bound {
183184
/// Python module.
184185
///
185186
/// Use this together with [`#[pymodule]`](crate::pymodule) and
186-
/// [`PyModule::add_wrapped`](crate::types::PyModule::add_wrapped).
187+
/// [`PyModule::add_wrapped`](crate::types::PyModuleMethods::add_wrapped).
187188
#[macro_export]
188189
macro_rules! wrap_pymodule {
189190
($module:path) => {

src/tests/common.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,18 @@ mod inner {
114114
}
115115

116116
impl<'py> CatchWarnings<'py> {
117-
pub fn enter<R>(py: Python<'py>, f: impl FnOnce(&PyList) -> PyResult<R>) -> PyResult<R> {
117+
pub fn enter<R>(
118+
py: Python<'py>,
119+
f: impl FnOnce(&Bound<'py, PyList>) -> PyResult<R>,
120+
) -> PyResult<R> {
118121
let warnings = py.import_bound("warnings")?;
119122
let kwargs = [("record", true)].into_py_dict_bound(py);
120123
let catch_warnings = warnings
121124
.getattr("catch_warnings")?
122125
.call((), Some(&kwargs))?;
123-
let list = catch_warnings.call_method0("__enter__")?.extract()?;
126+
let list = catch_warnings.call_method0("__enter__")?.downcast_into()?;
124127
let _guard = Self { catch_warnings };
125-
f(list)
128+
f(&list)
126129
}
127130
}
128131

@@ -139,6 +142,7 @@ mod inner {
139142
macro_rules! assert_warnings {
140143
($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{
141144
$crate::tests::common::CatchWarnings::enter($py, |w| {
145+
use $crate::types::{PyListMethods, PyStringMethods};
142146
$body;
143147
let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object_bound($py), $message)),+];
144148
assert_eq!(w.len(), expected_warnings.len());

src/tests/hygiene/pymodule.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fn foo(_py: crate::Python<'_>, _m: &crate::types::PyModule) -> crate::PyResult<(
1414
::std::result::Result::Ok(())
1515
}
1616

17+
#[cfg(feature = "gil-refs")]
1718
#[allow(deprecated)]
1819
#[crate::pymodule]
1920
#[pyo3(crate = "crate")]

src/types/bytearray.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ impl<'a> Borrowed<'a, '_, PyByteArray> {
491491
}
492492
}
493493

494+
#[cfg(feature = "gil-refs")]
494495
impl<'py> TryFrom<&'py PyAny> for &'py PyByteArray {
495496
type Error = crate::PyErr;
496497

src/types/complex.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ mod not_limited_impls {
5959
use super::*;
6060
use std::ops::{Add, Div, Mul, Neg, Sub};
6161

62+
#[cfg(feature = "gil-refs")]
6263
impl PyComplex {
6364
/// Returns `|self|`.
6465
pub fn abs(&self) -> c_double {
@@ -94,6 +95,7 @@ mod not_limited_impls {
9495
}
9596
}
9697

98+
#[cfg(feature = "gil-refs")]
9799
impl<'py> $trait for &'py PyComplex {
98100
type Output = &'py PyComplex;
99101
fn $fn(self, other: &'py PyComplex) -> &'py PyComplex {
@@ -136,6 +138,7 @@ mod not_limited_impls {
136138
bin_ops!(Mul, mul, *, ffi::_Py_c_prod);
137139
bin_ops!(Div, div, /, ffi::_Py_c_quot);
138140

141+
#[cfg(feature = "gil-refs")]
139142
impl<'py> Neg for &'py PyComplex {
140143
type Output = &'py PyComplex;
141144
fn neg(self) -> &'py PyComplex {

src/types/dict.rs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::instance::{Borrowed, Bound};
66
use crate::py_result_ext::PyResultExt;
77
use crate::types::any::PyAnyMethods;
88
use crate::types::{PyAny, PyList};
9-
use crate::{ffi, PyNativeType, Python, ToPyObject};
9+
#[cfg(feature = "gil-refs")]
10+
use crate::PyNativeType;
11+
use crate::{ffi, Python, ToPyObject};
1012

1113
/// Represents a Python `dict`.
1214
#[repr(transparent)]
@@ -56,34 +58,11 @@ pyobject_native_type_core!(
5658
);
5759

5860
impl PyDict {
59-
/// Deprecated form of [`new_bound`][PyDict::new_bound].
60-
#[cfg(feature = "gil-refs")]
61-
#[deprecated(
62-
since = "0.21.0",
63-
note = "`PyDict::new` will be replaced by `PyDict::new_bound` in a future PyO3 version"
64-
)]
65-
#[inline]
66-
pub fn new(py: Python<'_>) -> &PyDict {
67-
Self::new_bound(py).into_gil_ref()
68-
}
69-
7061
/// Creates a new empty dictionary.
7162
pub fn new_bound(py: Python<'_>) -> Bound<'_, PyDict> {
7263
unsafe { ffi::PyDict_New().assume_owned(py).downcast_into_unchecked() }
7364
}
7465

75-
/// Deprecated form of [`from_sequence_bound`][PyDict::from_sequence_bound].
76-
#[cfg(feature = "gil-refs")]
77-
#[deprecated(
78-
since = "0.21.0",
79-
note = "`PyDict::from_sequence` will be replaced by `PyDict::from_sequence_bound` in a future PyO3 version"
80-
)]
81-
#[inline]
82-
#[cfg(not(any(PyPy, GraalPy)))]
83-
pub fn from_sequence(seq: &PyAny) -> PyResult<&PyDict> {
84-
Self::from_sequence_bound(&seq.as_borrowed()).map(Bound::into_gil_ref)
85-
}
86-
8766
/// Creates a new dictionary from the sequence given.
8867
///
8968
/// The sequence must consist of `(PyObject, PyObject)`. This is
@@ -100,6 +79,30 @@ impl PyDict {
10079
})?;
10180
Ok(dict)
10281
}
82+
}
83+
84+
#[cfg(feature = "gil-refs")]
85+
impl PyDict {
86+
/// Deprecated form of [`new_bound`][PyDict::new_bound].
87+
#[deprecated(
88+
since = "0.21.0",
89+
note = "`PyDict::new` will be replaced by `PyDict::new_bound` in a future PyO3 version"
90+
)]
91+
#[inline]
92+
pub fn new(py: Python<'_>) -> &PyDict {
93+
Self::new_bound(py).into_gil_ref()
94+
}
95+
96+
/// Deprecated form of [`from_sequence_bound`][PyDict::from_sequence_bound].
97+
#[deprecated(
98+
since = "0.21.0",
99+
note = "`PyDict::from_sequence` will be replaced by `PyDict::from_sequence_bound` in a future PyO3 version"
100+
)]
101+
#[inline]
102+
#[cfg(not(any(PyPy, GraalPy)))]
103+
pub fn from_sequence(seq: &PyAny) -> PyResult<&PyDict> {
104+
Self::from_sequence_bound(&seq.as_borrowed()).map(Bound::into_gil_ref)
105+
}
103106

104107
/// Returns a new dictionary that contains the same key-value pairs as self.
105108
///
@@ -550,8 +553,10 @@ fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
550553
}
551554

552555
/// PyO3 implementation of an iterator for a Python `dict` object.
556+
#[cfg(feature = "gil-refs")]
553557
pub struct PyDictIterator<'py>(BoundDictIterator<'py>);
554558

559+
#[cfg(feature = "gil-refs")]
555560
impl<'py> Iterator for PyDictIterator<'py> {
556561
type Item = (&'py PyAny, &'py PyAny);
557562

@@ -567,12 +572,14 @@ impl<'py> Iterator for PyDictIterator<'py> {
567572
}
568573
}
569574

575+
#[cfg(feature = "gil-refs")]
570576
impl<'py> ExactSizeIterator for PyDictIterator<'py> {
571577
fn len(&self) -> usize {
572578
self.0.len()
573579
}
574580
}
575581

582+
#[cfg(feature = "gil-refs")]
576583
impl<'a> IntoIterator for &'a PyDict {
577584
type Item = (&'a PyAny, &'a PyAny);
578585
type IntoIter = PyDictIterator<'a>;

src/types/frozenset.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::types::PyIterator;
2+
#[cfg(feature = "gil-refs")]
3+
use crate::PyNativeType;
24
use crate::{
35
err::{self, PyErr, PyResult},
46
ffi,
57
ffi_ptr_ext::FfiPtrExt,
68
py_result_ext::PyResultExt,
79
types::any::PyAnyMethods,
8-
Bound, PyAny, PyNativeType, PyObject, Python, ToPyObject,
10+
Bound, PyAny, PyObject, Python, ToPyObject,
911
};
1012
use std::ptr;
1113

@@ -73,10 +75,32 @@ pyobject_native_type_core!(
7375
#checkfunction=ffi::PyFrozenSet_Check
7476
);
7577

78+
impl PyFrozenSet {
79+
/// Creates a new frozenset.
80+
///
81+
/// May panic when running out of memory.
82+
#[inline]
83+
pub fn new_bound<'a, 'p, T: ToPyObject + 'a>(
84+
py: Python<'p>,
85+
elements: impl IntoIterator<Item = &'a T>,
86+
) -> PyResult<Bound<'p, PyFrozenSet>> {
87+
new_from_iter(py, elements)
88+
}
89+
90+
/// Creates a new empty frozen set
91+
pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
92+
unsafe {
93+
ffi::PyFrozenSet_New(ptr::null_mut())
94+
.assume_owned_or_err(py)
95+
.downcast_into_unchecked()
96+
}
97+
}
98+
}
99+
100+
#[cfg(feature = "gil-refs")]
76101
impl PyFrozenSet {
77102
/// Deprecated form of [`PyFrozenSet::new_bound`].
78103
#[inline]
79-
#[cfg(feature = "gil-refs")]
80104
#[deprecated(
81105
since = "0.21.0",
82106
note = "`PyFrozenSet::new` will be replaced by `PyFrozenSet::new_bound` in a future PyO3 version"
@@ -88,19 +112,7 @@ impl PyFrozenSet {
88112
Self::new_bound(py, elements).map(Bound::into_gil_ref)
89113
}
90114

91-
/// Creates a new frozenset.
92-
///
93-
/// May panic when running out of memory.
94-
#[inline]
95-
pub fn new_bound<'a, 'p, T: ToPyObject + 'a>(
96-
py: Python<'p>,
97-
elements: impl IntoIterator<Item = &'a T>,
98-
) -> PyResult<Bound<'p, PyFrozenSet>> {
99-
new_from_iter(py, elements)
100-
}
101-
102115
/// Deprecated form of [`PyFrozenSet::empty_bound`].
103-
#[cfg(feature = "gil-refs")]
104116
#[deprecated(
105117
since = "0.21.0",
106118
note = "`PyFrozenSet::empty` will be replaced by `PyFrozenSet::empty_bound` in a future PyO3 version"
@@ -109,15 +121,6 @@ impl PyFrozenSet {
109121
Self::empty_bound(py).map(Bound::into_gil_ref)
110122
}
111123

112-
/// Creates a new empty frozen set
113-
pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
114-
unsafe {
115-
ffi::PyFrozenSet_New(ptr::null_mut())
116-
.assume_owned_or_err(py)
117-
.downcast_into_unchecked()
118-
}
119-
}
120-
121124
/// Return the number of items in the set.
122125
/// This is equivalent to len(p) on a set.
123126
#[inline]
@@ -201,8 +204,10 @@ impl<'py> PyFrozenSetMethods<'py> for Bound<'py, PyFrozenSet> {
201204
}
202205

203206
/// PyO3 implementation of an iterator for a Python `frozenset` object.
207+
#[cfg(feature = "gil-refs")]
204208
pub struct PyFrozenSetIterator<'py>(BoundFrozenSetIterator<'py>);
205209

210+
#[cfg(feature = "gil-refs")]
206211
impl<'py> Iterator for PyFrozenSetIterator<'py> {
207212
type Item = &'py super::PyAny;
208213

@@ -217,13 +222,15 @@ impl<'py> Iterator for PyFrozenSetIterator<'py> {
217222
}
218223
}
219224

225+
#[cfg(feature = "gil-refs")]
220226
impl ExactSizeIterator for PyFrozenSetIterator<'_> {
221227
#[inline]
222228
fn len(&self) -> usize {
223229
self.0.len()
224230
}
225231
}
226232

233+
#[cfg(feature = "gil-refs")]
227234
impl<'py> IntoIterator for &'py PyFrozenSet {
228235
type Item = &'py PyAny;
229236
type IntoIter = PyFrozenSetIterator<'py>;

0 commit comments

Comments
 (0)