Skip to content

Commit 21c0248

Browse files
authored
feature gate APIs using into_gil_ref (Part 2) (#4166)
1 parent 7beb64a commit 21c0248

34 files changed

+93
-165
lines changed

guide/src/conversions/traits.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ use pyo3::prelude::*;
265265

266266
#[derive(FromPyObject)]
267267
# #[derive(Debug)]
268-
enum RustyEnum<'a> {
268+
enum RustyEnum<'py> {
269269
Int(usize), // input is a positive int
270270
String(String), // input is a string
271271
IntTuple(usize, usize), // input is a 2-tuple with positive ints
@@ -284,7 +284,7 @@ enum RustyEnum<'a> {
284284
b: usize,
285285
},
286286
#[pyo3(transparent)]
287-
CatchAll(&'a PyAny), // This extraction never fails
287+
CatchAll(Bound<'py, PyAny>), // This extraction never fails
288288
}
289289
#
290290
# use pyo3::types::{PyBytes, PyString};
@@ -394,7 +394,7 @@ enum RustyEnum<'a> {
394394
# assert_eq!(
395395
# b"text",
396396
# match rust_thing {
397-
# RustyEnum::CatchAll(i) => i.downcast::<PyBytes>()?.as_bytes(),
397+
# RustyEnum::CatchAll(ref i) => i.downcast::<PyBytes>()?.as_bytes(),
398398
# other => unreachable!("Error extracting: {:?}", other),
399399
# }
400400
# );

guide/src/exception.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,5 @@ defines exceptions for several standard library modules.
128128
[`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html
129129
[`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html
130130
[`PyErr::from_value`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_value
131-
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance
132-
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance_of
131+
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance
132+
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance_of

guide/src/memory.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ held. (If PyO3 could not assume this, every PyO3 API would need to take a
3434
very simple and easy-to-understand programs like this:
3535

3636
```rust
37+
# #![allow(unused_imports)]
3738
# use pyo3::prelude::*;
3839
# use pyo3::types::PyString;
3940
# fn main() -> PyResult<()> {
41+
# #[cfg(feature = "gil-refs")]
4042
Python::with_gil(|py| -> PyResult<()> {
4143
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
4244
let hello = py
@@ -57,9 +59,11 @@ it owns are decreased, releasing them to the Python garbage collector. Most
5759
of the time we don't have to think about this, but consider the following:
5860

5961
```rust
62+
# #![allow(unused_imports)]
6063
# use pyo3::prelude::*;
6164
# use pyo3::types::PyString;
6265
# fn main() -> PyResult<()> {
66+
# #[cfg(feature = "gil-refs")]
6367
Python::with_gil(|py| -> PyResult<()> {
6468
for _ in 0..10 {
6569
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
@@ -96,9 +100,11 @@ In general we don't want unbounded memory growth during loops! One workaround
96100
is to acquire and release the GIL with each iteration of the loop.
97101

98102
```rust
103+
# #![allow(unused_imports)]
99104
# use pyo3::prelude::*;
100105
# use pyo3::types::PyString;
101106
# fn main() -> PyResult<()> {
107+
# #[cfg(feature = "gil-refs")]
102108
for _ in 0..10 {
103109
Python::with_gil(|py| -> PyResult<()> {
104110
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
@@ -118,9 +124,11 @@ times. Another workaround is to work with the `GILPool` object directly, but
118124
this is unsafe.
119125

120126
```rust
127+
# #![allow(unused_imports)]
121128
# use pyo3::prelude::*;
122129
# use pyo3::types::PyString;
123130
# fn main() -> PyResult<()> {
131+
# #[cfg(feature = "gil-refs")]
124132
Python::with_gil(|py| -> PyResult<()> {
125133
for _ in 0..10 {
126134
#[allow(deprecated)] // `new_pool` is not needed in code not using the GIL Refs API

guide/src/migration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pyo3 = { version = "0.21", features = ["gil-refs"] }
5454

5555
The `PyTryFrom` trait has aged poorly, its `try_from` method now conflicts with `TryFrom::try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`.
5656

57-
To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods [`PyAny::downcast`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast) and [`PyAny::downcast_exact`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast_exact) no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively.
57+
To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods `PyAny::downcast` and `PyAny::downcast_exact` no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively.
5858

5959
To migrate, switch all type casts to use `obj.downcast()` instead of `try_from(obj)` (and similar for `downcast_exact`).
6060

guide/src/types.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,10 @@ let _: &mut MyClass = &mut *py_ref_mut;
467467
`PyCell<T>` was also accessed like a Python-native type.
468468

469469
```rust
470+
#![allow(unused_imports)]
470471
# use pyo3::prelude::*;
471472
# #[pyclass] struct MyClass { }
473+
# #[cfg(feature = "gil-refs")]
472474
# Python::with_gil(|py| -> PyResult<()> {
473475
#[allow(deprecated)] // &PyCell is part of the deprecate GIL Refs API
474476
let cell: &PyCell<MyClass> = PyCell::new(py, MyClass {})?;

pytests/src/pyclasses.rs

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63,30 +63,6 @@ impl AssertingBaseClass {
6363
}
6464
}
6565

66-
#[allow(deprecated)]
67-
mod deprecated {
68-
use super::*;
69-
70-
#[pyclass(subclass)]
71-
#[derive(Clone, Debug)]
72-
pub struct AssertingBaseClassGilRef;
73-
74-
#[pymethods]
75-
impl AssertingBaseClassGilRef {
76-
#[new]
77-
#[classmethod]
78-
fn new(cls: &PyType, expected_type: &PyType) -> PyResult<Self> {
79-
if !cls.is(expected_type) {
80-
return Err(PyValueError::new_err(format!(
81-
"{:?} != {:?}",
82-
cls, expected_type
83-
)));
84-
}
85-
Ok(Self)
86-
}
87-
}
88-
}
89-
9066
#[pyclass]
9167
struct ClassWithoutConstructor;
9268

@@ -95,7 +71,7 @@ pub fn pyclasses(m: &Bound<'_, PyModule>) -> PyResult<()> {
9571
m.add_class::<EmptyClass>()?;
9672
m.add_class::<PyClassIter>()?;
9773
m.add_class::<AssertingBaseClass>()?;
98-
m.add_class::<deprecated::AssertingBaseClassGilRef>()?;
9974
m.add_class::<ClassWithoutConstructor>()?;
75+
10076
Ok(())
10177
}

pytests/src/sequence.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn array_to_array_i32(arr: [i32; 3]) -> [i32; 3] {
1212
}
1313

1414
#[pyfunction]
15-
fn vec_to_vec_pystring(vec: Vec<&PyString>) -> Vec<&PyString> {
15+
fn vec_to_vec_pystring(vec: Vec<Bound<'_, PyString>>) -> Vec<Bound<'_, PyString>> {
1616
vec
1717
}
1818

pytests/tests/test_pyclasses.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,6 @@ def test_new_classmethod():
6565
_ = AssertingSubClass(expected_type=str)
6666

6767

68-
def test_new_classmethod_gil_ref():
69-
class AssertingSubClass(pyclasses.AssertingBaseClassGilRef):
70-
pass
71-
72-
# The `AssertingBaseClass` constructor errors if it is not passed the
73-
# relevant subclass.
74-
_ = AssertingSubClass(expected_type=AssertingSubClass)
75-
with pytest.raises(ValueError):
76-
_ = AssertingSubClass(expected_type=str)
77-
78-
7968
class ClassWithoutConstructorPy:
8069
def __new__(cls):
8170
raise TypeError("No constructor defined")

src/conversion.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ where
345345
}
346346

347347
#[allow(deprecated)]
348+
#[cfg(feature = "gil-refs")]
348349
impl<'py, T> FromPyObject<'py> for &'py crate::PyCell<T>
349350
where
350351
T: PyClass,

src/conversions/chrono.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ impl FromPyObject<'_> for FixedOffset {
347347
/// does not supports microseconds.
348348
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<FixedOffset> {
349349
#[cfg(not(Py_LIMITED_API))]
350-
let ob: &PyTzInfo = ob.extract()?;
350+
let ob = ob.downcast::<PyTzInfo>()?;
351351
#[cfg(Py_LIMITED_API)]
352352
check_type(ob, &DatetimeTypes::get(ob.py()).tzinfo, "PyTzInfo")?;
353353

src/conversions/std/osstr.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl<'a> IntoPy<PyObject> for &'a OsString {
147147

148148
#[cfg(test)]
149149
mod tests {
150+
use crate::types::{PyAnyMethods, PyStringMethods};
150151
use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject};
151152
use std::fmt::Debug;
152153
use std::{
@@ -179,7 +180,7 @@ mod tests {
179180
Python::with_gil(|py| {
180181
fn test_roundtrip<T: ToPyObject + AsRef<OsStr> + Debug>(py: Python<'_>, obj: T) {
181182
let pyobject = obj.to_object(py);
182-
let pystring: &PyString = pyobject.extract(py).unwrap();
183+
let pystring = pyobject.downcast_bound::<PyString>(py).unwrap();
183184
assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
184185
let roundtripped_obj: OsString = pystring.extract().unwrap();
185186
assert_eq!(obj.as_ref(), roundtripped_obj.as_os_str());
@@ -200,7 +201,7 @@ mod tests {
200201
obj: T,
201202
) {
202203
let pyobject = obj.clone().into_py(py);
203-
let pystring: &PyString = pyobject.extract(py).unwrap();
204+
let pystring = pyobject.downcast_bound::<PyString>(py).unwrap();
204205
assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
205206
let roundtripped_obj: OsString = pystring.extract().unwrap();
206207
assert!(obj.as_ref() == roundtripped_obj.as_os_str());

src/conversions/std/path.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl<'a> IntoPy<PyObject> for &'a PathBuf {
6464

6565
#[cfg(test)]
6666
mod tests {
67+
use crate::types::{PyAnyMethods, PyStringMethods};
6768
use crate::{types::PyString, IntoPy, PyObject, Python, ToPyObject};
6869
use std::borrow::Cow;
6970
use std::fmt::Debug;
@@ -95,7 +96,7 @@ mod tests {
9596
Python::with_gil(|py| {
9697
fn test_roundtrip<T: ToPyObject + AsRef<Path> + Debug>(py: Python<'_>, obj: T) {
9798
let pyobject = obj.to_object(py);
98-
let pystring: &PyString = pyobject.extract(py).unwrap();
99+
let pystring = pyobject.downcast_bound::<PyString>(py).unwrap();
99100
assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
100101
let roundtripped_obj: PathBuf = pystring.extract().unwrap();
101102
assert_eq!(obj.as_ref(), roundtripped_obj.as_path());
@@ -116,7 +117,7 @@ mod tests {
116117
obj: T,
117118
) {
118119
let pyobject = obj.clone().into_py(py);
119-
let pystring: &PyString = pyobject.extract(py).unwrap();
120+
let pystring = pyobject.downcast_bound::<PyString>(py).unwrap();
120121
assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
121122
let roundtripped_obj: PathBuf = pystring.extract().unwrap();
122123
assert_eq!(obj.as_ref(), roundtripped_obj.as_path());

src/derive_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ impl<'a> PyFunctionArguments<'a> {
1313
match self {
1414
PyFunctionArguments::Python(py) => (py, None),
1515
PyFunctionArguments::PyModule(module) => {
16-
let py = module.py();
16+
let py = crate::PyNativeType::py(module);
1717
(py, Some(module))
1818
}
1919
}

src/err/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ where
10001000
}
10011001

10021002
/// Convert `PyDowncastError` to Python `TypeError`.
1003+
#[cfg(feature = "gil-refs")]
10031004
impl<'a> std::convert::From<PyDowncastError<'a>> for PyErr {
10041005
fn from(err: PyDowncastError<'_>) -> PyErr {
10051006
let args = PyDowncastErrorArguments {

src/exceptions.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ macro_rules! impl_exception_boilerplate {
3232

3333
$crate::impl_exception_boilerplate_bound!($name);
3434

35+
#[cfg(feature = "gil-refs")]
3536
impl ::std::error::Error for $name {
3637
fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> {
3738
unsafe {
@@ -58,6 +59,7 @@ macro_rules! impl_exception_boilerplate_bound {
5859
///
5960
/// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
6061
#[inline]
62+
#[allow(dead_code)]
6163
pub fn new_err<A>(args: A) -> $crate::PyErr
6264
where
6365
A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
@@ -881,7 +883,9 @@ mod tests {
881883
use super::*;
882884
use crate::types::any::PyAnyMethods;
883885
use crate::types::{IntoPyDict, PyDict};
884-
use crate::{PyErr, PyNativeType};
886+
use crate::PyErr;
887+
#[cfg(feature = "gil-refs")]
888+
use crate::PyNativeType;
885889

886890
import_exception_bound!(socket, gaierror);
887891
import_exception_bound!(email.errors, MessageError);

src/impl_/deprecations.rs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,39 +29,27 @@ impl<T> GilRefs<T> {
2929
}
3030

3131
impl GilRefs<Python<'_>> {
32-
#[cfg_attr(
33-
not(feature = "gil-refs"),
34-
deprecated(since = "0.21.0", note = "use `wrap_pyfunction_bound!` instead")
35-
)]
32+
#[deprecated(since = "0.21.0", note = "use `wrap_pyfunction_bound!` instead")]
3633
pub fn is_python(&self) {}
3734
}
3835

3936
impl<T: IsGilRef> GilRefs<T> {
40-
#[cfg_attr(
41-
not(feature = "gil-refs"),
42-
deprecated(
43-
since = "0.21.0",
44-
note = "use `&Bound<'_, T>` instead for this function argument"
45-
)
37+
#[deprecated(
38+
since = "0.21.0",
39+
note = "use `&Bound<'_, T>` instead for this function argument"
4640
)]
4741
pub fn function_arg(&self) {}
48-
#[cfg_attr(
49-
not(feature = "gil-refs"),
50-
deprecated(
51-
since = "0.21.0",
52-
note = "use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor"
53-
)
42+
#[deprecated(
43+
since = "0.21.0",
44+
note = "use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor"
5445
)]
5546
pub fn from_py_with_arg(&self) {}
5647
}
5748

5849
impl<T: IsGilRef> OptionGilRefs<Option<T>> {
59-
#[cfg_attr(
60-
not(feature = "gil-refs"),
61-
deprecated(
62-
since = "0.21.0",
63-
note = "use `Option<&Bound<'_, T>>` instead for this function argument"
64-
)
50+
#[deprecated(
51+
since = "0.21.0",
52+
note = "use `Option<&Bound<'_, T>>` instead for this function argument"
6553
)]
6654
pub fn function_arg(&self) {}
6755
}

src/impl_/extract_argument.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -790,10 +790,8 @@ fn push_parameter_list(msg: &mut String, parameter_names: &[&str]) {
790790

791791
#[cfg(test)]
792792
mod tests {
793-
use crate::{
794-
types::{IntoPyDict, PyTuple},
795-
PyAny, Python,
796-
};
793+
use crate::types::{IntoPyDict, PyTuple};
794+
use crate::Python;
797795

798796
use super::{push_parameter_list, FunctionDescription, NoVarargs, NoVarkeywords};
799797

@@ -809,7 +807,7 @@ mod tests {
809807
};
810808

811809
Python::with_gil(|py| {
812-
let args = PyTuple::new_bound(py, Vec::<&PyAny>::new());
810+
let args = PyTuple::empty_bound(py);
813811
let kwargs = [("foo", 0u8)].into_py_dict_bound(py);
814812
let err = unsafe {
815813
function_description
@@ -840,7 +838,7 @@ mod tests {
840838
};
841839

842840
Python::with_gil(|py| {
843-
let args = PyTuple::new_bound(py, Vec::<&PyAny>::new());
841+
let args = PyTuple::empty_bound(py);
844842
let kwargs = [(1u8, 1u8)].into_py_dict_bound(py);
845843
let err = unsafe {
846844
function_description
@@ -871,7 +869,7 @@ mod tests {
871869
};
872870

873871
Python::with_gil(|py| {
874-
let args = PyTuple::new_bound(py, Vec::<&PyAny>::new());
872+
let args = PyTuple::empty_bound(py);
875873
let mut output = [None, None];
876874
let err = unsafe {
877875
function_description.extract_arguments_tuple_dict::<NoVarargs, NoVarkeywords>(

src/instance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ impl<T> Py<T> {
12831283
}
12841284

12851285
/// Returns whether `self` and `other` point to the same object. To compare
1286-
/// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq).
1286+
/// the equality of two objects (the `==` operator), use [`eq`](PyAnyMethods::eq).
12871287
///
12881288
/// This is equivalent to the Python expression `self is other`.
12891289
#[inline]
@@ -2142,7 +2142,7 @@ a = A()
21422142
fn test_is_ellipsis() {
21432143
Python::with_gil(|py| {
21442144
let v = py
2145-
.eval("...", None, None)
2145+
.eval_bound("...", None, None)
21462146
.map_err(|e| e.display(py))
21472147
.unwrap()
21482148
.to_object(py);

0 commit comments

Comments
 (0)