feature gate `PyCell` (#4177)

* feature gate `PyCell`

* feature gate `HasPyGilRef` completely

* bump version
This commit is contained in:
Icxolu 2024-05-12 20:30:08 +02:00 committed by GitHub
parent 57500d9b09
commit 10152a7078
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 220 additions and 98 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3"
version = "0.21.2"
version = "0.22.0-dev"
description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
readme = "README.md"
@ -20,10 +20,10 @@ libc = "0.2.62"
memoffset = "0.9"
# ffi bindings to the python interpreter, split into a separate crate so they can be used independently
pyo3-ffi = { path = "pyo3-ffi", version = "=0.21.2" }
pyo3-ffi = { path = "pyo3-ffi", version = "=0.22.0-dev" }
# support crates for macros feature
pyo3-macros = { path = "pyo3-macros", version = "=0.21.2", optional = true }
pyo3-macros = { path = "pyo3-macros", version = "=0.22.0-dev", optional = true }
indoc = { version = "2.0.1", optional = true }
unindent = { version = "0.2.1", optional = true }
@ -62,7 +62,7 @@ rayon = "1.6.1"
futures = "0.3.28"
[build-dependencies]
pyo3-build-config = { path = "pyo3-build-config", version = "=0.21.2", features = ["resolve-config"] }
pyo3-build-config = { path = "pyo3-build-config", version = "=0.22.0-dev", features = ["resolve-config"] }
[features]
default = ["macros"]
@ -106,7 +106,7 @@ generate-import-lib = ["pyo3-ffi/generate-import-lib"]
auto-initialize = []
# Allows use of the deprecated "GIL Refs" APIs.
gil-refs = []
gil-refs = ["pyo3-macros/gil-refs"]
# Enables `Clone`ing references to Python objects `Py<T>` which panics if the GIL is not held.
py-clone = []

View File

@ -1307,6 +1307,7 @@ 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<Self>;
}

View File

@ -1609,7 +1609,7 @@ For more, see [the constructor section](class.md#constructor) of this guide.
<details>
<summary><small>Click to expand</small></summary>
PyO3 0.9 introduces [`PyCell`], which is a [`RefCell`]-like object wrapper
PyO3 0.9 introduces `PyCell`, which is a [`RefCell`]-like object wrapper
for ensuring Rust's rules regarding aliasing of references are upheld.
For more detail, see the
[Rust Book's section on Rust's rules of references](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references)
@ -1788,7 +1788,6 @@ impl PySequenceProtocol for ByteSequence {
[`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
[`PyAny`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html
[`PyCell`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyCell.html
[`PyBorrowMutError`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyBorrowMutError.html
[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html
[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-build-config"
version = "0.21.2"
version = "0.22.0-dev"
description = "Build configuration for the PyO3 ecosystem"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-ffi"
version = "0.21.2"
version = "0.22.0-dev"
description = "Python-API bindings for the PyO3 ecosystem"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -38,7 +38,7 @@ abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312"]
generate-import-lib = ["pyo3-build-config/python3-dll-a"]
[build-dependencies]
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.21.2", features = ["resolve-config"] }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.22.0-dev", features = ["resolve-config"] }
[lints]
workspace = true

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-macros-backend"
version = "0.21.2"
version = "0.22.0-dev"
description = "Code generation for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -16,7 +16,7 @@ edition = "2021"
[dependencies]
heck = "0.5"
proc-macro2 = { version = "1", default-features = false }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.21.2", features = ["resolve-config"] }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.22.0-dev", features = ["resolve-config"] }
quote = { version = "1", default-features = false }
[dependencies.syn]
@ -29,3 +29,4 @@ workspace = true
[features]
experimental-async = []
gil-refs = []

View File

@ -384,6 +384,11 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn
let Ctx { pyo3_path } = ctx;
let mut stmts: Vec<syn::Stmt> = 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)? {
@ -394,7 +399,7 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn
#wrapped_function
{
#[allow(unknown_lints, unused_imports, redundant_imports)]
use #pyo3_path::{PyNativeType, types::PyModuleMethods};
#imports
#module_name.as_borrowed().add_function(#pyo3_path::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?;
}
};

View File

@ -1307,11 +1307,19 @@ fn impl_pytypeinfo(
quote! { ::core::option::Option::None }
};
quote! {
#[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<Self>;
}
};
#[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;

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-macros"
version = "0.21.2"
version = "0.22.0-dev"
description = "Proc macros for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -17,12 +17,13 @@ proc-macro = true
multiple-pymethods = []
experimental-async = ["pyo3-macros-backend/experimental-async"]
experimental-declarative-modules = []
gil-refs = ["pyo3-macros-backend/gil-refs"]
[dependencies]
proc-macro2 = { version = "1", default-features = false }
quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] }
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.21.2" }
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.22.0-dev" }
[lints]
workspace = true

View File

@ -3,7 +3,7 @@
[tool.towncrier]
filename = "CHANGELOG.md"
version = "0.21.2"
version = "0.22.0-dev"
start_string = "<!-- towncrier release notes start -->\n"
template = ".towncrier.template.md"
title_format = "## [{version}] - {project_date}"

View File

@ -5,14 +5,12 @@ use crate::inspect::types::TypeInfo;
use crate::pyclass::boolean_struct::False;
use crate::types::any::PyAnyMethods;
use crate::types::PyTuple;
use crate::{
ffi, Borrowed, Bound, Py, PyAny, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
use crate::{ffi, Borrowed, Bound, Py, PyAny, PyClass, PyObject, PyRef, PyRefMut, Python};
#[cfg(feature = "gil-refs")]
use {
crate::{
err::{self, PyDowncastError},
gil,
gil, PyNativeType,
},
std::ptr::NonNull,
};
@ -221,6 +219,7 @@ pub trait FromPyObject<'py>: Sized {
///
/// 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> {
Self::extract_bound(&ob.as_borrowed())
}

View File

@ -3,11 +3,13 @@ use crate::panic::PanicException;
use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::{string::PyStringMethods, typeobject::PyTypeMethods, PyTraceback, PyType};
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{
exceptions::{self, PyBaseException},
ffi,
};
use crate::{Borrowed, IntoPy, Py, PyAny, PyNativeType, PyObject, Python, ToPyObject};
use crate::{Borrowed, IntoPy, Py, PyAny, PyObject, Python, ToPyObject};
use std::borrow::Cow;
use std::cell::UnsafeCell;
use std::ffi::CString;
@ -47,11 +49,13 @@ pub type PyResult<T> = Result<T, PyErr>;
/// Error that indicates a failure to convert a PyAny to a more specific Python type.
#[derive(Debug)]
#[cfg(feature = "gil-refs")]
pub struct PyDowncastError<'a> {
from: &'a PyAny,
to: Cow<'static, str>,
}
#[cfg(feature = "gil-refs")]
impl<'a> PyDowncastError<'a> {
/// Create a new `PyDowncastError` representing a failure to convert the object
/// `from` into the type named in `to`.
@ -64,7 +68,6 @@ impl<'a> PyDowncastError<'a> {
/// Compatibility API to convert the Bound variant `DowncastError` into the
/// gil-ref variant
#[cfg(feature = "gil-refs")]
pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self {
#[allow(deprecated)]
let from = unsafe { from.py().from_borrowed_ptr(from.as_ptr()) };
@ -1012,8 +1015,10 @@ impl<'a> std::convert::From<PyDowncastError<'a>> for PyErr {
}
}
#[cfg(feature = "gil-refs")]
impl<'a> std::error::Error for PyDowncastError<'a> {}
#[cfg(feature = "gil-refs")]
impl<'a> std::fmt::Display for PyDowncastError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
display_downcast_error(f, &self.from.as_borrowed(), &self.to)

View File

@ -146,6 +146,7 @@ macro_rules! import_exception_bound {
// 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;
}

View File

@ -19,6 +19,7 @@ pub struct NotAGilRef<T>(std::marker::PhantomData<T>);
pub trait IsGilRef {}
#[cfg(feature = "gil-refs")]
impl<T: crate::PyNativeType> IsGilRef for &'_ T {}
impl<T> GilRefs<T> {

View File

@ -1,3 +1,5 @@
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{
exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError},
ffi,
@ -7,8 +9,7 @@ use crate::{
pyclass_init::PyObjectInit,
types::any::PyAnyMethods,
types::PyBool,
Borrowed, Py, PyAny, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo,
Python,
Borrowed, Py, PyAny, PyClass, PyErr, PyMethodDefType, PyResult, PyTypeInfo, Python,
};
use std::{
borrow::Cow,
@ -168,7 +169,12 @@ 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:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.

View File

@ -2,6 +2,7 @@ use crate::err::{self, PyErr, PyResult};
use crate::impl_::pycell::PyClassObject;
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};
@ -24,6 +25,7 @@ use std::ptr::NonNull;
/// # 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<T>` smart pointer.
type AsRefSource: HasPyGilRef<AsRefTarget = Self>;
@ -666,11 +668,11 @@ impl<'a, 'py, T> From<&'a Bound<'py, T>> for Borrowed<'a, 'py, T> {
}
}
#[cfg(feature = "gil-refs")]
impl<'py, T> Borrowed<'py, 'py, T>
where
T: HasPyGilRef,
{
#[cfg(feature = "gil-refs")]
pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
// Safety: self is a borrow over `'py`.
#[allow(deprecated)]
@ -953,6 +955,7 @@ where
}
}
#[cfg(feature = "gil-refs")]
impl<T> Py<T>
where
T: HasPyGilRef,
@ -1000,7 +1003,6 @@ where
/// assert!(my_class_cell.try_borrow().is_ok());
/// });
/// ```
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "use `obj.bind(py)` instead of `obj.as_ref(py)`"
@ -1053,7 +1055,6 @@ where
/// obj.into_ref(py)
/// }
/// ```
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "use `obj.into_bound(py)` instead of `obj.into_ref(py)`"
@ -1118,8 +1119,7 @@ where
///
/// For frozen classes, the simpler [`get`][Self::get] is available.
///
/// Equivalent to `self.as_ref(py).borrow()` -
/// see [`PyCell::borrow`](crate::pycell::PyCell::borrow).
/// Equivalent to `self.bind(py).borrow()` - see [`Bound::borrow`].
///
/// # Examples
///
@ -1157,8 +1157,7 @@ where
///
/// This borrow lasts while the returned [`PyRefMut`] exists.
///
/// Equivalent to `self.as_ref(py).borrow_mut()` -
/// see [`PyCell::borrow_mut`](crate::pycell::PyCell::borrow_mut).
/// Equivalent to `self.bind(py).borrow_mut()` - see [`Bound::borrow_mut`].
///
/// # Examples
///
@ -1202,8 +1201,7 @@ where
///
/// For frozen classes, the simpler [`get`][Self::get] is available.
///
/// Equivalent to `self.as_ref(py).borrow_mut()` -
/// see [`PyCell::try_borrow`](crate::pycell::PyCell::try_borrow).
/// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`].
#[inline]
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.bind(py).try_borrow()
@ -1215,8 +1213,7 @@ where
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
///
/// Equivalent to `self.as_ref(py).try_borrow_mut()` -
/// see [`PyCell::try_borrow_mut`](crate::pycell::PyCell::try_borrow_mut).
/// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`].
#[inline]
pub fn try_borrow_mut<'py>(
&'py self,
@ -1742,6 +1739,7 @@ unsafe impl<T> crate::AsPyPointer for Py<T> {
}
}
#[cfg(feature = "gil-refs")]
impl<T> std::convert::From<&'_ T> for PyObject
where
T: PyNativeType,
@ -1866,6 +1864,7 @@ where
///
/// However for GIL lifetime reasons, cause() cannot be implemented for `Py<T>`.
/// Use .as_ref() to get the GIL-scoped error if you need to inspect the cause.
#[cfg(feature = "gil-refs")]
impl<T> std::error::Error for Py<T>
where
T: std::error::Error + PyTypeInfo,
@ -1876,7 +1875,6 @@ where
impl<T> std::fmt::Display for Py<T>
where
T: PyTypeInfo,
T::AsRefTarget: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Python::with_gil(|py| std::fmt::Display::fmt(self.bind(py), f))

View File

@ -320,15 +320,18 @@ pub use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, ToPyObject};
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
pub use crate::conversion::{FromPyPointer, PyTryFrom, PyTryInto};
pub use crate::err::{
DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyErrArguments, PyResult, ToPyErr,
};
#[cfg(feature = "gil-refs")]
pub use crate::err::PyDowncastError;
pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr};
#[allow(deprecated)]
pub use crate::gil::GILPool;
#[cfg(not(any(PyPy, GraalPy)))]
pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter};
pub use crate::instance::{Borrowed, Bound, Py, PyNativeType, PyObject};
#[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};
@ -443,6 +446,7 @@ mod conversions;
pub mod coroutine;
#[macro_use]
#[doc(hidden)]
#[cfg(feature = "gil-refs")]
pub mod derive_utils;
mod err;
pub mod exceptions;

View File

@ -105,6 +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())) {
e.print($py);

View File

@ -15,11 +15,13 @@ pub use crate::conversion::{PyTryFrom, PyTryInto};
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")]

View File

@ -14,13 +14,13 @@
//! - However, methods and functions in Rust usually *do* need `&mut` references. While PyO3 can
//! use the [`Python<'py>`](crate::Python) token to guarantee thread-safe access to them, it cannot
//! statically guarantee uniqueness of `&mut` references. As such those references have to be tracked
//! dynamically at runtime, using [`PyCell`] and the other types defined in this module. This works
//! dynamically at runtime, using `PyCell` and the other types defined in this module. This works
//! similar to std's [`RefCell`](std::cell::RefCell) type.
//!
//! # When *not* to use PyCell
//!
//! Usually you can use `&mut` references as method and function receivers and arguments, and you
//! won't need to use [`PyCell`] directly:
//! won't need to use `PyCell` directly:
//!
//! ```rust
//! use pyo3::prelude::*;
@ -39,7 +39,7 @@
//! ```
//!
//! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more),
//! using [`PyCell`] under the hood:
//! using `PyCell` under the hood:
//!
//! ```rust,ignore
//! # use pyo3::prelude::*;
@ -76,7 +76,7 @@
//! # When to use PyCell
//! ## Using pyclasses from Rust
//!
//! However, we *do* need [`PyCell`] if we want to call its methods from Rust:
//! However, we *do* need `PyCell` if we want to call its methods from Rust:
//! ```rust
//! # use pyo3::prelude::*;
//! #
@ -115,7 +115,7 @@
//! ```
//! ## Dealing with possibly overlapping mutable references
//!
//! It is also necessary to use [`PyCell`] if you can receive mutable arguments that may overlap.
//! It is also necessary to use `PyCell` if you can receive mutable arguments that may overlap.
//! Suppose the following function that swaps the values of two `Number`s:
//! ```
//! # use pyo3::prelude::*;
@ -193,28 +193,30 @@
//! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability"
//! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language"
use crate::conversion::{AsPyPointer, ToPyObject};
use crate::conversion::AsPyPointer;
use crate::exceptions::PyRuntimeError;
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::impl_::pyclass::PyClassImpl;
use crate::pyclass::{
boolean_struct::{False, True},
PyClass,
};
use crate::type_object::{PyLayout, PySizedLayout};
use crate::pyclass::{boolean_struct::False, PyClass};
use crate::types::any::PyAnyMethods;
use crate::types::PyAny;
use crate::{ffi, Bound, IntoPy, PyErr, PyNativeType, PyObject, PyTypeCheck, Python};
#[cfg(feature = "gil-refs")]
use crate::{pyclass_init::PyClassInitializer, PyResult};
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_;
use impl_::PyClassBorrowChecker;
use self::impl_::{PyClassObject, PyClassObjectLayout};
#[cfg(feature = "gil-refs")]
use self::impl_::PyClassObject;
use impl_::{PyClassBorrowChecker, PyClassObjectLayout};
/// A container type for (mutably) accessing [`PyClass`] values
///
@ -223,7 +225,7 @@ use self::impl_::{PyClassObject, PyClassObjectLayout};
/// # Examples
///
/// This example demonstrates getting a mutable reference of the contained `PyClass`.
/// ```rust,ignore
/// ```rust
/// use pyo3::prelude::*;
///
/// #[pyclass]
@ -252,28 +254,27 @@ use self::impl_::{PyClassObject, PyClassObjectLayout};
/// ```
/// For more information on how, when and why (not) to use `PyCell` please see the
/// [module-level documentation](self).
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyCell` was merged into `Bound`, use that instead; see the migration guide for more info"
)
#[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<T: PyClassImpl>(PyClassObject<T>);
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
unsafe impl<T: PyClass> PyNativeType for PyCell<T> {
type AsRefSource = T;
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<T: PyClass> PyCell<T> {
/// 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.
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "use `Bound::new(py, value)` or `Py::new(py, value)` instead of `PyCell::new(py, value)`"
@ -316,7 +317,7 @@ impl<T: PyClass> PyCell<T> {
///
/// # Examples
///
/// ```ignore
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
@ -346,7 +347,7 @@ impl<T: PyClass> PyCell<T> {
///
/// # Examples
///
/// ```ignore
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
@ -379,7 +380,7 @@ impl<T: PyClass> PyCell<T> {
///
/// # Examples
///
/// ```ignore
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
@ -416,7 +417,7 @@ impl<T: PyClass> PyCell<T> {
///
/// # Examples
///
/// ```ignore
/// ```
/// use std::sync::atomic::{AtomicUsize, Ordering};
/// # use pyo3::prelude::*;
///
@ -487,11 +488,14 @@ impl<T: PyClass> PyCell<T> {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<T> PyTypeCheck for PyCell<T>
where
@ -503,7 +507,7 @@ where
<T as PyTypeCheck>::type_check(object)
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
@ -511,6 +515,7 @@ unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<T: PyClass> ToPyObject for &PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
@ -542,6 +547,7 @@ impl<T: PyClass> Deref for PyCell<T> {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -768,6 +774,7 @@ impl<T: PyClass> IntoPy<PyObject> for &'_ PyRef<'_, T> {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> {
type Error = PyBorrowError;
@ -788,7 +795,7 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
}
}
/// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`.
/// A wrapper type for a mutably borrowed value from a [`Bound<'py, T>`].
///
/// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: PyClass<Frozen = False>> {
@ -928,6 +935,7 @@ unsafe impl<'a, T: PyClass<Frozen = False>> AsPyPointer for PyRefMut<'a, T> {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>>
for crate::PyRefMut<'a, T>
@ -944,7 +952,7 @@ impl<T: PyClass<Frozen = False> + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
}
}
/// An error type returned by [`PyCell::try_borrow`].
/// An error type returned by [`Bound::try_borrow`].
///
/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowError {
@ -969,7 +977,7 @@ impl From<PyBorrowError> for PyErr {
}
}
/// An error type returned by [`PyCell::try_borrow_mut`].
/// An error type returned by [`Bound::try_borrow_mut`].
///
/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowMutError {

View File

@ -74,6 +74,7 @@ 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
@ -96,6 +97,7 @@ impl PyClassBorrowChecker for EmptySlot {
}
#[inline]
#[cfg(feature = "gil-refs")]
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
Ok(())
}
@ -130,6 +132,7 @@ 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 {

View File

@ -16,6 +16,7 @@ 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<AsRefTarget = crate::PyCell<Self>> + PyClassImpl {
/// Whether the pyclass is frozen.
///
@ -23,6 +24,18 @@ pub trait PyClass: PyTypeInfo<AsRefTarget = crate::PyCell<Self>> + PyClassImpl {
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.
///
/// This can be enabled via `#[pyclass(frozen)]`.
type Frozen: Frozen;
}
/// Operators for the `__richcmp__` method
#[derive(Debug, Clone, Copy)]
pub enum CompareOp {

View File

@ -3,7 +3,9 @@
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyType};
use crate::{ffi, Bound, PyNativeType, Python};
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{ffi, Bound, Python};
/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap.
/// E.g., `PyClassObject` is a concrete representation of all `pyclass`es, and `ffi::PyObject`
@ -29,11 +31,13 @@ pub trait PySizedLayout<T>: PyLayout<T> + Sized {}
///
/// - `Py<Self>::as_ref` will hand out references to `Self::AsRefTarget`.
/// - `Self::AsRefTarget` must have the same layout as `UnsafeCell<ffi::PyAny>`.
#[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<T> HasPyGilRef for T
where
T: PyNativeType,
@ -54,6 +58,7 @@ where
///
/// 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;
@ -132,7 +137,62 @@ pub unsafe trait PyTypeInfo: Sized + HasPyGilRef {
}
}
/// 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(not(feature = "gil-refs"))]
pub unsafe trait PyTypeInfo: Sized {
/// 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]
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::<ffi::PyObject>()
.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]
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]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) }
}
}
/// Implemented by types which can be used as a concrete Python type inside `Py<T>` 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;
@ -143,6 +203,18 @@ pub trait PyTypeCheck: HasPyGilRef {
fn type_check(object: &Bound<'_, PyAny>) -> bool;
}
/// Implemented by types which can be used as a concrete Python type inside `Py<T>` 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;
/// 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;
}
impl<T> PyTypeCheck for T
where
T: PyTypeInfo,

View File

@ -1646,7 +1646,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// Extracts some type from the Python object.
///
/// This is a wrapper function around
/// [`FromPyObject::extract()`](crate::FromPyObject::extract).
/// [`FromPyObject::extract_bound()`](crate::FromPyObject::extract_bound).
fn extract<'a, T>(&'a self) -> PyResult<T>
where
T: FromPyObjectBound<'a, 'py>;

View File

@ -122,10 +122,12 @@ pub trait DerefToPyAny {
#[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>
@ -136,6 +138,7 @@ macro_rules! pyobject_native_type_base(
}
}
#[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>

View File

@ -14,7 +14,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"))]
#[cfg(all(not(Py_LIMITED_API), feature = "full", not(feature = "gil-refs")))]
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,12 +27,14 @@ 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");

View File

@ -34,6 +34,12 @@ error: use of deprecated constant `__pyfunction_pyfunction_option_4::SIGNATURE`:
138 | fn pyfunction_option_4(
| ^^^^^^^^^^^^^^^^^^^
error: use of deprecated struct `pyo3::PyCell`: `PyCell` was merged into `Bound`, use that instead; see the migration guide for more info
--> tests/ui/deprecations.rs:23:30
|
23 | fn method_gil_ref(_slf: &PyCell<Self>) {}
| ^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:45:44
|

View File

@ -13,5 +13,5 @@ error: lifetime may not live long enough
5 | Python::with_gil(|py| py.import_bound(pyo3::intern!(py, _foo)).unwrap());
| --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is pyo3::Bound<'2, pyo3::prelude::PyModule>
| has type `pyo3::Python<'1>`
| | return type of closure is pyo3::Bound<'2, PyModule>
| has type `Python<'1>`

View File

@ -26,23 +26,6 @@ error[E0277]: the trait bound `TwoNew: PyTypeInfo` is not satisfied
PyDate
and $N others
error[E0277]: the trait bound `TwoNew: HasPyGilRef` is not satisfied
--> tests/ui/invalid_pymethods_duplicates.rs:9:6
|
9 | impl TwoNew {
| ^^^^^^ the trait `PyNativeType` is not implemented for `TwoNew`, which is required by `TwoNew: HasPyGilRef`
|
= help: the trait `HasPyGilRef` is implemented for `pyo3::coroutine::Coroutine`
= note: required for `TwoNew` to implement `HasPyGilRef`
note: required by a bound in `pyo3::PyTypeInfo::NAME`
--> src/type_object.rs
|
| pub unsafe trait PyTypeInfo: Sized + HasPyGilRef {
| ^^^^^^^^^^^ required by this bound in `PyTypeInfo::NAME`
| /// Class name.
| const NAME: &'static str;
| ---- required by a bound in this associated constant
error[E0592]: duplicate definitions with name `__pymethod___new____`
--> tests/ui/invalid_pymethods_duplicates.rs:8:1
|

View File

@ -5,4 +5,4 @@ error: lifetime may not live long enough
| --- ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 pyo3::Bound<'_, PyDict>
| has type `pyo3::Python<'1>`
| has type `Python<'1>`