Skip to content

Commit dc9a415

Browse files
authored
feature gate deprecated APIs for Py (#4142)
1 parent 261d27d commit dc9a415

File tree

8 files changed

+88
-91
lines changed

8 files changed

+88
-91
lines changed

guide/src/memory.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,11 @@ What happens to the memory when the last `Py<PyAny>` is dropped and its
177177
reference count reaches zero? It depends whether or not we are holding the GIL.
178178

179179
```rust
180+
# #![allow(unused_imports)]
180181
# use pyo3::prelude::*;
181182
# use pyo3::types::PyString;
182183
# fn main() -> PyResult<()> {
184+
# #[cfg(feature = "gil-refs")]
183185
Python::with_gil(|py| -> PyResult<()> {
184186
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
185187
let hello: Py<PyString> = py.eval("\"Hello World!\"", None, None)?.extract()?;
@@ -203,9 +205,12 @@ This example wasn't very interesting. We could have just used a GIL-bound
203205
we are *not* holding the GIL?
204206

205207
```rust
208+
# #![allow(unused_imports)]
206209
# use pyo3::prelude::*;
207210
# use pyo3::types::PyString;
208211
# fn main() -> PyResult<()> {
212+
# #[cfg(feature = "gil-refs")]
213+
# {
209214
let hello: Py<PyString> = Python::with_gil(|py| {
210215
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
211216
py.eval("\"Hello World!\"", None, None)?.extract()
@@ -224,6 +229,7 @@ Python::with_gil(|py|
224229
// Memory for `hello` is released here.
225230
# ()
226231
);
232+
# }
227233
# Ok(())
228234
# }
229235
```
@@ -237,9 +243,12 @@ We can avoid the delay in releasing memory if we are careful to drop the
237243
`Py<Any>` while the GIL is held.
238244

239245
```rust
246+
# #![allow(unused_imports)]
240247
# use pyo3::prelude::*;
241248
# use pyo3::types::PyString;
242249
# fn main() -> PyResult<()> {
250+
# #[cfg(feature = "gil-refs")]
251+
# {
243252
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
244253
let hello: Py<PyString> =
245254
Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?;
@@ -252,6 +261,7 @@ Python::with_gil(|py| {
252261
}
253262
drop(hello); // Memory released here.
254263
});
264+
# }
255265
# Ok(())
256266
# }
257267
```
@@ -263,9 +273,12 @@ that rather than being released immediately, the memory will not be released
263273
until the GIL is dropped.
264274

265275
```rust
276+
# #![allow(unused_imports)]
266277
# use pyo3::prelude::*;
267278
# use pyo3::types::PyString;
268279
# fn main() -> PyResult<()> {
280+
# #[cfg(feature = "gil-refs")]
281+
# {
269282
#[allow(deprecated)] // py.eval() is part of the GIL Refs API
270283
let hello: Py<PyString> =
271284
Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?;
@@ -280,6 +293,7 @@ Python::with_gil(|py| {
280293
// Do more stuff...
281294
// Memory released here at end of `with_gil()` closure.
282295
});
296+
# }
283297
# Ok(())
284298
# }
285299
```

guide/src/python-from-rust/function-calls.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ fn main() -> PyResult<()> {
107107

108108
<div class="warning">
109109

110-
During PyO3's [migration from "GIL Refs" to the `Bound<T>` smart pointer](../migration.md#migrating-from-the-gil-refs-api-to-boundt), [`Py<T>::call`]({{#PYO3_DOCS_URL}}/pyo3/struct.Py.html#method.call) is temporarily named `call_bound` (and `call_method` is temporarily `call_method_bound`).
110+
During PyO3's [migration from "GIL Refs" to the `Bound<T>` smart pointer](../migration.md#migrating-from-the-gil-refs-api-to-boundt), `Py<T>::call` is temporarily named [`Py<T>::call_bound`]({{#PYO3_DOCS_URL}}/pyo3/struct.Py.html#method.call_bound) (and `call_method` is temporarily `call_method_bound`).
111111

112112
(This temporary naming is only the case for the `Py<T>` smart pointer. The methods on the `&PyAny` GIL Ref such as `call` have not been given replacements, and the methods on the `Bound<PyAny>` smart pointer such as [`Bound<PyAny>::call`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call) already use follow the newest API conventions.)
113113

guide/src/types.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,10 @@ let _: Py<PyList> = obj.extract()?;
353353
For a `&PyAny` object reference `any` where the underlying object is a `#[pyclass]`:
354354

355355
```rust
356+
# #![allow(unused_imports)]
356357
# use pyo3::prelude::*;
357358
# #[pyclass] #[derive(Clone)] struct MyClass { }
359+
# #[cfg(feature = "gil-refs")]
358360
# Python::with_gil(|py| -> PyResult<()> {
359361
#[allow(deprecated)] // into_ref is part of the deprecated GIL Refs API
360362
let obj: &PyAny = Py::new(py, MyClass {})?.into_ref(py);

src/err/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl<'a> PyDowncastError<'a> {
6464

6565
/// Compatibility API to convert the Bound variant `DowncastError` into the
6666
/// gil-ref variant
67+
#[cfg(feature = "gil-refs")]
6768
pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self {
6869
#[allow(deprecated)]
6970
let from = unsafe { from.py().from_borrowed_ptr(from.as_ptr()) };

src/instance.rs

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::err::{self, PyDowncastError, PyErr, PyResult};
1+
use crate::err::{self, PyErr, PyResult};
22
use crate::impl_::pycell::PyClassObject;
33
use crate::pycell::{PyBorrowError, PyBorrowMutError};
44
use crate::pyclass::boolean_struct::{False, True};
@@ -721,12 +721,12 @@ impl<T> IntoPy<PyObject> for Borrowed<'_, '_, T> {
721721
///
722722
/// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it.
723723
/// Instead, call one of its methods to access the inner object:
724-
/// - [`Py::as_ref`], to borrow a GIL-bound reference to the contained object.
724+
/// - [`Py::bind`] or [`Py::into_bound`], to borrow a GIL-bound reference to the contained object.
725725
/// - [`Py::borrow`], [`Py::try_borrow`], [`Py::borrow_mut`], or [`Py::try_borrow_mut`],
726726
/// to get a (mutable) reference to a contained pyclass, using a scheme similar to std's [`RefCell`].
727-
/// See the [`PyCell` guide entry](https://pyo3.rs/latest/class.html#pycell-and-interior-mutability)
727+
/// See the [guide entry](https://pyo3.rs/latest/class.html#bound-and-interior-mutability)
728728
/// for more information.
729-
/// - You can call methods directly on `Py` with [`Py::call`], [`Py::call_method`] and friends.
729+
/// - You can call methods directly on `Py` with [`Py::call_bound`], [`Py::call_method_bound`] and friends.
730730
/// These require passing in the [`Python<'py>`](crate::Python) token but are otherwise similar to the corresponding
731731
/// methods on [`PyAny`].
732732
///
@@ -991,12 +991,10 @@ where
991991
/// assert!(my_class_cell.try_borrow().is_ok());
992992
/// });
993993
/// ```
994-
#[cfg_attr(
995-
not(feature = "gil-refs"),
996-
deprecated(
997-
since = "0.21.0",
998-
note = "use `obj.bind(py)` instead of `obj.as_ref(py)`"
999-
)
994+
#[cfg(feature = "gil-refs")]
995+
#[deprecated(
996+
since = "0.21.0",
997+
note = "use `obj.bind(py)` instead of `obj.as_ref(py)`"
1000998
)]
1001999
pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py T::AsRefTarget {
10021000
let any = self.as_ptr() as *const PyAny;
@@ -1046,12 +1044,10 @@ where
10461044
/// obj.into_ref(py)
10471045
/// }
10481046
/// ```
1049-
#[cfg_attr(
1050-
not(feature = "gil-refs"),
1051-
deprecated(
1052-
since = "0.21.0",
1053-
note = "use `obj.into_bound(py)` instead of `obj.into_ref(py)`"
1054-
)
1047+
#[cfg(feature = "gil-refs")]
1048+
#[deprecated(
1049+
since = "0.21.0",
1050+
note = "use `obj.into_bound(py)` instead of `obj.into_ref(py)`"
10551051
)]
10561052
pub fn into_ref(self, py: Python<'_>) -> &T::AsRefTarget {
10571053
#[allow(deprecated)]
@@ -1464,12 +1460,10 @@ impl<T> Py<T> {
14641460
}
14651461

14661462
/// Deprecated form of [`call_bound`][Py::call_bound].
1467-
#[cfg_attr(
1468-
not(feature = "gil-refs"),
1469-
deprecated(
1470-
since = "0.21.0",
1471-
note = "`call` will be replaced by `call_bound` in a future PyO3 version"
1472-
)
1463+
#[cfg(feature = "gil-refs")]
1464+
#[deprecated(
1465+
since = "0.21.0",
1466+
note = "`call` will be replaced by `call_bound` in a future PyO3 version"
14731467
)]
14741468
#[inline]
14751469
pub fn call<A>(&self, py: Python<'_>, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
@@ -1506,12 +1500,10 @@ impl<T> Py<T> {
15061500
}
15071501

15081502
/// Deprecated form of [`call_method_bound`][Py::call_method_bound].
1509-
#[cfg_attr(
1510-
not(feature = "gil-refs"),
1511-
deprecated(
1512-
since = "0.21.0",
1513-
note = "`call_method` will be replaced by `call_method_bound` in a future PyO3 version"
1514-
)
1503+
#[cfg(feature = "gil-refs")]
1504+
#[deprecated(
1505+
since = "0.21.0",
1506+
note = "`call_method` will be replaced by `call_method_bound` in a future PyO3 version"
15151507
)]
15161508
#[inline]
15171509
pub fn call_method<N, A>(
@@ -1779,6 +1771,7 @@ impl<T> std::convert::From<Bound<'_, T>> for Py<T> {
17791771
}
17801772

17811773
// `&PyCell<T>` can be converted to `Py<T>`
1774+
#[cfg(feature = "gil-refs")]
17821775
#[allow(deprecated)]
17831776
impl<T> std::convert::From<&crate::PyCell<T>> for Py<T>
17841777
where
@@ -1844,10 +1837,7 @@ where
18441837
{
18451838
/// Extracts `Self` from the source `PyObject`.
18461839
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
1847-
// TODO update MSRV past 1.59 and use .cloned() to make
1848-
// clippy happy
1849-
#[allow(clippy::map_clone)]
1850-
ob.downcast().map(Clone::clone).map_err(Into::into)
1840+
ob.downcast().cloned().map_err(Into::into)
18511841
}
18521842
}
18531843

@@ -1888,21 +1878,22 @@ pub type PyObject = Py<PyAny>;
18881878

18891879
impl PyObject {
18901880
/// Deprecated form of [`PyObject::downcast_bound`]
1891-
#[cfg_attr(
1892-
not(feature = "gil-refs"),
1893-
deprecated(
1894-
since = "0.21.0",
1895-
note = "`PyObject::downcast` will be replaced by `PyObject::downcast_bound` in a future PyO3 version"
1896-
)
1881+
#[cfg(feature = "gil-refs")]
1882+
#[deprecated(
1883+
since = "0.21.0",
1884+
note = "`PyObject::downcast` will be replaced by `PyObject::downcast_bound` in a future PyO3 version"
18971885
)]
18981886
#[inline]
1899-
pub fn downcast<'py, T>(&'py self, py: Python<'py>) -> Result<&'py T, PyDowncastError<'py>>
1887+
pub fn downcast<'py, T>(
1888+
&'py self,
1889+
py: Python<'py>,
1890+
) -> Result<&'py T, crate::err::PyDowncastError<'py>>
19001891
where
19011892
T: PyTypeCheck<AsRefTarget = T>,
19021893
{
19031894
self.downcast_bound::<T>(py)
19041895
.map(Bound::as_gil_ref)
1905-
.map_err(PyDowncastError::from_downcast_err)
1896+
.map_err(crate::err::PyDowncastError::from_downcast_err)
19061897
}
19071898
/// Downcast this `PyObject` to a concrete Python type or pyclass.
19081899
///
@@ -1970,12 +1961,10 @@ impl PyObject {
19701961
/// # Safety
19711962
///
19721963
/// Callers must ensure that the type is valid or risk type confusion.
1973-
#[cfg_attr(
1974-
not(feature = "gil-refs"),
1975-
deprecated(
1976-
since = "0.21.0",
1977-
note = "`PyObject::downcast_unchecked` will be replaced by `PyObject::downcast_bound_unchecked` in a future PyO3 version"
1978-
)
1964+
#[cfg(feature = "gil-refs")]
1965+
#[deprecated(
1966+
since = "0.21.0",
1967+
note = "`PyObject::downcast_unchecked` will be replaced by `PyObject::downcast_bound_unchecked` in a future PyO3 version"
19791968
)]
19801969
#[inline]
19811970
pub unsafe fn downcast_unchecked<'py, T>(&'py self, py: Python<'py>) -> &T
@@ -1997,35 +1986,31 @@ impl PyObject {
19971986
}
19981987

19991988
#[cfg(test)]
2000-
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
20011989
mod tests {
20021990
use super::{Bound, Py, PyObject};
20031991
use crate::types::any::PyAnyMethods;
20041992
use crate::types::{dict::IntoPyDict, PyDict, PyString};
20051993
use crate::types::{PyCapsule, PyStringMethods};
2006-
use crate::{ffi, Borrowed, PyAny, PyNativeType, PyResult, Python, ToPyObject};
1994+
use crate::{ffi, Borrowed, PyAny, PyResult, Python, ToPyObject};
20071995

20081996
#[test]
20091997
fn test_call() {
20101998
Python::with_gil(|py| {
2011-
let obj = py.get_type::<PyDict>().to_object(py);
1999+
let obj = py.get_type_bound::<PyDict>().to_object(py);
20122000

2013-
let assert_repr = |obj: &PyAny, expected: &str| {
2014-
assert_eq!(obj.repr().unwrap().to_str().unwrap(), expected);
2001+
let assert_repr = |obj: &Bound<'_, PyAny>, expected: &str| {
2002+
assert_eq!(obj.repr().unwrap().to_cow().unwrap(), expected);
20152003
};
20162004

2017-
assert_repr(obj.call0(py).unwrap().as_ref(py), "{}");
2018-
assert_repr(obj.call1(py, ()).unwrap().as_ref(py), "{}");
2019-
assert_repr(obj.call(py, (), None).unwrap().as_ref(py), "{}");
2005+
assert_repr(obj.call0(py).unwrap().bind(py), "{}");
2006+
assert_repr(obj.call1(py, ()).unwrap().bind(py), "{}");
2007+
assert_repr(obj.call_bound(py, (), None).unwrap().bind(py), "{}");
20202008

2021-
assert_repr(
2022-
obj.call1(py, ((('x', 1),),)).unwrap().as_ref(py),
2023-
"{'x': 1}",
2024-
);
2009+
assert_repr(obj.call1(py, ((('x', 1),),)).unwrap().bind(py), "{'x': 1}");
20252010
assert_repr(
20262011
obj.call_bound(py, (), Some(&[('x', 1)].into_py_dict_bound(py)))
20272012
.unwrap()
2028-
.as_ref(py),
2013+
.bind(py),
20292014
"{'x': 1}",
20302015
);
20312016
})
@@ -2037,7 +2022,7 @@ mod tests {
20372022
let obj: PyObject = PyDict::new_bound(py).into();
20382023
assert!(obj.call_method0(py, "asdf").is_err());
20392024
assert!(obj
2040-
.call_method(py, "nonexistent_method", (1,), None)
2025+
.call_method_bound(py, "nonexistent_method", (1,), None)
20412026
.is_err());
20422027
assert!(obj.call_method0(py, "nonexistent_method").is_err());
20432028
assert!(obj.call_method1(py, "nonexistent_method", (1,)).is_err());
@@ -2083,7 +2068,7 @@ a = A()
20832068

20842069
assert!(instance
20852070
.getattr(py, "foo")?
2086-
.as_ref(py)
2071+
.bind(py)
20872072
.eq(PyString::new_bound(py, "bar"))?);
20882073

20892074
instance.getattr(py, "foo")?;
@@ -2109,15 +2094,15 @@ a = A()
21092094

21102095
instance.getattr(py, foo).unwrap_err();
21112096
instance.setattr(py, foo, bar)?;
2112-
assert!(instance.getattr(py, foo)?.as_ref(py).eq(bar)?);
2097+
assert!(instance.getattr(py, foo)?.bind(py).eq(bar)?);
21132098
Ok(())
21142099
})
21152100
}
21162101

21172102
#[test]
21182103
fn invalid_attr() -> PyResult<()> {
21192104
Python::with_gil(|py| {
2120-
let instance: Py<PyAny> = py.eval("object()", None, None)?.into();
2105+
let instance: Py<PyAny> = py.eval_bound("object()", None, None)?.into();
21212106

21222107
instance.getattr(py, "foo").unwrap_err();
21232108

@@ -2130,7 +2115,7 @@ a = A()
21302115
#[test]
21312116
fn test_py2_from_py_object() {
21322117
Python::with_gil(|py| {
2133-
let instance: &PyAny = py.eval("object()", None, None).unwrap();
2118+
let instance = py.eval_bound("object()", None, None).unwrap();
21342119
let ptr = instance.as_ptr();
21352120
let instance: Bound<'_, PyAny> = instance.extract().unwrap();
21362121
assert_eq!(instance.as_ptr(), ptr);
@@ -2141,7 +2126,7 @@ a = A()
21412126
fn test_py2_into_py_object() {
21422127
Python::with_gil(|py| {
21432128
let instance = py
2144-
.eval("object()", None, None)
2129+
.eval_bound("object()", None, None)
21452130
.unwrap()
21462131
.as_borrowed()
21472132
.to_owned();

0 commit comments

Comments
 (0)