From 065215f2d11623d177c03d93feb571d5934261ea Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:37:29 +0200 Subject: [PATCH 1/2] fully remove the rest of the gil-refs --- Cargo.toml | 3 - guide/src/SUMMARY.md | 1 - guide/src/class.md | 5 - guide/src/memory.md | 309 ------------ guide/src/migration.md | 2 +- guide/src/types.md | 182 ------- noxfile.py | 12 +- pyo3-macros-backend/Cargo.toml | 1 - pyo3-macros-backend/src/module.rs | 10 +- pyo3-macros-backend/src/pyclass.rs | 13 - pyo3-macros/Cargo.toml | 1 - src/conversion.rs | 146 ------ src/conversions/num_complex.rs | 10 - src/exceptions.rs | 7 - src/gil.rs | 218 +-------- src/impl_/extract_argument.rs | 2 +- src/impl_/not_send.rs | 3 - src/impl_/pyclass.rs | 7 - src/impl_/pymethods.rs | 29 -- src/instance.rs | 281 ----------- src/lib.rs | 9 - src/macros.rs | 4 +- src/marker.rs | 336 ------------- src/prelude.rs | 5 - src/pycell.rs | 484 ------------------- src/pycell/impl_.rs | 19 - src/pyclass.rs | 14 - src/type_object.rs | 130 ----- src/types/any.rs | 737 ----------------------------- src/types/datetime.rs | 10 - src/types/dict.rs | 11 - src/types/mod.rs | 81 ---- tests/test_compile_error.rs | 12 +- tests/test_super.rs | 1 - 34 files changed, 15 insertions(+), 3080 deletions(-) delete mode 100644 guide/src/memory.md diff --git a/Cargo.toml b/Cargo.toml index 38673913049..5aac73fced4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,9 +103,6 @@ generate-import-lib = ["pyo3-ffi/generate-import-lib"] # Changes `Python::with_gil` to automatically initialize the Python interpreter if needed. auto-initialize = [] -# Allows use of the deprecated "GIL Refs" APIs. -gil-refs = ["pyo3-macros/gil-refs"] - # Enables `Clone`ing references to Python objects `Py` which panics if the GIL is not held. py-clone = [] diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 4c22c26f587..af43897c014 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -27,7 +27,6 @@ - [Parallelism](parallelism.md) - [Debugging](debugging.md) - [Features reference](features.md) -- [Memory management](memory.md) - [Performance](performance.md) - [Advanced topics](advanced.md) - [Building and distribution](building-and-distribution.md) diff --git a/guide/src/class.md b/guide/src/class.md index 94f3f333581..3a6f3f9f36c 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -1367,11 +1367,6 @@ struct MyClass { impl pyo3::types::DerefToPyAny for MyClass {} -# #[allow(deprecated)] -# #[cfg(feature = "gil-refs")] -unsafe impl pyo3::type_object::HasPyGilRef for MyClass { - type AsRefTarget = pyo3::PyCell; -} unsafe impl pyo3::type_object::PyTypeInfo for MyClass { const NAME: &'static str = "MyClass"; const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None; diff --git a/guide/src/memory.md b/guide/src/memory.md deleted file mode 100644 index 41454b03500..00000000000 --- a/guide/src/memory.md +++ /dev/null @@ -1,309 +0,0 @@ -# Memory management - -
- -⚠️ Warning: API update in progress 🛠️ - -PyO3 0.21 has introduced a significant new API, termed the "Bound" API after the new smart pointer `Bound`. - -This section on memory management is heavily weighted towards the now-deprecated "GIL Refs" API, which suffered from the drawbacks detailed here as well as CPU overheads. - -See [the smart pointer types](./types.md#pyo3s-smart-pointers) for description on the new, simplified, memory model of the Bound API, which is built as a thin wrapper on Python reference counting. -
- -Rust and Python have very different notions of memory management. Rust has -a strict memory model with concepts of ownership, borrowing, and lifetimes, -where memory is freed at predictable points in program execution. Python has -a looser memory model in which variables are reference-counted with shared, -mutable state by default. A global interpreter lock (GIL) is needed to prevent -race conditions, and a garbage collector is needed to break reference cycles. -Memory in Python is freed eventually by the garbage collector, but not usually -in a predictable way. - -PyO3 bridges the Rust and Python memory models with two different strategies for -accessing memory allocated on Python's heap from inside Rust. These are -GIL Refs such as `&'py PyAny`, and GIL-independent `Py` smart pointers. - -## GIL-bound memory - -PyO3's GIL Refs such as `&'py PyAny` make PyO3 more ergonomic to -use by ensuring that their lifetime can never be longer than the duration the -Python GIL is held. This means that most of PyO3's API can assume the GIL is -held. (If PyO3 could not assume this, every PyO3 API would need to take a -`Python` GIL token to prove that the GIL is held.) This allows us to write -very simple and easy-to-understand programs like this: - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -Python::with_gil(|py| -> PyResult<()> { - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - let hello = py - .eval("\"Hello World!\"", None, None)? - .downcast::()?; - println!("Python says: {}", hello); - Ok(()) -})?; -# Ok(()) -# } -``` - -Internally, calling `Python::with_gil()` creates a `GILPool` which owns the -memory pointed to by the reference. In the example above, the lifetime of the -reference `hello` is bound to the `GILPool`. When the `with_gil()` closure ends -the `GILPool` is also dropped and the Python reference counts of the variables -it owns are decreased, releasing them to the Python garbage collector. Most -of the time we don't have to think about this, but consider the following: - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -Python::with_gil(|py| -> PyResult<()> { - for _ in 0..10 { - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - let hello = py - .eval("\"Hello World!\"", None, None)? - .downcast::()?; - println!("Python says: {}", hello); - } - // There are 10 copies of `hello` on Python's heap here. - Ok(()) -})?; -# Ok(()) -# } -``` - -We might assume that the `hello` variable's memory is freed at the end of each -loop iteration, but in fact we create 10 copies of `hello` on Python's heap. -This may seem surprising at first, but it is completely consistent with Rust's -memory model. The `hello` variable is dropped at the end of each loop, but it -is only a reference to the memory owned by the `GILPool`, and its lifetime is -bound to the `GILPool`, not the for loop. The `GILPool` isn't dropped until -the end of the `with_gil()` closure, at which point the 10 copies of `hello` -are finally released to the Python garbage collector. - -
- -⚠️ Warning: `GILPool` is no longer the preferred way to manage memory with PyO3 🛠️ - -PyO3 0.21 has introduced a new API known as the Bound API, which doesn't have the same surprising results. Instead, each `Bound` smart pointer releases the Python reference immediately on drop. See [the smart pointer types](./types.md#pyo3s-smart-pointers) for more details. -
- - -In general we don't want unbounded memory growth during loops! One workaround -is to acquire and release the GIL with each iteration of the loop. - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -for _ in 0..10 { - Python::with_gil(|py| -> PyResult<()> { - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - let hello = py - .eval("\"Hello World!\"", None, None)? - .downcast::()?; - println!("Python says: {}", hello); - Ok(()) - })?; // only one copy of `hello` at a time -} -# Ok(()) -# } -``` - -It might not be practical or performant to acquire and release the GIL so many -times. Another workaround is to work with the `GILPool` object directly, but -this is unsafe. - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -Python::with_gil(|py| -> PyResult<()> { - for _ in 0..10 { - #[allow(deprecated)] // `new_pool` is not needed in code not using the GIL Refs API - let pool = unsafe { py.new_pool() }; - let py = pool.python(); - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - let hello = py - .eval("\"Hello World!\"", None, None)? - .downcast::()?; - println!("Python says: {}", hello); - } - Ok(()) -})?; -# Ok(()) -# } -``` - -The unsafe method `Python::new_pool` allows you to create a nested `GILPool` -from which you can retrieve a new `py: Python` GIL token. Variables created -with this new GIL token are bound to the nested `GILPool` and will be released -when the nested `GILPool` is dropped. Here, the nested `GILPool` is dropped -at the end of each loop iteration, before the `with_gil()` closure ends. - -When doing this, you must be very careful to ensure that once the `GILPool` is -dropped you do not retain access to any owned references created after the -`GILPool` was created. Read the documentation for `Python::new_pool()` -for more information on safety. - -This memory management can also be applicable when writing extension modules. -`#[pyfunction]` and `#[pymethods]` will create a `GILPool` which lasts the entire -function call, releasing objects when the function returns. Most functions only create -a few objects, meaning this doesn't have a significant impact. Occasionally functions -with long complex loops may need to use `Python::new_pool` as shown above. - -
- -⚠️ Warning: `GILPool` is no longer the preferred way to manage memory with PyO3 🛠️ - -PyO3 0.21 has introduced a new API known as the Bound API, which doesn't have the same surprising results. Instead, each `Bound` smart pointer releases the Python reference immediately on drop. See [the smart pointer types](./types.md#pyo3s-smart-pointers) for more details. -
- -## GIL-independent memory - -Sometimes we need a reference to memory on Python's heap that can outlive the -GIL. Python's `Py` is analogous to `Arc`, but for variables whose -memory is allocated on Python's heap. Cloning a `Py` increases its -internal reference count just like cloning `Arc`. The smart pointer can -outlive the "GIL is held" period in which it was created. It isn't magic, -though. We need to reacquire the GIL to access the memory pointed to by the -`Py`. - -What happens to the memory when the last `Py` is dropped and its -reference count reaches zero? It depends whether or not we are holding the GIL. - -```rust -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -Python::with_gil(|py| -> PyResult<()> { - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - let hello: Py = py.eval("\"Hello World!\"", None, None)?.extract()?; - #[allow(deprecated)] // as_ref is part of the GIL Refs API - { - println!("Python says: {}", hello.as_ref(py)); - } - Ok(()) -})?; -# Ok(()) -# } -``` - -At the end of the `Python::with_gil()` closure `hello` is dropped, and then the -GIL is dropped. Since `hello` is dropped while the GIL is still held by the -current thread, its memory is released to the Python garbage collector -immediately. - -This example wasn't very interesting. We could have just used a GIL-bound -`&PyString` reference. What happens when the last `Py` is dropped while -we are *not* holding the GIL? - -```rust -# #![allow(unused_imports, dead_code)] -# #[cfg(not(pyo3_disable_reference_pool))] { -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -# { -let hello: Py = Python::with_gil(|py| { - #[allow(deprecated)] // py.eval() is part of the GIL Refs API - py.eval("\"Hello World!\"", None, None)?.extract() -})?; -// Do some stuff... -// Now sometime later in the program we want to access `hello`. -Python::with_gil(|py| { - #[allow(deprecated)] // as_ref is part of the deprecated "GIL Refs" API. - let hello = hello.as_ref(py); - println!("Python says: {}", hello); -}); -// Now we're done with `hello`. -drop(hello); // Memory *not* released here. -// Sometime later we need the GIL again for something... -Python::with_gil(|py| - // Memory for `hello` is released here. -# () -); -# } -# Ok(()) -# } -# } -``` - -When `hello` is dropped *nothing* happens to the pointed-to memory on Python's -heap because nothing _can_ happen if we're not holding the GIL. Fortunately, -the memory isn't leaked. If the `pyo3_disable_reference_pool` conditional compilation flag -is not enabled, PyO3 keeps track of the memory internally and will release it -the next time we acquire the GIL. - -We can avoid the delay in releasing memory if we are careful to drop the -`Py` while the GIL is held. - -```rust -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -# { -#[allow(deprecated)] // py.eval() is part of the GIL Refs API -let hello: Py = - Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?; -// Do some stuff... -// Now sometime later in the program: -Python::with_gil(|py| { - #[allow(deprecated)] // as_ref is part of the GIL Refs API - { - println!("Python says: {}", hello.as_ref(py)); - } - drop(hello); // Memory released here. -}); -# } -# Ok(()) -# } -``` - -We could also have used `Py::into_ref()`, which consumes `self`, instead of -`Py::as_ref()`. But note that in addition to being slower than `as_ref()`, -`into_ref()` binds the memory to the lifetime of the `GILPool`, which means -that rather than being released immediately, the memory will not be released -until the GIL is dropped. - -```rust -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyString; -# fn main() -> PyResult<()> { -# #[cfg(feature = "gil-refs")] -# { -#[allow(deprecated)] // py.eval() is part of the GIL Refs API -let hello: Py = - Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?; -// Do some stuff... -// Now sometime later in the program: -Python::with_gil(|py| { - #[allow(deprecated)] // into_ref is part of the GIL Refs API - { - println!("Python says: {}", hello.into_ref(py)); - } - // Memory not released yet. - // Do more stuff... - // Memory released here at end of `with_gil()` closure. -}); -# } -# Ok(()) -# } -``` diff --git a/guide/src/migration.md b/guide/src/migration.md index f45ba567291..113560ecaea 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -487,7 +487,7 @@ A key thing to note here is because extracting to these types now ties them to t Before: -```rust +```rust,ignore # #[cfg(feature = "gil-refs")] { # use pyo3::prelude::*; # use pyo3::types::{PyList, PyType}; diff --git a/guide/src/types.md b/guide/src/types.md index 564937124ba..ee46b97ebcd 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -6,8 +6,6 @@ The first set of types are the [smart pointers][smart-pointers] which all Python The second set of types are types which fill in the generic parameter `T` of the smart pointers. The most common is `PyAny`, which represents any Python object (similar to Python's `typing.Any`). There are also concrete types for many Python built-in types, such as `PyList`, `PyDict`, and `PyTuple`. User defined `#[pyclass]` types also fit this category. The [second section below](#concrete-python-types) expands on how to use these types. -Before PyO3 0.21, PyO3's main API to interact with Python objects was a deprecated API known as the "GIL Refs" API, containing reference types such as `&PyAny`, `&PyList`, and `&PyCell` for user-defined `#[pyclass]` types. The [third section below](#the-gil-refs-api) details this deprecated API. - ## PyO3's smart pointers PyO3's API offers three generic smart pointers: `Py`, `Bound<'py, T>` and `Borrowed<'a, 'py, T>`. For each of these the type parameter `T` will be filled by a [concrete Python type](#concrete-python-types). For example, a Python list object can be represented by `Py`, `Bound<'py, PyList>`, and `Borrowed<'a, 'py, PyList>`. @@ -310,186 +308,6 @@ assert_eq!((x, y, z), (1, 2, 3)); To avoid copying data, [`#[pyclass]`][pyclass] types can directly reference Rust data stored within the Python objects without needing to `.extract()`. See the [corresponding documentation in the class section of the guide](./class.md#bound-and-interior-mutability) for more detail. -## The GIL Refs API - -The GIL Refs API was PyO3's primary API prior to PyO3 0.21. The main difference was that instead of the `Bound<'py, PyAny>` smart pointer, the "GIL Reference" `&'py PyAny` was used. (This was similar for other Python types.) - -As of PyO3 0.21, the GIL Refs API is deprecated. See the [migration guide](./migration.md#from-020-to-021) for details on how to upgrade. - -The following sections note some historical detail about the GIL Refs API. - -### [`PyAny`][PyAny] - -**Represented:** a Python object of unspecified type. In the GIL Refs API, this was only accessed as the GIL Ref `&'py PyAny`. - -**Used:** `&'py PyAny` was used to refer to some Python object when the GIL lifetime was available for the whole duration access was needed. For example, intermediate values and arguments to `pyfunction`s or `pymethod`s implemented in Rust where any type is allowed. - -**Conversions:** - -For a `&PyAny` object reference `any` where the underlying object is a Python-native type such as -a list: - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyList; -# #[cfg(feature = "gil-refs")] -# Python::with_gil(|py| -> PyResult<()> { -#[allow(deprecated)] // PyList::empty is part of the deprecated "GIL Refs" API. -let obj: &PyAny = PyList::empty(py); - -// To &PyList with PyAny::downcast -let _: &PyList = obj.downcast()?; - -// To Py (aka PyObject) with .into() -let _: Py = obj.into(); - -// To Py with PyAny::extract -let _: Py = obj.extract()?; -# Ok(()) -# }).unwrap(); -``` - -For a `&PyAny` object reference `any` where the underlying object is a `#[pyclass]`: - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# #[pyclass] #[derive(Clone)] struct MyClass { } -# #[cfg(feature = "gil-refs")] -# Python::with_gil(|py| -> PyResult<()> { -#[allow(deprecated)] // into_ref is part of the deprecated GIL Refs API -let obj: &PyAny = Py::new(py, MyClass {})?.into_ref(py); - -// To &PyCell with PyAny::downcast -#[allow(deprecated)] // &PyCell is part of the deprecated GIL Refs API -let _: &PyCell = obj.downcast()?; - -// To Py (aka PyObject) with .into() -let _: Py = obj.into(); - -// To Py with PyAny::extract -let _: Py = obj.extract()?; - -// To MyClass with PyAny::extract, if MyClass: Clone -let _: MyClass = obj.extract()?; - -// To PyRef<'_, MyClass> or PyRefMut<'_, MyClass> with PyAny::extract -let _: PyRef<'_, MyClass> = obj.extract()?; -let _: PyRefMut<'_, MyClass> = obj.extract()?; -# Ok(()) -# }).unwrap(); -``` - -### `PyTuple`, `PyDict`, and many more - -**Represented:** a native Python object of known type. In the GIL Refs API, they were only accessed as the GIL Refs `&'py PyTuple`, `&'py PyDict`. - -**Used:** `&'py PyTuple` and similar were used to operate with native Python types while holding the GIL. Like `PyAny`, this is the most convenient form to use for function arguments and intermediate values. - -These GIL Refs implement `Deref`, so they all expose the same methods which can be found on `PyAny`. - -To see all Python types exposed by `PyO3` consult the [`pyo3::types`][pyo3::types] module. - -**Conversions:** - -```rust,ignore -# #![allow(unused_imports)] -# use pyo3::prelude::*; -# use pyo3::types::PyList; -# #[cfg(feature = "gil-refs")] -# Python::with_gil(|py| -> PyResult<()> { -#[allow(deprecated)] // PyList::empty is part of the deprecated "GIL Refs" API. -let list = PyList::empty(py); - -// Use methods from PyAny on all Python types with Deref implementation -let _ = list.repr()?; - -// To &PyAny automatically with Deref implementation -let _: &PyAny = list; - -// To &PyAny explicitly with .as_ref() -#[allow(deprecated)] // as_ref is part of the deprecated "GIL Refs" API. -let _: &PyAny = list.as_ref(); - -// To Py with .into() or Py::from() -let _: Py = list.into(); - -// To PyObject with .into() or .to_object(py) -let _: PyObject = list.into(); -# Ok(()) -# }).unwrap(); -``` - -### `Py` and `PyObject` - -**Represented:** a GIL-independent reference to a Python object. This can be a Python native type -(like `PyTuple`), or a `pyclass` type implemented in Rust. The most commonly-used variant, -`Py`, is also known as `PyObject`. - -**Used:** Whenever you want to carry around references to a Python object without caring about a -GIL lifetime. For example, storing Python object references in a Rust struct that outlives the -Python-Rust FFI boundary, or returning objects from functions implemented in Rust back to Python. - -Can be cloned using Python reference counts with `.clone()`. - -### `PyCell` - -**Represented:** a reference to a Rust object (instance of `PyClass`) wrapped in a Python object. The cell part is an analog to stdlib's [`RefCell`][RefCell] to allow access to `&mut` references. - -**Used:** for accessing pure-Rust API of the instance (members and functions taking `&SomeType` or `&mut SomeType`) while maintaining the aliasing rules of Rust references. - -Like PyO3's Python native types, the GIL Ref `&PyCell` implements `Deref`, so it also exposed all of the methods on `PyAny`. - -**Conversions:** - -`PyCell` was used to access `&T` and `&mut T` via `PyRef` and `PyRefMut` respectively. - -```rust -#![allow(unused_imports)] -# use pyo3::prelude::*; -# #[pyclass] struct MyClass { } -# #[cfg(feature = "gil-refs")] -# Python::with_gil(|py| -> PyResult<()> { -#[allow(deprecated)] // &PyCell is part of the deprecated GIL Refs API -let cell: &PyCell = PyCell::new(py, MyClass {})?; - -// To PyRef with .borrow() or .try_borrow() -let py_ref: PyRef<'_, MyClass> = cell.try_borrow()?; -let _: &MyClass = &*py_ref; -# drop(py_ref); - -// To PyRefMut with .borrow_mut() or .try_borrow_mut() -let mut py_ref_mut: PyRefMut<'_, MyClass> = cell.try_borrow_mut()?; -let _: &mut MyClass = &mut *py_ref_mut; -# Ok(()) -# }).unwrap(); -``` - -`PyCell` was also accessed like a Python-native type. - -```rust -#![allow(unused_imports)] -# use pyo3::prelude::*; -# #[pyclass] struct MyClass { } -# #[cfg(feature = "gil-refs")] -# Python::with_gil(|py| -> PyResult<()> { -#[allow(deprecated)] // &PyCell is part of the deprecate GIL Refs API -let cell: &PyCell = PyCell::new(py, MyClass {})?; - -// Use methods from PyAny on PyCell with Deref implementation -let _ = cell.repr()?; - -// To &PyAny automatically with Deref implementation -let _: &PyAny = cell; - -// To &PyAny explicitly with .as_ref() -#[allow(deprecated)] // as_ref is part of the deprecated "GIL Refs" API. -let _: &PyAny = cell.as_ref(); -# Ok(()) -# }).unwrap(); -``` - [Bound]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html [`Bound::unbind`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html#method.unbind [Py]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html diff --git a/noxfile.py b/noxfile.py index 5d8123c7eb4..0267531cb7d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -51,7 +51,6 @@ def test_rust(session: nox.Session): _run_cargo_test(session, features="abi3") if "skip-full" not in session.posargs: _run_cargo_test(session, features="full") - _run_cargo_test(session, features="full gil-refs") _run_cargo_test(session, features="abi3 full") @@ -673,7 +672,6 @@ def check_feature_powerset(session: nox.Session): EXCLUDED_FROM_FULL = { "nightly", - "gil-refs", "extension-module", "full", "default", @@ -715,7 +713,7 @@ def check_feature_powerset(session: nox.Session): session.error("no experimental features exist; please simplify the noxfile") features_to_skip = [ - *(EXCLUDED_FROM_FULL - {"gil-refs"}), + *(EXCLUDED_FROM_FULL), *abi3_version_features, ] @@ -793,8 +791,8 @@ def _get_feature_sets() -> Tuple[Tuple[str, ...], ...]: "--no-default-features", "--features=abi3", ), - ("--features=full gil-refs multiple-pymethods",), - ("--features=abi3 full gil-refs multiple-pymethods",), + ("--features=full multiple-pymethods",), + ("--features=abi3 full multiple-pymethods",), ) else: return ( @@ -803,8 +801,8 @@ def _get_feature_sets() -> Tuple[Tuple[str, ...], ...]: "--no-default-features", "--features=abi3", ), - ("--features=full gil-refs",), - ("--features=abi3 full gil-refs",), + ("--features=full",), + ("--features=abi3 full",), ) diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index b3775ef8518..337d19f5653 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -32,4 +32,3 @@ workspace = true [features] experimental-async = [] -gil-refs = [] diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index ea982f686b8..e0025fda6dd 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -462,11 +462,6 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn let Ctx { pyo3_path, .. } = ctx; let mut stmts: Vec = Vec::new(); - #[cfg(feature = "gil-refs")] - let imports = quote!(use #pyo3_path::{PyNativeType, types::PyModuleMethods};); - #[cfg(not(feature = "gil-refs"))] - let imports = quote!(use #pyo3_path::types::PyModuleMethods;); - for mut stmt in func.block.stmts.drain(..) { if let syn::Stmt::Item(Item::Fn(func)) = &mut stmt { if let Some(pyfn_args) = get_pyfn_attr(&mut func.attrs)? { @@ -476,9 +471,8 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn let statements: Vec = syn::parse_quote! { #wrapped_function { - #[allow(unknown_lints, unused_imports, redundant_imports)] - #imports - #module_name.as_borrowed().add_function(#pyo3_path::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?; + use #pyo3_path::types::PyModuleMethods; + #module_name.add_function(#pyo3_path::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?; } }; stmts.extend(statements); diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 65fffc1655f..e9e5af6a8ac 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -1743,20 +1743,7 @@ fn impl_pytypeinfo(cls: &syn::Ident, attr: &PyClassArgs, ctx: &Ctx) -> TokenStre quote! { ::core::option::Option::None } }; - #[cfg(feature = "gil-refs")] - let has_py_gil_ref = quote! { - #[allow(deprecated)] - unsafe impl #pyo3_path::type_object::HasPyGilRef for #cls { - type AsRefTarget = #pyo3_path::PyCell; - } - }; - - #[cfg(not(feature = "gil-refs"))] - let has_py_gil_ref = TokenStream::new(); - quote! { - #has_py_gil_ref - unsafe impl #pyo3_path::type_object::PyTypeInfo for #cls { const NAME: &'static str = #cls_name; const MODULE: ::std::option::Option<&'static str> = #module; diff --git a/pyo3-macros/Cargo.toml b/pyo3-macros/Cargo.toml index 42d6d801c89..c86afd8e2d2 100644 --- a/pyo3-macros/Cargo.toml +++ b/pyo3-macros/Cargo.toml @@ -16,7 +16,6 @@ proc-macro = true [features] multiple-pymethods = [] experimental-async = ["pyo3-macros-backend/experimental-async"] -gil-refs = ["pyo3-macros-backend/gil-refs"] [dependencies] proc-macro2 = { version = "1.0.60", default-features = false } diff --git a/src/conversion.rs b/src/conversion.rs index 79f5f7f13cf..bbebf89c15c 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -6,11 +6,6 @@ use crate::pyclass::boolean_struct::False; use crate::types::any::PyAnyMethods; use crate::types::PyTuple; use crate::{ffi, Borrowed, Bound, Py, PyAny, PyClass, PyObject, PyRef, PyRefMut, Python}; -#[cfg(feature = "gil-refs")] -use { - crate::{err, gil, PyNativeType}, - std::ptr::NonNull, -}; /// Returns a borrowed pointer to a Python object. /// @@ -220,15 +215,6 @@ pub trait IntoPy: Sized { /// infinite recursion, implementors must implement at least one of these methods. The recommendation /// is to implement `extract_bound` and leave `extract` as the default implementation. pub trait FromPyObject<'py>: Sized { - /// Extracts `Self` from the source GIL Ref `obj`. - /// - /// Implementors are encouraged to implement `extract_bound` and leave this method as the - /// default implementation, which will forward calls to `extract_bound`. - #[cfg(feature = "gil-refs")] - fn extract(ob: &'py PyAny) -> PyResult { - Self::extract_bound(&ob.as_borrowed()) - } - /// Extracts `Self` from the bound smart pointer `obj`. /// /// Implementors are encouraged to implement this method and leave `extract` defaulted, as @@ -384,138 +370,6 @@ impl IntoPy> for () { } } -/// Raw level conversion between `*mut ffi::PyObject` and PyO3 types. -/// -/// # Safety -/// -/// See safety notes on individual functions. -#[cfg(feature = "gil-refs")] -#[deprecated(since = "0.21.0")] -pub unsafe trait FromPyPointer<'p>: Sized { - /// Convert from an arbitrary `PyObject`. - /// - /// # Safety - /// - /// Implementations must ensure the object does not get freed during `'p` - /// and ensure that `ptr` is of the correct type. - /// Note that it must be safe to decrement the reference count of `ptr`. - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr_or_opt(py, ptr)` or `Bound::from_owned_ptr_or_opt(py, ptr)` instead" - )] - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>; - /// Convert from an arbitrary `PyObject` or panic. - /// - /// # Safety - /// - /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr(py, ptr)` or `Bound::from_owned_ptr(py, ptr)` instead" - )] - unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { - #[allow(deprecated)] - Self::from_owned_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py)) - } - /// Convert from an arbitrary `PyObject` or panic. - /// - /// # Safety - /// - /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr(py, ptr)` or `Bound::from_owned_ptr(py, ptr)` instead" - )] - unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { - #[allow(deprecated)] - Self::from_owned_ptr_or_panic(py, ptr) - } - /// Convert from an arbitrary `PyObject`. - /// - /// # Safety - /// - /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr_or_err(py, ptr)` or `Bound::from_owned_ptr_or_err(py, ptr)` instead" - )] - unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> { - #[allow(deprecated)] - Self::from_owned_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py)) - } - /// Convert from an arbitrary borrowed `PyObject`. - /// - /// # Safety - /// - /// Implementations must ensure the object does not get freed during `'p` and avoid type confusion. - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr_or_opt(py, ptr)` or `Bound::from_borrowed_ptr_or_opt(py, ptr)` instead" - )] - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) - -> Option<&'p Self>; - /// Convert from an arbitrary borrowed `PyObject`. - /// - /// # Safety - /// - /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr(py, ptr)` or `Bound::from_borrowed_ptr(py, ptr)` instead" - )] - unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { - #[allow(deprecated)] - Self::from_borrowed_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py)) - } - /// Convert from an arbitrary borrowed `PyObject`. - /// - /// # Safety - /// - /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr(py, ptr)` or `Bound::from_borrowed_ptr(py, ptr)` instead" - )] - unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { - #[allow(deprecated)] - Self::from_borrowed_ptr_or_panic(py, ptr) - } - /// Convert from an arbitrary borrowed `PyObject`. - /// - /// # Safety - /// - /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr_or_err(py, ptr)` or `Bound::from_borrowed_ptr_or_err(py, ptr)` instead" - )] - unsafe fn from_borrowed_ptr_or_err( - py: Python<'p>, - ptr: *mut ffi::PyObject, - ) -> PyResult<&'p Self> { - #[allow(deprecated)] - Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py)) - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -unsafe impl<'p, T> FromPyPointer<'p> for T -where - T: 'p + crate::PyNativeType, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { - gil::register_owned(py, NonNull::new(ptr)?); - Some(&*(ptr as *mut Self)) - } - unsafe fn from_borrowed_ptr_or_opt( - _py: Python<'p>, - ptr: *mut ffi::PyObject, - ) -> Option<&'p Self> { - NonNull::new(ptr as *mut Self).map(|p| &*p.as_ptr()) - } -} - /// ```rust,compile_fail /// use pyo3::prelude::*; /// diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index 12f208aa8d1..172d1efd505 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -103,16 +103,6 @@ use num_complex::Complex; use std::os::raw::c_double; impl PyComplex { - /// Deprecated form of [`PyComplex::from_complex_bound`] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`PyComplex::from_complex` will be replaced by `PyComplex::from_complex_bound` in a future PyO3 version" - )] - pub fn from_complex>(py: Python<'_>, complex: Complex) -> &PyComplex { - Self::from_complex_bound(py, complex).into_gil_ref() - } - /// Creates a new Python `PyComplex` object from `num_complex`'s [`Complex`]. pub fn from_complex_bound>( py: Python<'_>, diff --git a/src/exceptions.rs b/src/exceptions.rs index bca706c43d4..e9be7a96606 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -118,13 +118,6 @@ macro_rules! import_exception_bound { $crate::impl_exception_boilerplate_bound!($name); - // FIXME remove this: was necessary while `PyTypeInfo` requires `HasPyGilRef`, - // should change in 0.22. - #[cfg(feature = "gil-refs")] - unsafe impl $crate::type_object::HasPyGilRef for $name { - type AsRefTarget = $crate::PyAny; - } - $crate::pyobject_native_type_info!( $name, $name::type_object_raw, diff --git a/src/gil.rs b/src/gil.rs index ec20fc64c34..644fbe6e00a 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -1,17 +1,11 @@ //! Interaction with Python's global interpreter lock -#[cfg(feature = "gil-refs")] -use crate::impl_::not_send::{NotSend, NOT_SEND}; #[cfg(pyo3_disable_reference_pool)] use crate::impl_::panic::PanicTrap; use crate::{ffi, Python}; #[cfg(not(pyo3_disable_reference_pool))] use once_cell::sync::Lazy; use std::cell::Cell; -#[cfg(all(feature = "gil-refs", debug_assertions))] -use std::cell::RefCell; -#[cfg(all(feature = "gil-refs", not(debug_assertions)))] -use std::cell::UnsafeCell; use std::{mem, ptr::NonNull, sync}; static START: sync::Once = sync::Once::new(); @@ -27,12 +21,6 @@ std::thread_local! { /// Additionally, we sometimes need to prevent safe access to the GIL, /// e.g. when implementing `__traverse__`, which is represented by a negative value. static GIL_COUNT: Cell = const { Cell::new(0) }; - - /// Temporarily hold objects that will be released when the GILPool drops. - #[cfg(all(feature = "gil-refs", debug_assertions))] - static OWNED_OBJECTS: RefCell = const { RefCell::new(Vec::new()) }; - #[cfg(all(feature = "gil-refs", not(debug_assertions)))] - static OWNED_OBJECTS: UnsafeCell = const { UnsafeCell::new(Vec::new()) }; } const GIL_LOCKED_DURING_TRAVERSE: isize = -1; @@ -152,12 +140,7 @@ pub(crate) enum GILGuard { /// Indicates the GIL was already held with this GILGuard was acquired. Assumed, /// Indicates that we actually acquired the GIL when this GILGuard was acquired - Ensured { - gstate: ffi::PyGILState_STATE, - #[cfg(feature = "gil-refs")] - #[allow(deprecated)] - pool: mem::ManuallyDrop, - }, + Ensured { gstate: ffi::PyGILState_STATE }, } impl GILGuard { @@ -224,19 +207,11 @@ impl GILGuard { let gstate = ffi::PyGILState_Ensure(); // acquire GIL increment_gil_count(); - #[cfg(feature = "gil-refs")] - #[allow(deprecated)] - let pool = mem::ManuallyDrop::new(GILPool::new()); - #[cfg(not(pyo3_disable_reference_pool))] if let Some(pool) = Lazy::get(&POOL) { pool.update_counts(Python::assume_gil_acquired()); } - GILGuard::Ensured { - gstate, - #[cfg(feature = "gil-refs")] - pool, - } + GILGuard::Ensured { gstate } } /// Acquires the `GILGuard` while assuming that the GIL is already held. @@ -262,14 +237,8 @@ impl Drop for GILGuard { fn drop(&mut self) { match self { GILGuard::Assumed => {} - GILGuard::Ensured { - gstate, - #[cfg(feature = "gil-refs")] - pool, - } => unsafe { + GILGuard::Ensured { gstate } => unsafe { // Drop the objects in the pool before attempting to release the thread state - #[cfg(feature = "gil-refs")] - mem::ManuallyDrop::drop(pool); ffi::PyGILState_Release(*gstate); }, } @@ -386,92 +355,6 @@ impl Drop for LockGIL { } } -/// A RAII pool which PyO3 uses to store owned Python references. -/// -/// See the [Memory Management] chapter of the guide for more information about how PyO3 uses -/// [`GILPool`] to manage memory. - -/// -/// [Memory Management]: https://pyo3.rs/main/memory.html#gil-bound-memory -#[cfg(feature = "gil-refs")] -#[deprecated( - since = "0.21.0", - note = "`GILPool` has no function if PyO3's deprecated GIL Refs API is not used" -)] -pub struct GILPool { - /// Initial length of owned objects and anys. - /// `Option` is used since TSL can be broken when `new` is called from `atexit`. - start: Option, - _not_send: NotSend, -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl GILPool { - /// Creates a new [`GILPool`]. This function should only ever be called with the GIL held. - /// - /// It is recommended not to use this API directly, but instead to use `Python::new_pool`, as - /// that guarantees the GIL is held. - /// - /// # Safety - /// - /// As well as requiring the GIL, see the safety notes on `Python::new_pool`. - #[inline] - pub unsafe fn new() -> GILPool { - // Update counts of PyObjects / Py that have been cloned or dropped since last acquisition - #[cfg(not(pyo3_disable_reference_pool))] - if let Some(pool) = Lazy::get(&POOL) { - pool.update_counts(Python::assume_gil_acquired()); - } - GILPool { - start: OWNED_OBJECTS - .try_with(|owned_objects| { - #[cfg(debug_assertions)] - let len = owned_objects.borrow().len(); - #[cfg(not(debug_assertions))] - // SAFETY: This is not re-entrant. - let len = unsafe { (*owned_objects.get()).len() }; - len - }) - .ok(), - _not_send: NOT_SEND, - } - } - - /// Gets the Python token associated with this [`GILPool`]. - #[inline] - pub fn python(&self) -> Python<'_> { - unsafe { Python::assume_gil_acquired() } - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl Drop for GILPool { - fn drop(&mut self) { - if let Some(start) = self.start { - let owned_objects = OWNED_OBJECTS.with(|owned_objects| { - #[cfg(debug_assertions)] - let mut owned_objects = owned_objects.borrow_mut(); - #[cfg(not(debug_assertions))] - // SAFETY: `OWNED_OBJECTS` is released before calling Py_DECREF, - // or Py_DECREF may call `GILPool::drop` recursively, resulting in invalid borrowing. - let owned_objects = unsafe { &mut *owned_objects.get() }; - if start < owned_objects.len() { - owned_objects.split_off(start) - } else { - Vec::new() - } - }); - for obj in owned_objects { - unsafe { - ffi::Py_DECREF(obj.as_ptr()); - } - } - } - } -} - /// Increments the reference count of a Python object if the GIL is held. If /// the GIL is not held, this function will panic. /// @@ -513,25 +396,6 @@ pub unsafe fn register_decref(obj: NonNull) { } } -/// Registers an owned object inside the GILPool, to be released when the GILPool drops. -/// -/// # Safety -/// The object must be an owned Python reference. -#[cfg(feature = "gil-refs")] -pub unsafe fn register_owned(_py: Python<'_>, obj: NonNull) { - debug_assert!(gil_is_acquired()); - // Ignores the error in case this function called from `atexit`. - let _ = OWNED_OBJECTS.try_with(|owned_objects| { - #[cfg(debug_assertions)] - owned_objects.borrow_mut().push(obj); - #[cfg(not(debug_assertions))] - // SAFETY: This is not re-entrant. - unsafe { - (*owned_objects.get()).push(obj); - } - }); -} - /// Increments pyo3's internal GIL count - to be called whenever GILPool or GILGuard is created. #[inline(always)] fn increment_gil_count() { @@ -562,13 +426,8 @@ fn decrement_gil_count() { #[cfg(test)] mod tests { use super::GIL_COUNT; - #[cfg(feature = "gil-refs")] - #[allow(deprecated)] - use super::OWNED_OBJECTS; #[cfg(not(pyo3_disable_reference_pool))] use super::{gil_is_acquired, POOL}; - #[cfg(feature = "gil-refs")] - use crate::{ffi, gil}; use crate::{gil::GILGuard, types::any::PyAnyMethods}; use crate::{PyObject, Python}; use std::ptr::NonNull; @@ -577,15 +436,6 @@ mod tests { py.eval_bound("object()", None, None).unwrap().unbind() } - #[cfg(feature = "gil-refs")] - fn owned_object_count() -> usize { - #[cfg(debug_assertions)] - let len = OWNED_OBJECTS.with(|owned_objects| owned_objects.borrow().len()); - #[cfg(not(debug_assertions))] - let len = OWNED_OBJECTS.with(|owned_objects| unsafe { (*owned_objects.get()).len() }); - len - } - #[cfg(not(pyo3_disable_reference_pool))] fn pool_dec_refs_does_not_contain(obj: &PyObject) -> bool { !POOL @@ -603,68 +453,6 @@ mod tests { .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) } - #[test] - #[cfg(feature = "gil-refs")] - #[allow(deprecated)] - fn test_owned() { - Python::with_gil(|py| { - let obj = get_object(py); - let obj_ptr = obj.as_ptr(); - // Ensure that obj does not get freed - let _ref = obj.clone_ref(py); - - unsafe { - { - let pool = py.new_pool(); - gil::register_owned(pool.python(), NonNull::new_unchecked(obj.into_ptr())); - - assert_eq!(owned_object_count(), 1); - assert_eq!(ffi::Py_REFCNT(obj_ptr), 2); - } - { - let _pool = py.new_pool(); - assert_eq!(owned_object_count(), 0); - assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); - } - } - }) - } - - #[test] - #[cfg(feature = "gil-refs")] - #[allow(deprecated)] - fn test_owned_nested() { - Python::with_gil(|py| { - let obj = get_object(py); - // Ensure that obj does not get freed - let _ref = obj.clone_ref(py); - let obj_ptr = obj.as_ptr(); - - unsafe { - { - let _pool = py.new_pool(); - assert_eq!(owned_object_count(), 0); - - gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr())); - - assert_eq!(owned_object_count(), 1); - assert_eq!(ffi::Py_REFCNT(obj_ptr), 2); - { - let _pool = py.new_pool(); - let obj = get_object(py); - gil::register_owned(py, NonNull::new_unchecked(obj.into_ptr())); - assert_eq!(owned_object_count(), 2); - } - assert_eq!(owned_object_count(), 1); - } - { - assert_eq!(owned_object_count(), 0); - assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); - } - } - }); - } - #[test] fn test_pyobject_drop_with_gil_decreases_refcnt() { Python::with_gil(|py| { diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index ef62ef26e7b..4945d977dd9 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -65,7 +65,7 @@ where } } -#[cfg(all(Py_LIMITED_API, not(any(feature = "gil-refs", Py_3_10))))] +#[cfg(all(Py_LIMITED_API, not(Py_3_10)))] impl<'a> PyFunctionArgument<'a, '_> for &'a str { type Holder = Option>; diff --git a/src/impl_/not_send.rs b/src/impl_/not_send.rs index 382e07a14ee..3304d79a1ba 100644 --- a/src/impl_/not_send.rs +++ b/src/impl_/not_send.rs @@ -5,6 +5,3 @@ use crate::Python; /// A marker type that makes the type !Send. /// Workaround for lack of !Send on stable (). pub(crate) struct NotSend(PhantomData<*mut Python<'static>>); - -#[cfg(feature = "gil-refs")] -pub(crate) const NOT_SEND: NotSend = NotSend(PhantomData); diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 391e96f5f43..7ad772f0496 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "gil-refs")] -use crate::PyNativeType; use crate::{ exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError}, ffi, @@ -177,11 +175,6 @@ pub trait PyClassImpl: Sized + 'static { /// The closest native ancestor. This is `PyAny` by default, and when you declare /// `#[pyclass(extends=PyDict)]`, it's `PyDict`. - #[cfg(feature = "gil-refs")] - type BaseNativeType: PyTypeInfo + PyNativeType; - /// The closest native ancestor. This is `PyAny` by default, and when you declare - /// `#[pyclass(extends=PyDict)]`, it's `PyDict`. - #[cfg(not(feature = "gil-refs"))] type BaseNativeType: PyTypeInfo; /// This handles following two situations: diff --git a/src/impl_/pymethods.rs b/src/impl_/pymethods.rs index 60b655e5647..77c6e6991ac 100644 --- a/src/impl_/pymethods.rs +++ b/src/impl_/pymethods.rs @@ -5,8 +5,6 @@ use crate::impl_::panic::PanicTrap; use crate::pycell::{PyBorrowError, PyBorrowMutError}; use crate::pyclass::boolean_struct::False; use crate::types::any::PyAnyMethods; -#[cfg(feature = "gil-refs")] -use crate::types::{PyModule, PyType}; use crate::{ ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyClass, PyClassInitializer, PyErr, PyObject, PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python, @@ -467,33 +465,6 @@ impl<'a, 'py> BoundRef<'a, 'py, PyAny> { } } -// GIL Ref implementations for &'a T ran into trouble with orphan rules, -// so explicit implementations are used instead for the two relevant types. -#[cfg(feature = "gil-refs")] -impl<'a> From> for &'a PyType { - #[inline] - fn from(bound: BoundRef<'a, 'a, PyType>) -> Self { - bound.0.as_gil_ref() - } -} - -#[cfg(feature = "gil-refs")] -impl<'a> From> for &'a PyModule { - #[inline] - fn from(bound: BoundRef<'a, 'a, PyModule>) -> Self { - bound.0.as_gil_ref() - } -} - -#[allow(deprecated)] -#[cfg(feature = "gil-refs")] -impl<'a, 'py, T: PyClass> From> for &'a crate::PyCell { - #[inline] - fn from(bound: BoundRef<'a, 'py, T>) -> Self { - bound.0.as_gil_ref() - } -} - impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'py, T> { type Error = PyBorrowError; #[inline] diff --git a/src/instance.rs b/src/instance.rs index 3b8e2529a6e..51c4d62b2d5 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,8 +3,6 @@ use crate::impl_::pycell::PyClassObject; use crate::internal_tricks::ptr_from_ref; use crate::pycell::{PyBorrowError, PyBorrowMutError}; use crate::pyclass::boolean_struct::{False, True}; -#[cfg(feature = "gil-refs")] -use crate::type_object::HasPyGilRef; use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods}; use crate::types::{DerefToPyAny, PyDict, PyString, PyTuple}; use crate::{ @@ -17,54 +15,6 @@ use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::NonNull; -/// Types that are built into the Python interpreter. -/// -/// PyO3 is designed in a way that all references to those types are bound -/// to the GIL, which is why you can get a token from all references of those -/// types. -/// -/// # Safety -/// -/// This trait must only be implemented for types which cannot be accessed without the GIL. -#[cfg(feature = "gil-refs")] -pub unsafe trait PyNativeType: Sized { - /// The form of this which is stored inside a `Py` smart pointer. - type AsRefSource: HasPyGilRef; - - /// Cast `&self` to a `Borrowed` smart pointer. - /// - /// `Borrowed` implements `Deref>`, so can also be used in locations - /// where `Bound` is expected. - /// - /// This is available as a migration tool to adjust code from the deprecated "GIL Refs" - /// API to the `Bound` smart pointer API. - #[inline] - fn as_borrowed(&self) -> Borrowed<'_, '_, Self::AsRefSource> { - // Safety: &'py Self is expected to be a Python pointer, - // so has the same layout as Borrowed<'py, 'py, T> - Borrowed( - unsafe { NonNull::new_unchecked(ptr_from_ref(self) as *mut _) }, - PhantomData, - self.py(), - ) - } - - /// Returns a GIL marker constrained to the lifetime of this type. - #[inline] - fn py(&self) -> Python<'_> { - unsafe { Python::assume_gil_acquired() } - } - /// Cast `&PyAny` to `&Self` without no type checking. - /// - /// # Safety - /// - /// `obj` must have the same layout as `*const ffi::PyObject` and must be - /// an instance of a type corresponding to `Self`. - unsafe fn unchecked_downcast(obj: &PyAny) -> &Self { - &*(obj.as_ptr() as *const Self) - } -} - /// A GIL-attached equivalent to [`Py`]. /// /// This type can be thought of as equivalent to the tuple `(Py, Python<'py>)`. By having the `'py` @@ -607,37 +557,6 @@ impl<'py, T> Bound<'py, T> { pub fn as_unbound(&self) -> &Py { &self.1 } - - /// Casts this `Bound` as the corresponding "GIL Ref" type. - /// - /// This is a helper to be used for migration from the deprecated "GIL Refs" API. - #[inline] - #[cfg(feature = "gil-refs")] - pub fn as_gil_ref(&'py self) -> &'py T::AsRefTarget - where - T: HasPyGilRef, - { - #[allow(deprecated)] - unsafe { - self.py().from_borrowed_ptr(self.as_ptr()) - } - } - - /// Casts this `Bound` as the corresponding "GIL Ref" type, registering the pointer on the - /// [release pool](Python::from_owned_ptr). - /// - /// This is a helper to be used for migration from the deprecated "GIL Refs" API. - #[inline] - #[cfg(feature = "gil-refs")] - pub fn into_gil_ref(self) -> &'py T::AsRefTarget - where - T: HasPyGilRef, - { - #[allow(deprecated)] - unsafe { - self.py().from_owned_ptr(self.into_ptr()) - } - } } unsafe impl AsPyPointer for Bound<'_, T> { @@ -1060,118 +979,6 @@ where } } -#[cfg(feature = "gil-refs")] -impl Py -where - T: HasPyGilRef, -{ - /// Borrows a GIL-bound reference to the contained `T`. - /// - /// By binding to the GIL lifetime, this allows the GIL-bound reference to not require - /// [`Python<'py>`](crate::Python) for any of its methods, which makes calling methods - /// on it more ergonomic. - /// - /// For native types, this reference is `&T`. For pyclasses, this is `&PyCell`. - /// - /// Note that the lifetime of the returned reference is the shortest of `&self` and - /// [`Python<'py>`](crate::Python). - /// Consider using [`Py::into_ref`] instead if this poses a problem. - /// - /// # Examples - /// - /// Get access to `&PyList` from `Py`: - /// - /// ```ignore - /// # use pyo3::prelude::*; - /// # use pyo3::types::PyList; - /// # - /// Python::with_gil(|py| { - /// let list: Py = PyList::empty_bound(py).into(); - /// # #[allow(deprecated)] - /// let list: &PyList = list.as_ref(py); - /// assert_eq!(list.len(), 0); - /// }); - /// ``` - /// - /// Get access to `&PyCell` from `Py`: - /// - /// ``` - /// # use pyo3::prelude::*; - /// # - /// #[pyclass] - /// struct MyClass {} - /// - /// Python::with_gil(|py| { - /// let my_class: Py = Py::new(py, MyClass {}).unwrap(); - /// # #[allow(deprecated)] - /// let my_class_cell: &PyCell = my_class.as_ref(py); - /// assert!(my_class_cell.try_borrow().is_ok()); - /// }); - /// ``` - #[deprecated( - since = "0.21.0", - note = "use `obj.bind(py)` instead of `obj.as_ref(py)`" - )] - pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py T::AsRefTarget { - let any = self.as_ptr() as *const PyAny; - unsafe { PyNativeType::unchecked_downcast(&*any) } - } - - /// Borrows a GIL-bound reference to the contained `T` independently of the lifetime of `T`. - /// - /// This method is similar to [`as_ref`](#method.as_ref) but consumes `self` and registers the - /// Python object reference in PyO3's object storage. The reference count for the Python - /// object will not be decreased until the GIL lifetime ends. - /// - /// You should prefer using [`as_ref`](#method.as_ref) if you can as it'll have less overhead. - /// - /// # Examples - /// - /// [`Py::as_ref`]'s lifetime limitation forbids creating a function that references a - /// variable created inside the function. - /// - /// ```rust,compile_fail - /// # use pyo3::prelude::*; - /// # - /// fn new_py_any<'py>(py: Python<'py>, value: impl IntoPy>) -> &'py PyAny { - /// let obj: Py = value.into_py(py); - /// - /// // The lifetime of the return value of this function is the shortest - /// // of `obj` and `py`. As `obj` is owned by the current function, - /// // Rust won't let the return value escape this function! - /// obj.as_ref(py) - /// } - /// ``` - /// - /// This can be solved by using [`Py::into_ref`] instead, which does not suffer from this issue. - /// Note that the lifetime of the [`Python<'py>`](crate::Python) token is transferred to - /// the returned reference. - /// - /// ```rust - /// # use pyo3::prelude::*; - /// # #[allow(dead_code)] // This is just to show it compiles. - /// fn new_py_any<'py>(py: Python<'py>, value: impl IntoPy>) -> &'py PyAny { - /// let obj: Py = value.into_py(py); - /// - /// // This reference's lifetime is determined by `py`'s lifetime. - /// // Because that originates from outside this function, - /// // this return value is allowed. - /// # #[allow(deprecated)] - /// obj.into_ref(py) - /// } - /// ``` - #[deprecated( - since = "0.21.0", - note = "use `obj.into_bound(py)` instead of `obj.into_ref(py)`" - )] - pub fn into_ref(self, py: Python<'_>) -> &T::AsRefTarget { - #[allow(deprecated)] - unsafe { - py.from_owned_ptr(self.into_ptr()) - } - } -} - impl Py { /// Returns the raw FFI pointer represented by self. /// @@ -1558,20 +1365,6 @@ impl Py { .setattr(attr_name, value.into_py(py).into_bound(py)) } - /// Deprecated form of [`call_bound`][Py::call_bound]. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`call` will be replaced by `call_bound` in a future PyO3 version" - )] - #[inline] - pub fn call(&self, py: Python<'_>, args: A, kwargs: Option<&PyDict>) -> PyResult - where - A: IntoPy>, - { - self.call_bound(py, args, kwargs.map(PyDict::as_borrowed).as_deref()) - } - /// Calls the object. /// /// This is equivalent to the Python expression `self(*args, **kwargs)`. @@ -1598,27 +1391,6 @@ impl Py { self.bind(py).as_any().call0().map(Bound::unbind) } - /// Deprecated form of [`call_method_bound`][Py::call_method_bound]. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`call_method` will be replaced by `call_method_bound` in a future PyO3 version" - )] - #[inline] - pub fn call_method( - &self, - py: Python<'_>, - name: N, - args: A, - kwargs: Option<&PyDict>, - ) -> PyResult - where - N: IntoPy>, - A: IntoPy>, - { - self.call_method_bound(py, name, args, kwargs.map(PyDict::as_borrowed).as_deref()) - } - /// Calls a method on the object. /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. @@ -1831,17 +1603,6 @@ unsafe impl crate::AsPyPointer for Py { } } -#[cfg(feature = "gil-refs")] -impl std::convert::From<&'_ T> for PyObject -where - T: PyNativeType, -{ - #[inline] - fn from(obj: &T) -> Self { - obj.as_borrowed().to_owned().into_any().unbind() - } -} - impl std::convert::From> for PyObject where T: AsRef, @@ -1870,18 +1631,6 @@ impl std::convert::From> for Py { } } -// `&PyCell` can be converted to `Py` -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl std::convert::From<&crate::PyCell> for Py -where - T: PyClass, -{ - fn from(cell: &crate::PyCell) -> Self { - cell.as_borrowed().to_owned().unbind() - } -} - impl<'a, T> std::convert::From> for Py where T: PyClass, @@ -1952,18 +1701,6 @@ where } } -/// `Py` can be used as an error when T is an Error. -/// -/// However for GIL lifetime reasons, cause() cannot be implemented for `Py`. -/// Use .as_ref() to get the GIL-scoped error if you need to inspect the cause. -#[cfg(feature = "gil-refs")] -impl std::error::Error for Py -where - T: std::error::Error + PyTypeInfo, - T::AsRefTarget: std::fmt::Display, -{ -} - impl std::fmt::Display for Py where T: PyTypeInfo, @@ -2049,24 +1786,6 @@ impl PyObject { self.bind(py).downcast() } - /// Deprecated form of [`PyObject::downcast_bound_unchecked`] - /// - /// # Safety - /// - /// Callers must ensure that the type is valid or risk type confusion. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`PyObject::downcast_unchecked` will be replaced by `PyObject::downcast_bound_unchecked` in a future PyO3 version" - )] - #[inline] - pub unsafe fn downcast_unchecked<'py, T>(&'py self, py: Python<'py>) -> &T - where - T: HasPyGilRef, - { - self.downcast_bound_unchecked::(py).as_gil_ref() - } - /// Casts the PyObject to a concrete Python object type without checking validity. /// /// # Safety diff --git a/src/lib.rs b/src/lib.rs index f0854d547b8..fdba3f842e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -318,18 +318,10 @@ pub use crate::class::*; pub use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, ToPyObject}; pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr}; -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -pub use crate::gil::GILPool; #[cfg(not(any(PyPy, GraalPy)))] pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter}; -#[cfg(feature = "gil-refs")] -pub use crate::instance::PyNativeType; pub use crate::instance::{Borrowed, Bound, Py, PyObject}; pub use crate::marker::Python; -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -pub use crate::pycell::PyCell; pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pyclass::PyClass; pub use crate::pyclass_init::PyClassInitializer; @@ -502,7 +494,6 @@ pub mod doc_test { "guide/src/function.md" => guide_function_md, "guide/src/function/error-handling.md" => guide_function_error_handling_md, "guide/src/function/signature.md" => guide_function_signature_md, - "guide/src/memory.md" => guide_memory_md, "guide/src/migration.md" => guide_migration_md, "guide/src/module.md" => guide_module_md, "guide/src/parallelism.md" => guide_parallelism_md, diff --git a/src/macros.rs b/src/macros.rs index 4de52185eda..79b65c17b45 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -105,9 +105,7 @@ macro_rules! py_run_impl { ($py:expr, *$dict:expr, $code:expr) => {{ use ::std::option::Option::*; #[allow(unused_imports)] - #[cfg(feature = "gil-refs")] - use $crate::PyNativeType; - if let ::std::result::Result::Err(e) = $py.run_bound($code, None, Some(&$dict.as_borrowed())) { + if let ::std::result::Result::Err(e) = $py.run_bound($code, None, Some(&$dict)) { e.print($py); // So when this c api function the last line called printed the error to stderr, // the output is only written into a buffer which is never flushed because we diff --git a/src/marker.rs b/src/marker.rs index 58843cf29aa..bc0e22e37e7 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -126,9 +126,6 @@ use crate::types::{ PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType, }; use crate::version::PythonVersionInfo; -#[allow(deprecated)] -#[cfg(feature = "gil-refs")] -use crate::{conversion::FromPyPointer, gil::GILPool, PyNativeType}; use crate::{ffi, Bound, IntoPy, Py, PyObject, PyTypeInfo}; use std::ffi::{CStr, CString}; use std::marker::PhantomData; @@ -279,10 +276,6 @@ mod nightly { // This means that PyString, PyList, etc all inherit !Ungil from this. impl !Ungil for crate::PyAny {} - // All the borrowing wrappers - #[allow(deprecated)] - #[cfg(feature = "gil-refs")] - impl !Ungil for crate::PyCell {} impl !Ungil for crate::PyRef<'_, T> {} impl !Ungil for crate::PyRefMut<'_, T> {} @@ -522,26 +515,6 @@ impl<'py> Python<'py> { f() } - /// Deprecated version of [`Python::eval_bound`] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`Python::eval` will be replaced by `Python::eval_bound` in a future PyO3 version" - )] - pub fn eval( - self, - code: &str, - globals: Option<&'py PyDict>, - locals: Option<&'py PyDict>, - ) -> PyResult<&'py PyAny> { - self.eval_bound( - code, - globals.map(PyNativeType::as_borrowed).as_deref(), - locals.map(PyNativeType::as_borrowed).as_deref(), - ) - .map(Bound::into_gil_ref) - } - /// Evaluates a Python expression in the given context and returns the result. /// /// If `globals` is `None`, it defaults to Python module `__main__`. @@ -569,25 +542,6 @@ impl<'py> Python<'py> { self.run_code(code, ffi::Py_eval_input, globals, locals) } - /// Deprecated version of [`Python::run_bound`] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`Python::run` will be replaced by `Python::run_bound` in a future PyO3 version" - )] - pub fn run( - self, - code: &str, - globals: Option<&PyDict>, - locals: Option<&PyDict>, - ) -> PyResult<()> { - self.run_bound( - code, - globals.map(PyNativeType::as_borrowed).as_deref(), - locals.map(PyNativeType::as_borrowed).as_deref(), - ) - } - /// Executes one or more Python statements in the given context. /// /// If `globals` is `None`, it defaults to Python module `__main__`. @@ -695,20 +649,6 @@ impl<'py> Python<'py> { } } - /// Gets the Python type object for type `T`. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`Python::get_type` will be replaced by `Python::get_type_bound` in a future PyO3 version" - )] - #[inline] - pub fn get_type(self) -> &'py PyType - where - T: PyTypeInfo, - { - self.get_type_bound::().into_gil_ref() - } - /// Gets the Python type object for type `T`. #[inline] pub fn get_type_bound(self) -> Bound<'py, PyType> @@ -718,19 +658,6 @@ impl<'py> Python<'py> { T::type_object_bound(self) } - /// Deprecated form of [`Python::import_bound`] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`Python::import` will be replaced by `Python::import_bound` in a future PyO3 version" - )] - pub fn import(self, name: N) -> PyResult<&'py PyModule> - where - N: IntoPy>, - { - Self::import_bound(self, name).map(Bound::into_gil_ref) - } - /// Imports the Python module with the specified name. pub fn import_bound(self, name: N) -> PyResult> where @@ -801,127 +728,6 @@ impl<'py> Python<'py> { PythonVersionInfo::from_str(version_number_str).unwrap() } - /// Registers the object pointer in the release pool, - /// and does an unchecked downcast to the specific type. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr(py, ptr)` or `Bound::from_owned_ptr(py, ptr)` instead" - )] - pub unsafe fn from_owned_ptr(self, ptr: *mut ffi::PyObject) -> &'py T - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_owned_ptr(self, ptr) - } - - /// Registers the owned object pointer in the release pool. - /// - /// Returns `Err(PyErr)` if the pointer is NULL. - /// Does an unchecked downcast to the specific type. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr_or_err(py, ptr)` or `Bound::from_owned_ptr_or_err(py, ptr)` instead" - )] - pub unsafe fn from_owned_ptr_or_err(self, ptr: *mut ffi::PyObject) -> PyResult<&'py T> - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_owned_ptr_or_err(self, ptr) - } - - /// Registers the owned object pointer in release pool. - /// - /// Returns `None` if the pointer is NULL. - /// Does an unchecked downcast to the specific type. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_owned_ptr_or_opt(py, ptr)` or `Bound::from_owned_ptr_or_opt(py, ptr)` instead" - )] - pub unsafe fn from_owned_ptr_or_opt(self, ptr: *mut ffi::PyObject) -> Option<&'py T> - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_owned_ptr_or_opt(self, ptr) - } - - /// Does an unchecked downcast to the specific type. - /// - /// Panics if the pointer is NULL. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr(py, ptr)` or `Bound::from_borrowed_ptr(py, ptr)` instead" - )] - pub unsafe fn from_borrowed_ptr(self, ptr: *mut ffi::PyObject) -> &'py T - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_borrowed_ptr(self, ptr) - } - - /// Does an unchecked downcast to the specific type. - /// - /// Returns `Err(PyErr)` if the pointer is NULL. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr_or_err(py, ptr)` or `Bound::from_borrowed_ptr_or_err(py, ptr)` instead" - )] - pub unsafe fn from_borrowed_ptr_or_err(self, ptr: *mut ffi::PyObject) -> PyResult<&'py T> - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_borrowed_ptr_or_err(self, ptr) - } - - /// Does an unchecked downcast to the specific type. - /// - /// Returns `None` if the pointer is NULL. - /// - /// # Safety - /// - /// Callers must ensure that ensure that the cast is valid. - #[allow(clippy::wrong_self_convention, deprecated)] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "use `Py::from_borrowed_ptr_or_opt(py, ptr)` or `Bound::from_borrowed_ptr_or_opt(py, ptr)` instead" - )] - pub unsafe fn from_borrowed_ptr_or_opt(self, ptr: *mut ffi::PyObject) -> Option<&'py T> - where - T: FromPyPointer<'py>, - { - FromPyPointer::from_borrowed_ptr_or_opt(self, ptr) - } - /// Lets the Python interpreter check and handle any pending signals. This will invoke the /// corresponding signal handlers registered in Python (if any). /// @@ -967,148 +773,6 @@ impl<'py> Python<'py> { pub fn check_signals(self) -> PyResult<()> { err::error_on_minusone(self, unsafe { ffi::PyErr_CheckSignals() }) } - - /// Create a new pool for managing PyO3's GIL Refs. This has no functional - /// use for code which does not use the deprecated GIL Refs API. - /// - /// When this `GILPool` is dropped, all GIL Refs created after this `GILPool` will - /// all have their Python reference counts decremented, potentially allowing Python to drop - /// the corresponding Python objects. - /// - /// Typical usage of PyO3 will not need this API, as [`Python::with_gil`] automatically creates - /// a `GILPool` where appropriate. - /// - /// Advanced uses of PyO3 which perform long-running tasks which never free the GIL may need - /// to use this API to clear memory, as PyO3 usually does not clear memory until the GIL is - /// released. - /// - /// # Examples - /// - /// ```rust - /// # use pyo3::prelude::*; - /// Python::with_gil(|py| { - /// // Some long-running process like a webserver, which never releases the GIL. - /// loop { - /// // Create a new pool, so that PyO3 can clear memory at the end of the loop. - /// #[allow(deprecated)] // `new_pool` is not needed in code not using the GIL Refs API - /// let pool = unsafe { py.new_pool() }; - /// - /// // It is recommended to *always* immediately set py to the pool's Python, to help - /// // avoid creating references with invalid lifetimes. - /// let py = pool.python(); - /// - /// // do stuff... - /// # break; // Exit the loop so that doctest terminates! - /// } - /// }); - /// ``` - /// - /// # Safety - /// - /// Extreme care must be taken when using this API, as misuse can lead to accessing invalid - /// memory. In addition, the caller is responsible for guaranteeing that the GIL remains held - /// for the entire lifetime of the returned `GILPool`. - /// - /// Two best practices are required when using this API: - /// - From the moment `new_pool()` is called, only the `Python` token from the returned - /// `GILPool` (accessible using [`.python()`]) should be used in PyO3 APIs. All other older - /// `Python` tokens with longer lifetimes are unsafe to use until the `GILPool` is dropped, - /// because they can be used to create PyO3 owned references which have lifetimes which - /// outlive the `GILPool`. - /// - Similarly, methods on existing owned references will implicitly refer back to the - /// `Python` token which that reference was originally created with. If the returned values - /// from these methods are owned references they will inherit the same lifetime. As a result, - /// Rust's lifetime rules may allow them to outlive the `GILPool`, even though this is not - /// safe for reasons discussed above. Care must be taken to never access these return values - /// after the `GILPool` is dropped, unless they are converted to `Py` *before* the pool - /// is dropped. - /// - /// [`.python()`]: crate::GILPool::python - #[inline] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "code not using the GIL Refs API can safely remove use of `Python::new_pool`" - )] - #[allow(deprecated)] - pub unsafe fn new_pool(self) -> GILPool { - GILPool::new() - } -} - -impl Python<'_> { - /// Creates a scope using a new pool for managing PyO3's GIL Refs. This has no functional - /// use for code which does not use the deprecated GIL Refs API. - /// - /// This is a safe alterantive to [`new_pool`][Self::new_pool] as - /// it limits the closure to using the new GIL token at the cost of - /// being unable to capture existing GIL-bound references. - /// - /// Note that on stable Rust, this API suffers from the same the `SendWrapper` loophole - /// as [`allow_threads`][Self::allow_threads], c.f. the documentation of the [`Ungil`] trait, - /// - /// # Examples - /// - /// ```rust - /// # use pyo3::prelude::*; - /// Python::with_gil(|py| { - /// // Some long-running process like a webserver, which never releases the GIL. - /// loop { - /// // Create a new scope, so that PyO3 can clear memory at the end of the loop. - /// #[allow(deprecated)] // `with_pool` is not needed in code not using the GIL Refs API - /// py.with_pool(|py| { - /// // do stuff... - /// }); - /// # break; // Exit the loop so that doctest terminates! - /// } - /// }); - /// ``` - /// - /// The `Ungil` bound on the closure does prevent hanging on to existing GIL-bound references - /// - /// ```compile_fail - /// # #![allow(deprecated)] - /// # use pyo3::prelude::*; - /// # use pyo3::types::PyString; - /// - /// Python::with_gil(|py| { - /// let old_str = PyString::new(py, "a message from the past"); - /// - /// py.with_pool(|_py| { - /// print!("{:?}", old_str); - /// }); - /// }); - /// ``` - /// - /// or continuing to use the old GIL token - /// - /// ```compile_fail - /// # use pyo3::prelude::*; - /// - /// Python::with_gil(|old_py| { - /// old_py.with_pool(|_new_py| { - /// let _none = old_py.None(); - /// }); - /// }); - /// ``` - #[inline] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "code not using the GIL Refs API can safely remove use of `Python::with_pool`" - )] - #[allow(deprecated)] - pub fn with_pool(&self, f: F) -> R - where - F: for<'py> FnOnce(Python<'py>) -> R + Ungil, - { - // SAFETY: The closure is `Ungil`, - // i.e. it does not capture any GIL-bound references - // and accesses only the newly created GIL token. - let pool = unsafe { GILPool::new() }; - - f(pool.python()) - } } impl<'unbound> Python<'unbound> { diff --git a/src/prelude.rs b/src/prelude.rs index eac04d0e048..6182b21c2d1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -12,14 +12,9 @@ pub use crate::conversion::{FromPyObject, IntoPy, ToPyObject}; pub use crate::err::{PyErr, PyResult}; pub use crate::instance::{Borrowed, Bound, Py, PyObject}; pub use crate::marker::Python; -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -pub use crate::pycell::PyCell; pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pyclass_init::PyClassInitializer; pub use crate::types::{PyAny, PyModule}; -#[cfg(feature = "gil-refs")] -pub use crate::PyNativeType; #[cfg(feature = "macros")] pub use pyo3_macros::{pyclass, pyfunction, pymethods, pymodule, FromPyObject}; diff --git a/src/pycell.rs b/src/pycell.rs index 7dadb8361d5..6b501ad0401 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -199,376 +199,14 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::internal_tricks::{ptr_from_mut, ptr_from_ref}; use crate::pyclass::{boolean_struct::False, PyClass}; use crate::types::any::PyAnyMethods; -#[cfg(feature = "gil-refs")] -use crate::{ - conversion::ToPyObject, - impl_::pyclass::PyClassImpl, - pyclass::boolean_struct::True, - pyclass_init::PyClassInitializer, - type_object::{PyLayout, PySizedLayout}, - types::PyAny, - PyNativeType, PyResult, PyTypeCheck, -}; use crate::{ffi, Bound, IntoPy, PyErr, PyObject, Python}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; pub(crate) mod impl_; -#[cfg(feature = "gil-refs")] -use self::impl_::PyClassObject; use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; -/// A container type for (mutably) accessing [`PyClass`] values -/// -/// `PyCell` autodereferences to [`PyAny`], so you can call `PyAny`'s methods on a `PyCell`. -/// -/// # Examples -/// -/// This example demonstrates getting a mutable reference of the contained `PyClass`. -/// ```rust -/// use pyo3::prelude::*; -/// -/// #[pyclass] -/// struct Number { -/// inner: u32, -/// } -/// -/// #[pymethods] -/// impl Number { -/// fn increment(&mut self) { -/// self.inner += 1; -/// } -/// } -/// -/// # fn main() -> PyResult<()> { -/// Python::with_gil(|py| { -/// # #[allow(deprecated)] -/// let n = PyCell::new(py, Number { inner: 0 })?; -/// -/// let n_mutable: &mut Number = &mut n.borrow_mut(); -/// n_mutable.increment(); -/// -/// Ok(()) -/// }) -/// # } -/// ``` -/// For more information on how, when and why (not) to use `PyCell` please see the -/// [module-level documentation](self). -#[cfg(feature = "gil-refs")] -#[deprecated( - since = "0.21.0", - note = "`PyCell` was merged into `Bound`, use that instead; see the migration guide for more info" -)] -#[repr(transparent)] -pub struct PyCell(PyClassObject); - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -unsafe impl PyNativeType for PyCell { - type AsRefSource = T; -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl PyCell { - /// Makes a new `PyCell` on the Python heap and return the reference to it. - /// - /// In cases where the value in the cell does not need to be accessed immediately after - /// creation, consider [`Py::new`](crate::Py::new) as a more efficient alternative. - #[deprecated( - since = "0.21.0", - note = "use `Bound::new(py, value)` or `Py::new(py, value)` instead of `PyCell::new(py, value)`" - )] - pub fn new(py: Python<'_>, value: impl Into>) -> PyResult<&Self> { - Bound::new(py, value).map(Bound::into_gil_ref) - } - - /// Immutably borrows the value `T`. This borrow lasts as long as the returned `PyRef` exists. - /// - /// For frozen classes, the simpler [`get`][Self::get] is available. - /// - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use - /// [`try_borrow`](#method.try_borrow). - pub fn borrow(&self) -> PyRef<'_, T> { - PyRef::borrow(&self.as_borrowed()) - } - - /// Mutably borrows the value `T`. This borrow lasts as long as the returned `PyRefMut` exists. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. For a non-panicking variant, use - /// [`try_borrow_mut`](#method.try_borrow_mut). - pub fn borrow_mut(&self) -> PyRefMut<'_, T> - where - T: PyClass, - { - PyRefMut::borrow(&self.as_borrowed()) - } - - /// Immutably borrows the value `T`, returning an error if the value is currently - /// mutably borrowed. This borrow lasts as long as the returned `PyRef` exists. - /// - /// This is the non-panicking variant of [`borrow`](#method.borrow). - /// - /// For frozen classes, the simpler [`get`][Self::get] is available. - /// - /// # Examples - /// - /// ``` - /// # use pyo3::prelude::*; - /// #[pyclass] - /// struct Class {} - /// - /// Python::with_gil(|py| { - /// # #[allow(deprecated)] - /// let c = PyCell::new(py, Class {}).unwrap(); - /// { - /// let m = c.borrow_mut(); - /// assert!(c.try_borrow().is_err()); - /// } - /// - /// { - /// let m = c.borrow(); - /// assert!(c.try_borrow().is_ok()); - /// } - /// }); - /// ``` - pub fn try_borrow(&self) -> Result, PyBorrowError> { - PyRef::try_borrow(&self.as_borrowed()) - } - - /// Mutably borrows the value `T`, returning an error if the value is currently borrowed. - /// This borrow lasts as long as the returned `PyRefMut` exists. - /// - /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). - /// - /// # Examples - /// - /// ``` - /// # use pyo3::prelude::*; - /// #[pyclass] - /// struct Class {} - /// Python::with_gil(|py| { - /// # #[allow(deprecated)] - /// let c = PyCell::new(py, Class {}).unwrap(); - /// { - /// let m = c.borrow(); - /// assert!(c.try_borrow_mut().is_err()); - /// } - /// - /// assert!(c.try_borrow_mut().is_ok()); - /// }); - /// ``` - pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> - where - T: PyClass, - { - PyRefMut::try_borrow(&self.as_borrowed()) - } - - /// Immutably borrows the value `T`, returning an error if the value is - /// currently mutably borrowed. - /// - /// # Safety - /// - /// This method is unsafe because it does not return a `PyRef`, - /// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell` - /// while the reference returned by this method is alive is undefined behaviour. - /// - /// # Examples - /// - /// ``` - /// # use pyo3::prelude::*; - /// #[pyclass] - /// struct Class {} - /// Python::with_gil(|py| { - /// # #[allow(deprecated)] - /// let c = PyCell::new(py, Class {}).unwrap(); - /// - /// { - /// let m = c.borrow_mut(); - /// assert!(unsafe { c.try_borrow_unguarded() }.is_err()); - /// } - /// - /// { - /// let m = c.borrow(); - /// assert!(unsafe { c.try_borrow_unguarded() }.is_ok()); - /// } - /// }); - /// ``` - pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - self.0.ensure_threadsafe(); - self.0 - .borrow_checker() - .try_borrow_unguarded() - .map(|_: ()| &*self.0.get_ptr()) - } - - /// Provide an immutable borrow of the value `T` without acquiring the GIL. - /// - /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`]. - /// - /// While the GIL is usually required to get access to `&PyCell`, - /// compared to [`borrow`][Self::borrow] or [`try_borrow`][Self::try_borrow] - /// this avoids any thread or borrow checking overhead at runtime. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// # use pyo3::prelude::*; - /// - /// #[pyclass(frozen)] - /// struct FrozenCounter { - /// value: AtomicUsize, - /// } - /// - /// Python::with_gil(|py| { - /// let counter = FrozenCounter { value: AtomicUsize::new(0) }; - /// - /// # #[allow(deprecated)] - /// let cell = PyCell::new(py, counter).unwrap(); - /// - /// cell.get().value.fetch_add(1, Ordering::Relaxed); - /// }); - /// ``` - pub fn get(&self) -> &T - where - T: PyClass + Sync, - { - // SAFETY: The class itself is frozen and `Sync` and we do not access anything but `self.contents.value`. - unsafe { &*self.get_ptr() } - } - - /// Replaces the wrapped value with a new one, returning the old value. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - #[inline] - pub fn replace(&self, t: T) -> T - where - T: PyClass, - { - std::mem::replace(&mut *self.borrow_mut(), t) - } - - /// Replaces the wrapped value with a new one computed from `f`, returning the old value. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - pub fn replace_with T>(&self, f: F) -> T - where - T: PyClass, - { - let mut_borrow = &mut *self.borrow_mut(); - let replacement = f(mut_borrow); - std::mem::replace(mut_borrow, replacement) - } - - /// Swaps the wrapped value of `self` with the wrapped value of `other`. - /// - /// # Panics - /// - /// Panics if the value in either `PyCell` is currently borrowed. - #[inline] - pub fn swap(&self, other: &Self) - where - T: PyClass, - { - std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) - } - - pub(crate) fn get_ptr(&self) -> *mut T { - self.0.get_ptr() - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -unsafe impl PyLayout for PyCell {} -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl PySizedLayout for PyCell {} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl PyTypeCheck for PyCell -where - T: PyClass, -{ - const NAME: &'static str = ::NAME; - - fn type_check(object: &Bound<'_, PyAny>) -> bool { - ::type_check(object) - } -} -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -unsafe impl AsPyPointer for PyCell { - fn as_ptr(&self) -> *mut ffi::PyObject { - ptr_from_ref(self) as *mut _ - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl ToPyObject for &PyCell { - fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl AsRef for PyCell { - fn as_ref(&self) -> &PyAny { - #[allow(deprecated)] - unsafe { - self.py().from_borrowed_ptr(self.as_ptr()) - } - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl Deref for PyCell { - type Target = PyAny; - - fn deref(&self) -> &PyAny { - #[allow(deprecated)] - unsafe { - self.py().from_borrowed_ptr(self.as_ptr()) - } - } -} - -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl fmt::Debug for PyCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_borrow() { - Ok(borrow) => f.debug_struct("RefCell").field("value", &borrow).finish(), - Err(_) => { - struct BorrowedPlaceholder; - impl fmt::Debug for BorrowedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - f.debug_struct("RefCell") - .field("value", &BorrowedPlaceholder) - .finish() - } - } - } -} - /// A wrapper type for an immutably borrowed value from a [`Bound<'py, T>`]. /// /// See the [`Bound`] documentation for more information. @@ -828,15 +466,6 @@ impl IntoPy for &'_ PyRef<'_, T> { } } -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell> for crate::PyRef<'a, T> { - type Error = PyBorrowError; - fn try_from(cell: &'a crate::PyCell) -> Result { - cell.try_borrow() - } -} - unsafe impl<'a, T: PyClass> AsPyPointer for PyRef<'a, T> { fn as_ptr(&self) -> *mut ffi::PyObject { self.inner.as_ptr() @@ -1012,17 +641,6 @@ unsafe impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> { } } -#[cfg(feature = "gil-refs")] -#[allow(deprecated)] -impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell> - for crate::PyRefMut<'a, T> -{ - type Error = PyBorrowMutError; - fn try_from(cell: &'a crate::PyCell) -> Result { - cell.try_borrow_mut() - } -} - impl + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.deref(), f) @@ -1090,108 +708,6 @@ mod tests { #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct SomeClass(i32); - #[cfg(feature = "gil-refs")] - mod deprecated { - use super::*; - - #[test] - fn pycell_replace() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - assert_eq!(*cell.borrow(), SomeClass(0)); - - let previous = cell.replace(SomeClass(123)); - assert_eq!(previous, SomeClass(0)); - assert_eq!(*cell.borrow(), SomeClass(123)); - }) - } - - #[test] - #[should_panic(expected = "Already borrowed: PyBorrowMutError")] - fn pycell_replace_panic() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - let _guard = cell.borrow(); - - cell.replace(SomeClass(123)); - }) - } - - #[test] - fn pycell_replace_with() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - assert_eq!(*cell.borrow(), SomeClass(0)); - - let previous = cell.replace_with(|value| { - *value = SomeClass(2); - SomeClass(123) - }); - assert_eq!(previous, SomeClass(2)); - assert_eq!(*cell.borrow(), SomeClass(123)); - }) - } - - #[test] - #[should_panic(expected = "Already borrowed: PyBorrowMutError")] - fn pycell_replace_with_panic() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - let _guard = cell.borrow(); - - cell.replace_with(|_| SomeClass(123)); - }) - } - - #[test] - fn pycell_swap() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - #[allow(deprecated)] - let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); - assert_eq!(*cell.borrow(), SomeClass(0)); - assert_eq!(*cell2.borrow(), SomeClass(123)); - - cell.swap(cell2); - assert_eq!(*cell.borrow(), SomeClass(123)); - assert_eq!(*cell2.borrow(), SomeClass(0)); - }) - } - - #[test] - #[should_panic(expected = "Already borrowed: PyBorrowMutError")] - fn pycell_swap_panic() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - #[allow(deprecated)] - let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); - - let _guard = cell.borrow(); - cell.swap(cell2); - }) - } - - #[test] - #[should_panic(expected = "Already borrowed: PyBorrowMutError")] - fn pycell_swap_panic_other_borrowed() { - Python::with_gil(|py| { - #[allow(deprecated)] - let cell = PyCell::new(py, SomeClass(0)).unwrap(); - #[allow(deprecated)] - let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); - - let _guard = cell2.borrow(); - cell.swap(cell2); - }) - } - } - #[test] fn test_as_ptr() { Python::with_gil(|py| { diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index efc057e74f7..1b5ce774379 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -74,9 +74,6 @@ pub trait PyClassBorrowChecker { /// Increments immutable borrow count, if possible fn try_borrow(&self) -> Result<(), PyBorrowError>; - #[cfg(feature = "gil-refs")] - fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>; - /// Decrements immutable borrow count fn release_borrow(&self); /// Increments mutable borrow count, if possible @@ -96,12 +93,6 @@ impl PyClassBorrowChecker for EmptySlot { Ok(()) } - #[inline] - #[cfg(feature = "gil-refs")] - fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> { - Ok(()) - } - #[inline] fn release_borrow(&self) {} @@ -132,16 +123,6 @@ impl PyClassBorrowChecker for BorrowChecker { } } - #[cfg(feature = "gil-refs")] - fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> { - let flag = self.0.get(); - if flag != BorrowFlag::HAS_MUTABLE_BORROW { - Ok(()) - } else { - Err(PyBorrowError { _private: () }) - } - } - fn release_borrow(&self) { let flag = self.0.get(); self.0.set(flag.decrement()) diff --git a/src/pyclass.rs b/src/pyclass.rs index 91510e11151..4e409566af5 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -12,20 +12,6 @@ pub use self::gc::{PyTraverseError, PyVisit}; /// /// The `#[pyclass]` attribute implements this trait for your Rust struct - /// you shouldn't implement this trait directly. -#[allow(deprecated)] -#[cfg(feature = "gil-refs")] -pub trait PyClass: PyTypeInfo> + PyClassImpl { - /// Whether the pyclass is frozen. - /// - /// This can be enabled via `#[pyclass(frozen)]`. - type Frozen: Frozen; -} - -/// Types that can be used as Python classes. -/// -/// The `#[pyclass]` attribute implements this trait for your Rust struct - -/// you shouldn't implement this trait directly. -#[cfg(not(feature = "gil-refs"))] pub trait PyClass: PyTypeInfo + PyClassImpl { /// Whether the pyclass is frozen. /// diff --git a/src/type_object.rs b/src/type_object.rs index 871e8366865..ed3cbf52de4 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -3,8 +3,6 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::types::any::PyAnyMethods; use crate::types::{PyAny, PyType}; -#[cfg(feature = "gil-refs")] -use crate::PyNativeType; use crate::{ffi, Bound, Python}; /// `T: PyLayout` represents that `T` is a concrete representation of `U` in the Python heap. @@ -23,120 +21,6 @@ pub unsafe trait PyLayout {} /// In addition, that `T` is a concrete representation of `U`. pub trait PySizedLayout: PyLayout + Sized {} -/// Specifies that this type has a "GIL-bound Reference" form. -/// -/// This is expected to be deprecated in the near future, see -/// -/// # Safety -/// -/// - `Py::as_ref` will hand out references to `Self::AsRefTarget`. -/// - `Self::AsRefTarget` must have the same layout as `UnsafeCell`. -#[cfg(feature = "gil-refs")] -pub unsafe trait HasPyGilRef { - /// Utility type to make Py::as_ref work. - type AsRefTarget: PyNativeType; -} - -#[cfg(feature = "gil-refs")] -unsafe impl HasPyGilRef for T -where - T: PyNativeType, -{ - type AsRefTarget = Self; -} - -/// Python type information. -/// All Python native types (e.g., `PyDict`) and `#[pyclass]` structs implement this trait. -/// -/// This trait is marked unsafe because: -/// - specifying the incorrect layout can lead to memory errors -/// - the return value of type_object must always point to the same PyTypeObject instance -/// -/// It is safely implemented by the `pyclass` macro. -/// -/// # Safety -/// -/// Implementations must provide an implementation for `type_object_raw` which infallibly produces a -/// non-null pointer to the corresponding Python type object. -#[cfg(feature = "gil-refs")] -pub unsafe trait PyTypeInfo: Sized + HasPyGilRef { - /// Class name. - const NAME: &'static str; - - /// Module name, if any. - const MODULE: Option<&'static str>; - - /// Returns the PyTypeObject instance for this type. - fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject; - - /// Returns the safe abstraction over the type object. - #[inline] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`PyTypeInfo::type_object` will be replaced by `PyTypeInfo::type_object_bound` in a future PyO3 version" - )] - fn type_object(py: Python<'_>) -> &PyType { - // This isn't implemented in terms of `type_object_bound` because this just borrowed the - // object, for legacy reasons. - #[allow(deprecated)] - unsafe { - py.from_borrowed_ptr(Self::type_object_raw(py) as _) - } - } - - /// Returns the safe abstraction over the type object. - #[inline] - fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> { - // Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme - // edge case, but arbitrary Python code _could_ change the __class__ of an object and cause - // the type object to be freed. - // - // By making `Bound` we assume ownership which is then safe against races. - unsafe { - Self::type_object_raw(py) - .cast::() - .assume_borrowed_unchecked(py) - .to_owned() - .downcast_into_unchecked() - } - } - - /// Checks if `object` is an instance of this type or a subclass of this type. - #[inline] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`PyTypeInfo::is_type_of` will be replaced by `PyTypeInfo::is_type_of_bound` in a future PyO3 version" - )] - fn is_type_of(object: &PyAny) -> bool { - Self::is_type_of_bound(&object.as_borrowed()) - } - - /// Checks if `object` is an instance of this type or a subclass of this type. - #[inline] - fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { - unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 } - } - - /// Checks if `object` is an instance of this type. - #[inline] - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`PyTypeInfo::is_exact_type_of` will be replaced by `PyTypeInfo::is_exact_type_of_bound` in a future PyO3 version" - )] - fn is_exact_type_of(object: &PyAny) -> bool { - Self::is_exact_type_of_bound(&object.as_borrowed()) - } - - /// Checks if `object` is an instance of this type. - #[inline] - fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { - unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) } - } -} - /// Python type information. /// All Python native types (e.g., `PyDict`) and `#[pyclass]` structs implement this trait. /// @@ -150,7 +34,6 @@ pub unsafe trait PyTypeInfo: Sized + HasPyGilRef { /// /// Implementations must provide an implementation for `type_object_raw` which infallibly produces a /// non-null pointer to the corresponding Python type object. -#[cfg(not(feature = "gil-refs"))] pub unsafe trait PyTypeInfo: Sized { /// Class name. const NAME: &'static str; @@ -192,19 +75,6 @@ pub unsafe trait PyTypeInfo: Sized { } /// Implemented by types which can be used as a concrete Python type inside `Py` smart pointers. -#[cfg(feature = "gil-refs")] -pub trait PyTypeCheck: HasPyGilRef { - /// Name of self. This is used in error messages, for example. - const NAME: &'static str; - - /// Checks if `object` is an instance of `Self`, which may include a subtype. - /// - /// This should be equivalent to the Python expression `isinstance(object, Self)`. - fn type_check(object: &Bound<'_, PyAny>) -> bool; -} - -/// Implemented by types which can be used as a concrete Python type inside `Py` smart pointers. -#[cfg(not(feature = "gil-refs"))] pub trait PyTypeCheck { /// Name of self. This is used in error messages, for example. const NAME: &'static str; diff --git a/src/types/any.rs b/src/types/any.rs index 33ac04df763..626feba0856 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -10,8 +10,6 @@ use crate::type_object::{PyTypeCheck, PyTypeInfo}; #[cfg(not(any(PyPy, GraalPy)))] use crate::types::PySuper; use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType}; -#[cfg(feature = "gil-refs")] -use crate::PyNativeType; use crate::{err, ffi, Py, Python}; use std::cell::UnsafeCell; use std::cmp::Ordering; @@ -45,8 +43,6 @@ fn PyObject_Check(_: *mut ffi::PyObject) -> c_int { 1 } -pyobject_native_type_base!(PyAny); - pyobject_native_type_info!( PyAny, pyobject_native_static_type_object!(ffi::PyBaseObject_Type), @@ -56,739 +52,6 @@ pyobject_native_type_info!( pyobject_native_type_sized!(PyAny, ffi::PyObject); -#[cfg(feature = "gil-refs")] -impl PyAny { - /// Returns whether `self` and `other` point to the same object. To compare - /// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq). - /// - /// This is equivalent to the Python expression `self is other`. - #[inline] - pub fn is(&self, other: &T) -> bool { - self.as_borrowed().is(other) - } - - /// Determines whether this object has the given attribute. - /// - /// This is equivalent to the Python expression `hasattr(self, attr_name)`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `attr_name`. - /// - /// # Example: `intern!`ing the attribute name - /// - /// ``` - /// # use pyo3::{prelude::*, intern}; - /// # - /// #[pyfunction] - /// fn has_version(sys: &Bound<'_, PyModule>) -> PyResult { - /// sys.hasattr(intern!(sys.py(), "version")) - /// } - /// # - /// # Python::with_gil(|py| { - /// # let sys = py.import_bound("sys").unwrap(); - /// # has_version(&sys).unwrap(); - /// # }); - /// ``` - pub fn hasattr(&self, attr_name: N) -> PyResult - where - N: IntoPy>, - { - self.as_borrowed().hasattr(attr_name) - } - - /// Retrieves an attribute value. - /// - /// This is equivalent to the Python expression `self.attr_name`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `attr_name`. - /// - /// # Example: `intern!`ing the attribute name - /// - /// ``` - /// # use pyo3::{prelude::*, intern}; - /// # - /// #[pyfunction] - /// fn version<'py>(sys: &Bound<'py, PyModule>) -> PyResult> { - /// sys.getattr(intern!(sys.py(), "version")) - /// } - /// # - /// # Python::with_gil(|py| { - /// # let sys = py.import_bound("sys").unwrap(); - /// # version(&sys).unwrap(); - /// # }); - /// ``` - pub fn getattr(&self, attr_name: N) -> PyResult<&PyAny> - where - N: IntoPy>, - { - self.as_borrowed() - .getattr(attr_name) - .map(Bound::into_gil_ref) - } - - /// Sets an attribute value. - /// - /// This is equivalent to the Python expression `self.attr_name = value`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `name`. - /// - /// # Example: `intern!`ing the attribute name - /// - /// ``` - /// # use pyo3::{prelude::*, intern}; - /// # - /// #[pyfunction] - /// fn set_answer(ob: &Bound<'_, PyAny>) -> PyResult<()> { - /// ob.setattr(intern!(ob.py(), "answer"), 42) - /// } - /// # - /// # Python::with_gil(|py| { - /// # let ob = PyModule::new_bound(py, "empty").unwrap(); - /// # set_answer(&ob).unwrap(); - /// # }); - /// ``` - pub fn setattr(&self, attr_name: N, value: V) -> PyResult<()> - where - N: IntoPy>, - V: ToPyObject, - { - self.as_borrowed().setattr(attr_name, value) - } - - /// Deletes an attribute. - /// - /// This is equivalent to the Python statement `del self.attr_name`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `attr_name`. - pub fn delattr(&self, attr_name: N) -> PyResult<()> - where - N: IntoPy>, - { - self.as_borrowed().delattr(attr_name) - } - - /// Returns an [`Ordering`] between `self` and `other`. - /// - /// This is equivalent to the following Python code: - /// ```python - /// if self == other: - /// return Equal - /// elif a < b: - /// return Less - /// elif a > b: - /// return Greater - /// else: - /// raise TypeError("PyAny::compare(): All comparisons returned false") - /// ``` - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// use pyo3::types::PyFloat; - /// use std::cmp::Ordering; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| -> PyResult<()> { - /// let a = PyFloat::new_bound(py, 0_f64); - /// let b = PyFloat::new_bound(py, 42_f64); - /// assert_eq!(a.compare(b)?, Ordering::Less); - /// Ok(()) - /// })?; - /// # Ok(())} - /// ``` - /// - /// It will return `PyErr` for values that cannot be compared: - /// - /// ```rust - /// use pyo3::prelude::*; - /// use pyo3::types::{PyFloat, PyString}; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| -> PyResult<()> { - /// let a = PyFloat::new_bound(py, 0_f64); - /// let b = PyString::new_bound(py, "zero"); - /// assert!(a.compare(b).is_err()); - /// Ok(()) - /// })?; - /// # Ok(())} - /// ``` - pub fn compare(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().compare(other) - } - - /// Tests whether two Python objects obey a given [`CompareOp`]. - /// - /// [`lt`](Self::lt), [`le`](Self::le), [`eq`](Self::eq), [`ne`](Self::ne), - /// [`gt`](Self::gt) and [`ge`](Self::ge) are the specialized versions - /// of this function. - /// - /// Depending on the value of `compare_op`, this is equivalent to one of the - /// following Python expressions: - /// - /// | `compare_op` | Python expression | - /// | :---: | :----: | - /// | [`CompareOp::Eq`] | `self == other` | - /// | [`CompareOp::Ne`] | `self != other` | - /// | [`CompareOp::Lt`] | `self < other` | - /// | [`CompareOp::Le`] | `self <= other` | - /// | [`CompareOp::Gt`] | `self > other` | - /// | [`CompareOp::Ge`] | `self >= other` | - /// - /// # Examples - /// - /// ```rust - /// use pyo3::class::basic::CompareOp; - /// use pyo3::prelude::*; - /// use pyo3::types::PyInt; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| -> PyResult<()> { - /// let a: Bound<'_, PyInt> = 0_u8.into_py(py).into_bound(py).downcast_into()?; - /// let b: Bound<'_, PyInt> = 42_u8.into_py(py).into_bound(py).downcast_into()?; - /// assert!(a.rich_compare(b, CompareOp::Le)?.is_truthy()?); - /// Ok(()) - /// })?; - /// # Ok(())} - /// ``` - pub fn rich_compare(&self, other: O, compare_op: CompareOp) -> PyResult<&PyAny> - where - O: ToPyObject, - { - self.as_borrowed() - .rich_compare(other, compare_op) - .map(Bound::into_gil_ref) - } - - /// Tests whether this object is less than another. - /// - /// This is equivalent to the Python expression `self < other`. - pub fn lt(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().lt(other) - } - - /// Tests whether this object is less than or equal to another. - /// - /// This is equivalent to the Python expression `self <= other`. - pub fn le(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().le(other) - } - - /// Tests whether this object is equal to another. - /// - /// This is equivalent to the Python expression `self == other`. - pub fn eq(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().eq(other) - } - - /// Tests whether this object is not equal to another. - /// - /// This is equivalent to the Python expression `self != other`. - pub fn ne(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().ne(other) - } - - /// Tests whether this object is greater than another. - /// - /// This is equivalent to the Python expression `self > other`. - pub fn gt(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().gt(other) - } - - /// Tests whether this object is greater than or equal to another. - /// - /// This is equivalent to the Python expression `self >= other`. - pub fn ge(&self, other: O) -> PyResult - where - O: ToPyObject, - { - self.as_borrowed().ge(other) - } - - /// Determines whether this object appears callable. - /// - /// This is equivalent to Python's [`callable()`][1] function. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| -> PyResult<()> { - /// let builtins = PyModule::import_bound(py, "builtins")?; - /// let print = builtins.getattr("print")?; - /// assert!(print.is_callable()); - /// Ok(()) - /// })?; - /// # Ok(())} - /// ``` - /// - /// This is equivalent to the Python statement `assert callable(print)`. - /// - /// Note that unless an API needs to distinguish between callable and - /// non-callable objects, there is no point in checking for callability. - /// Instead, it is better to just do the call and handle potential - /// exceptions. - /// - /// [1]: https://docs.python.org/3/library/functions.html#callable - pub fn is_callable(&self) -> bool { - self.as_borrowed().is_callable() - } - - /// Calls the object. - /// - /// This is equivalent to the Python expression `self(*args, **kwargs)`. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// use pyo3::types::PyDict; - /// - /// const CODE: &str = r#" - /// def function(*args, **kwargs): - /// assert args == ("hello",) - /// assert kwargs == {"cruel": "world"} - /// return "called with args and kwargs" - /// "#; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let module = PyModule::from_code_bound(py, CODE, "", "")?; - /// let fun = module.getattr("function")?; - /// let args = ("hello",); - /// let kwargs = PyDict::new_bound(py); - /// kwargs.set_item("cruel", "world")?; - /// let result = fun.call(args, Some(&kwargs))?; - /// assert_eq!(result.extract::()?, "called with args and kwargs"); - /// Ok(()) - /// }) - /// # } - /// ``` - pub fn call( - &self, - args: impl IntoPy>, - kwargs: Option<&PyDict>, - ) -> PyResult<&PyAny> { - self.as_borrowed() - .call(args, kwargs.map(PyDict::as_borrowed).as_deref()) - .map(Bound::into_gil_ref) - } - - /// Calls the object without arguments. - /// - /// This is equivalent to the Python expression `self()`. - /// - /// # Examples - /// - /// ```no_run - /// use pyo3::prelude::*; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| -> PyResult<()> { - /// let module = PyModule::import_bound(py, "builtins")?; - /// let help = module.getattr("help")?; - /// help.call0()?; - /// Ok(()) - /// })?; - /// # Ok(())} - /// ``` - /// - /// This is equivalent to the Python expression `help()`. - pub fn call0(&self) -> PyResult<&PyAny> { - self.as_borrowed().call0().map(Bound::into_gil_ref) - } - - /// Calls the object with only positional arguments. - /// - /// This is equivalent to the Python expression `self(*args)`. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// - /// const CODE: &str = r#" - /// def function(*args, **kwargs): - /// assert args == ("hello",) - /// assert kwargs == {} - /// return "called with args" - /// "#; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let module = PyModule::from_code_bound(py, CODE, "", "")?; - /// let fun = module.getattr("function")?; - /// let args = ("hello",); - /// let result = fun.call1(args)?; - /// assert_eq!(result.extract::()?, "called with args"); - /// Ok(()) - /// }) - /// # } - /// ``` - pub fn call1(&self, args: impl IntoPy>) -> PyResult<&PyAny> { - self.as_borrowed().call1(args).map(Bound::into_gil_ref) - } - - /// Calls a method on the object. - /// - /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `name`. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// use pyo3::types::PyDict; - /// - /// const CODE: &str = r#" - /// class A: - /// def method(self, *args, **kwargs): - /// assert args == ("hello",) - /// assert kwargs == {"cruel": "world"} - /// return "called with args and kwargs" - /// a = A() - /// "#; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let module = PyModule::from_code_bound(py, CODE, "", "")?; - /// let instance = module.getattr("a")?; - /// let args = ("hello",); - /// let kwargs = PyDict::new_bound(py); - /// kwargs.set_item("cruel", "world")?; - /// let result = instance.call_method("method", args, Some(&kwargs))?; - /// assert_eq!(result.extract::()?, "called with args and kwargs"); - /// Ok(()) - /// }) - /// # } - /// ``` - pub fn call_method(&self, name: N, args: A, kwargs: Option<&PyDict>) -> PyResult<&PyAny> - where - N: IntoPy>, - A: IntoPy>, - { - self.as_borrowed() - .call_method(name, args, kwargs.map(PyDict::as_borrowed).as_deref()) - .map(Bound::into_gil_ref) - } - - /// Calls a method on the object without arguments. - /// - /// This is equivalent to the Python expression `self.name()`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `name`. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// - /// const CODE: &str = r#" - /// class A: - /// def method(self, *args, **kwargs): - /// assert args == () - /// assert kwargs == {} - /// return "called with no arguments" - /// a = A() - /// "#; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let module = PyModule::from_code_bound(py, CODE, "", "")?; - /// let instance = module.getattr("a")?; - /// let result = instance.call_method0("method")?; - /// assert_eq!(result.extract::()?, "called with no arguments"); - /// Ok(()) - /// }) - /// # } - /// ``` - pub fn call_method0(&self, name: N) -> PyResult<&PyAny> - where - N: IntoPy>, - { - self.as_borrowed() - .call_method0(name) - .map(Bound::into_gil_ref) - } - - /// Calls a method on the object with only positional arguments. - /// - /// This is equivalent to the Python expression `self.name(*args)`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used - /// to intern `name`. - /// - /// # Examples - /// - /// ```rust - /// use pyo3::prelude::*; - /// - /// const CODE: &str = r#" - /// class A: - /// def method(self, *args, **kwargs): - /// assert args == ("hello",) - /// assert kwargs == {} - /// return "called with args" - /// a = A() - /// "#; - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let module = PyModule::from_code_bound(py, CODE, "", "")?; - /// let instance = module.getattr("a")?; - /// let args = ("hello",); - /// let result = instance.call_method1("method", args)?; - /// assert_eq!(result.extract::()?, "called with args"); - /// Ok(()) - /// }) - /// # } - /// ``` - pub fn call_method1(&self, name: N, args: A) -> PyResult<&PyAny> - where - N: IntoPy>, - A: IntoPy>, - { - self.as_borrowed() - .call_method1(name, args) - .map(Bound::into_gil_ref) - } - - /// Returns whether the object is considered to be true. - /// - /// This applies truth value testing equivalent to the Python expression `bool(self)`. - pub fn is_truthy(&self) -> PyResult { - self.as_borrowed().is_truthy() - } - - /// Returns whether the object is considered to be None. - /// - /// This is equivalent to the Python expression `self is None`. - #[inline] - pub fn is_none(&self) -> bool { - self.as_borrowed().is_none() - } - - /// Returns true if the sequence or mapping has a length of 0. - /// - /// This is equivalent to the Python expression `len(self) == 0`. - pub fn is_empty(&self) -> PyResult { - self.as_borrowed().is_empty() - } - - /// Gets an item from the collection. - /// - /// This is equivalent to the Python expression `self[key]`. - pub fn get_item(&self, key: K) -> PyResult<&PyAny> - where - K: ToPyObject, - { - self.as_borrowed().get_item(key).map(Bound::into_gil_ref) - } - - /// Sets a collection item value. - /// - /// This is equivalent to the Python expression `self[key] = value`. - pub fn set_item(&self, key: K, value: V) -> PyResult<()> - where - K: ToPyObject, - V: ToPyObject, - { - self.as_borrowed().set_item(key, value) - } - - /// Deletes an item from the collection. - /// - /// This is equivalent to the Python expression `del self[key]`. - pub fn del_item(&self, key: K) -> PyResult<()> - where - K: ToPyObject, - { - self.as_borrowed().del_item(key) - } - - /// Takes an object and returns an iterator for it. - /// - /// This is typically a new iterator but if the argument is an iterator, - /// this returns itself. - pub fn iter(&self) -> PyResult<&PyIterator> { - self.as_borrowed().iter().map(Bound::into_gil_ref) - } - - /// Returns the Python type object for this object's type. - pub fn get_type(&self) -> &PyType { - self.as_borrowed().get_type().into_gil_ref() - } - - /// Returns the Python type pointer for this object. - #[inline] - pub fn get_type_ptr(&self) -> *mut ffi::PyTypeObject { - self.as_borrowed().get_type_ptr() - } - - /// Extracts some type from the Python object. - /// - /// This is a wrapper function around - /// [`FromPyObject::extract()`](crate::FromPyObject::extract). - #[inline] - pub fn extract<'py, D>(&'py self) -> PyResult - where - D: FromPyObjectBound<'py, 'py>, - { - FromPyObjectBound::from_py_object_bound(self.as_borrowed()) - } - - /// Returns the reference count for the Python object. - pub fn get_refcnt(&self) -> isize { - self.as_borrowed().get_refcnt() - } - - /// Computes the "repr" representation of self. - /// - /// This is equivalent to the Python expression `repr(self)`. - pub fn repr(&self) -> PyResult<&PyString> { - self.as_borrowed().repr().map(Bound::into_gil_ref) - } - - /// Computes the "str" representation of self. - /// - /// This is equivalent to the Python expression `str(self)`. - pub fn str(&self) -> PyResult<&PyString> { - self.as_borrowed().str().map(Bound::into_gil_ref) - } - - /// Retrieves the hash code of self. - /// - /// This is equivalent to the Python expression `hash(self)`. - pub fn hash(&self) -> PyResult { - self.as_borrowed().hash() - } - - /// Returns the length of the sequence or mapping. - /// - /// This is equivalent to the Python expression `len(self)`. - pub fn len(&self) -> PyResult { - self.as_borrowed().len() - } - - /// Returns the list of attributes of this object. - /// - /// This is equivalent to the Python expression `dir(self)`. - pub fn dir(&self) -> PyResult<&PyList> { - self.as_borrowed().dir().map(Bound::into_gil_ref) - } - - /// Checks whether this object is an instance of type `ty`. - /// - /// This is equivalent to the Python expression `isinstance(self, ty)`. - #[inline] - pub fn is_instance(&self, ty: &PyAny) -> PyResult { - self.as_borrowed().is_instance(&ty.as_borrowed()) - } - - /// Checks whether this object is an instance of exactly type `ty` (not a subclass). - /// - /// This is equivalent to the Python expression `type(self) is ty`. - #[inline] - pub fn is_exact_instance(&self, ty: &PyAny) -> bool { - self.as_borrowed().is_exact_instance(&ty.as_borrowed()) - } - - /// Checks whether this object is an instance of type `T`. - /// - /// This is equivalent to the Python expression `isinstance(self, T)`, - /// if the type `T` is known at compile time. - #[inline] - pub fn is_instance_of(&self) -> bool { - self.as_borrowed().is_instance_of::() - } - - /// Checks whether this object is an instance of exactly type `T`. - /// - /// This is equivalent to the Python expression `type(self) is T`, - /// if the type `T` is known at compile time. - #[inline] - pub fn is_exact_instance_of(&self) -> bool { - self.as_borrowed().is_exact_instance_of::() - } - - /// Determines if self contains `value`. - /// - /// This is equivalent to the Python expression `value in self`. - pub fn contains(&self, value: V) -> PyResult - where - V: ToPyObject, - { - self.as_borrowed().contains(value) - } - - /// Returns a GIL marker constrained to the lifetime of this type. - #[inline] - pub fn py(&self) -> Python<'_> { - PyNativeType::py(self) - } - - /// Returns the raw FFI pointer represented by self. - /// - /// # Safety - /// - /// Callers are responsible for ensuring that the pointer does not outlive self. - /// - /// The reference is borrowed; callers should not decrease the reference count - /// when they are finished with the pointer. - #[inline] - pub fn as_ptr(&self) -> *mut ffi::PyObject { - ptr_from_ref(self) as *mut ffi::PyObject - } - - /// Returns an owned raw FFI pointer represented by self. - /// - /// # Safety - /// - /// The reference is owned; when finished the caller should either transfer ownership - /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). - #[inline] - pub fn into_ptr(&self) -> *mut ffi::PyObject { - // Safety: self.as_ptr() returns a valid non-null pointer - let ptr = self.as_ptr(); - unsafe { ffi::Py_INCREF(ptr) }; - ptr - } - - /// Return a proxy object that delegates method calls to a parent or sibling class of type. - /// - /// This is equivalent to the Python expression `super()` - #[cfg(not(any(PyPy, GraalPy)))] - pub fn py_super(&self) -> PyResult<&PySuper> { - self.as_borrowed().py_super().map(Bound::into_gil_ref) - } -} - /// This trait represents the Python APIs which are usable on all Python objects. /// /// It is recommended you import this trait via `use pyo3::prelude::*` rather than diff --git a/src/types/datetime.rs b/src/types/datetime.rs index b8a344a60b1..72b3bf886f2 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -171,16 +171,6 @@ pub trait PyTimeAccess { /// Trait for accessing the components of a struct containing a tzinfo. pub trait PyTzInfoAccess<'py> { - /// Deprecated form of `get_tzinfo_bound`. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`get_tzinfo` will be replaced by `get_tzinfo_bound` in a future PyO3 version" - )] - fn get_tzinfo(&self) -> Option<&'py PyTzInfo> { - self.get_tzinfo_bound().map(Bound::into_gil_ref) - } - /// Returns the tzinfo (which may be None). /// /// Implementations should conform to the upstream documentation: diff --git a/src/types/dict.rs b/src/types/dict.rs index 3c731d909e0..1c86b291e2d 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -517,17 +517,6 @@ pub(crate) use borrowed_iter::BorrowedDictIter; /// Conversion trait that allows a sequence of tuples to be converted into `PyDict` /// Primary use case for this trait is `call` and `call_method` methods as keywords argument. pub trait IntoPyDict: Sized { - /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed - /// depends on implementation. - #[cfg(feature = "gil-refs")] - #[deprecated( - since = "0.21.0", - note = "`IntoPyDict::into_py_dict` will be replaced by `IntoPyDict::into_py_dict_bound` in a future PyO3 version" - )] - fn into_py_dict(self, py: Python<'_>) -> &PyDict { - Self::into_py_dict_bound(self, py).into_gil_ref() - } - /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed /// depends on implementation. fn into_py_dict_bound(self, py: Python<'_>) -> Bound<'_, PyDict>; diff --git a/src/types/mod.rs b/src/types/mod.rs index 11d409edfa3..1acd19ed74f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -110,63 +110,12 @@ pub trait DerefToPyAny { // Empty. } -// Implementations core to all native types -#[doc(hidden)] -#[macro_export] -macro_rules! pyobject_native_type_base( - ($name:ty $(;$generics:ident)* ) => { - #[cfg(feature = "gil-refs")] - unsafe impl<$($generics,)*> $crate::PyNativeType for $name { - type AsRefSource = Self; - } - - #[cfg(feature = "gil-refs")] - impl<$($generics,)*> ::std::fmt::Debug for $name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) - -> ::std::result::Result<(), ::std::fmt::Error> - { - use $crate::{PyNativeType, types::{PyAnyMethods, PyStringMethods}}; - let s = self.as_borrowed().repr().or(::std::result::Result::Err(::std::fmt::Error))?; - f.write_str(&s.to_string_lossy()) - } - } - - #[cfg(feature = "gil-refs")] - impl<$($generics,)*> ::std::fmt::Display for $name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) - -> ::std::result::Result<(), ::std::fmt::Error> - { - use $crate::{PyNativeType, types::{PyAnyMethods, PyStringMethods, PyTypeMethods}}; - match self.as_borrowed().str() { - ::std::result::Result::Ok(s) => return f.write_str(&s.to_string_lossy()), - ::std::result::Result::Err(err) => err.write_unraisable_bound(self.py(), ::std::option::Option::Some(&self.as_borrowed())), - } - - match self.as_borrowed().get_type().name() { - ::std::result::Result::Ok(name) => ::std::write!(f, "", name), - ::std::result::Result::Err(_err) => f.write_str(""), - } - } - } - - #[cfg(feature = "gil-refs")] - impl<$($generics,)*> $crate::ToPyObject for $name - { - #[inline] - fn to_object(&self, py: $crate::Python<'_>) -> $crate::PyObject { - unsafe { $crate::PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } - } - }; -); - // Implementations core to all native types except for PyAny (because they don't // make sense on PyAny / have different implementations). #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_named ( ($name:ty $(;$generics:ident)*) => { - $crate::pyobject_native_type_base!($name $(;$generics)*); impl<$($generics,)*> ::std::convert::AsRef<$crate::PyAny> for $name { #[inline] @@ -192,36 +141,6 @@ macro_rules! pyobject_native_type_named ( } } - // FIXME https://github.com/PyO3/pyo3/issues/3903 - #[allow(unknown_lints, non_local_definitions)] - #[cfg(feature = "gil-refs")] - impl<$($generics,)*> $crate::IntoPy<$crate::Py<$name>> for &'_ $name { - #[inline] - fn into_py(self, py: $crate::Python<'_>) -> $crate::Py<$name> { - unsafe { $crate::Py::from_borrowed_ptr(py, self.as_ptr()) } - } - } - - // FIXME https://github.com/PyO3/pyo3/issues/3903 - #[allow(unknown_lints, non_local_definitions)] - #[cfg(feature = "gil-refs")] - impl<$($generics,)*> ::std::convert::From<&'_ $name> for $crate::Py<$name> { - #[inline] - fn from(other: &$name) -> Self { - use $crate::PyNativeType; - unsafe { $crate::Py::from_borrowed_ptr(other.py(), other.as_ptr()) } - } - } - - // FIXME https://github.com/PyO3/pyo3/issues/3903 - #[allow(unknown_lints, non_local_definitions)] - #[cfg(feature = "gil-refs")] - impl<'a, $($generics,)*> ::std::convert::From<&'a $name> for &'a $crate::PyAny { - fn from(ob: &'a $name) -> Self { - unsafe{&*(ob as *const $name as *const $crate::PyAny)} - } - } - impl $crate::types::DerefToPyAny for $name {} }; ); diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index eb9934af949..c2d8d3287c1 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -15,7 +15,7 @@ fn test_compile_errors() { #[cfg(any(not(Py_LIMITED_API), Py_3_11))] t.compile_fail("tests/ui/invalid_pymethods_buffer.rs"); // The output is not stable across abi3 / not abi3 and features - #[cfg(all(not(Py_LIMITED_API), feature = "full", not(feature = "gil-refs")))] + #[cfg(all(not(Py_LIMITED_API), feature = "full"))] t.compile_fail("tests/ui/invalid_pymethods_duplicates.rs"); t.compile_fail("tests/ui/invalid_pymethod_enum.rs"); t.compile_fail("tests/ui/invalid_pymethod_names.rs"); @@ -27,26 +27,18 @@ fn test_compile_errors() { t.compile_fail("tests/ui/invalid_argument_attributes.rs"); t.compile_fail("tests/ui/invalid_frompy_derive.rs"); t.compile_fail("tests/ui/static_ref.rs"); - #[cfg(not(feature = "gil-refs"))] t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); t.compile_fail("tests/ui/invalid_pyfunctions.rs"); t.compile_fail("tests/ui/invalid_pymethods.rs"); // output changes with async feature #[cfg(all(Py_LIMITED_API, feature = "experimental-async"))] t.compile_fail("tests/ui/abi3_nativetype_inheritance.rs"); - #[cfg(not(feature = "gil-refs"))] t.compile_fail("tests/ui/invalid_intern_arg.rs"); t.compile_fail("tests/ui/invalid_frozen_pyclass_borrow.rs"); t.compile_fail("tests/ui/invalid_pymethod_receiver.rs"); t.compile_fail("tests/ui/missing_intopy.rs"); // adding extra error conversion impls changes the output - #[cfg(not(any( - windows, - feature = "eyre", - feature = "anyhow", - feature = "gil-refs", - Py_LIMITED_API - )))] + #[cfg(not(any(windows, feature = "eyre", feature = "anyhow", Py_LIMITED_API)))] t.compile_fail("tests/ui/invalid_result_conversion.rs"); t.compile_fail("tests/ui/not_send.rs"); t.compile_fail("tests/ui/not_send2.rs"); diff --git a/tests/test_super.rs b/tests/test_super.rs index 3647e7d5b23..3362ad57814 100644 --- a/tests/test_super.rs +++ b/tests/test_super.rs @@ -35,7 +35,6 @@ impl SubClass { } fn method_super_new<'py>(self_: &Bound<'py, Self>) -> PyResult> { - #[cfg_attr(not(feature = "gil-refs"), allow(deprecated))] let super_ = PySuper::new_bound(&self_.get_type(), self_)?; super_.call_method("method", (), None) } From 092d4bf6254004ef35e62abdbd4fc96f1fb26ee2 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:22:06 +0200 Subject: [PATCH 2/2] add newsfragment --- newsfragments/4378.removed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4378.removed.md diff --git a/newsfragments/4378.removed.md b/newsfragments/4378.removed.md new file mode 100644 index 00000000000..3c43d46478d --- /dev/null +++ b/newsfragments/4378.removed.md @@ -0,0 +1 @@ +removed deprecated `gil-refs` feature