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

View File

@ -1307,6 +1307,7 @@ struct MyClass {
impl pyo3::types::DerefToPyAny for MyClass {} impl pyo3::types::DerefToPyAny for MyClass {}
# #[allow(deprecated)] # #[allow(deprecated)]
# #[cfg(feature = "gil-refs")]
unsafe impl pyo3::type_object::HasPyGilRef for MyClass { unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
type AsRefTarget = pyo3::PyCell<Self>; type AsRefTarget = pyo3::PyCell<Self>;
} }

View File

@ -1609,7 +1609,7 @@ For more, see [the constructor section](class.md#constructor) of this guide.
<details> <details>
<summary><small>Click to expand</small></summary> <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 ensuring Rust's rules regarding aliasing of references are upheld.
For more detail, see the 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) [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 [`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
[`PyAny`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.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 [`PyBorrowMutError`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyBorrowMutError.html
[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html
[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html

View File

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

View File

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

View File

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3-macros-backend" name = "pyo3-macros-backend"
version = "0.21.2" version = "0.22.0-dev"
description = "Code generation for PyO3 package" description = "Code generation for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"] keywords = ["pyo3", "python", "cpython", "ffi"]
@ -16,7 +16,7 @@ edition = "2021"
[dependencies] [dependencies]
heck = "0.5" heck = "0.5"
proc-macro2 = { version = "1", default-features = false } 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 } quote = { version = "1", default-features = false }
[dependencies.syn] [dependencies.syn]
@ -29,3 +29,4 @@ workspace = true
[features] [features]
experimental-async = [] 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 Ctx { pyo3_path } = ctx;
let mut stmts: Vec<syn::Stmt> = Vec::new(); 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(..) { for mut stmt in func.block.stmts.drain(..) {
if let syn::Stmt::Item(Item::Fn(func)) = &mut stmt { if let syn::Stmt::Item(Item::Fn(func)) = &mut stmt {
if let Some(pyfn_args) = get_pyfn_attr(&mut func.attrs)? { 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 #wrapped_function
{ {
#[allow(unknown_lints, unused_imports, redundant_imports)] #[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())?)?; #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! { ::core::option::Option::None }
}; };
quote! { #[cfg(feature = "gil-refs")]
let has_py_gil_ref = quote! {
#[allow(deprecated)] #[allow(deprecated)]
unsafe impl #pyo3_path::type_object::HasPyGilRef for #cls { unsafe impl #pyo3_path::type_object::HasPyGilRef for #cls {
type AsRefTarget = #pyo3_path::PyCell<Self>; 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 { unsafe impl #pyo3_path::type_object::PyTypeInfo for #cls {
const NAME: &'static str = #cls_name; const NAME: &'static str = #cls_name;

View File

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

View File

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

View File

@ -5,14 +5,12 @@ use crate::inspect::types::TypeInfo;
use crate::pyclass::boolean_struct::False; use crate::pyclass::boolean_struct::False;
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::types::PyTuple; use crate::types::PyTuple;
use crate::{ use crate::{ffi, Borrowed, Bound, Py, PyAny, PyClass, PyObject, PyRef, PyRefMut, Python};
ffi, Borrowed, Bound, Py, PyAny, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
#[cfg(feature = "gil-refs")] #[cfg(feature = "gil-refs")]
use { use {
crate::{ crate::{
err::{self, PyDowncastError}, err::{self, PyDowncastError},
gil, gil, PyNativeType,
}, },
std::ptr::NonNull, 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 /// Implementors are encouraged to implement `extract_bound` and leave this method as the
/// default implementation, which will forward calls to `extract_bound`. /// default implementation, which will forward calls to `extract_bound`.
#[cfg(feature = "gil-refs")]
fn extract(ob: &'py PyAny) -> PyResult<Self> { fn extract(ob: &'py PyAny) -> PyResult<Self> {
Self::extract_bound(&ob.as_borrowed()) Self::extract_bound(&ob.as_borrowed())
} }

View File

@ -3,11 +3,13 @@ use crate::panic::PanicException;
use crate::type_object::PyTypeInfo; use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::types::{string::PyStringMethods, typeobject::PyTypeMethods, PyTraceback, PyType}; use crate::types::{string::PyStringMethods, typeobject::PyTypeMethods, PyTraceback, PyType};
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{ use crate::{
exceptions::{self, PyBaseException}, exceptions::{self, PyBaseException},
ffi, 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::borrow::Cow;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::ffi::CString; 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. /// Error that indicates a failure to convert a PyAny to a more specific Python type.
#[derive(Debug)] #[derive(Debug)]
#[cfg(feature = "gil-refs")]
pub struct PyDowncastError<'a> { pub struct PyDowncastError<'a> {
from: &'a PyAny, from: &'a PyAny,
to: Cow<'static, str>, to: Cow<'static, str>,
} }
#[cfg(feature = "gil-refs")]
impl<'a> PyDowncastError<'a> { impl<'a> PyDowncastError<'a> {
/// Create a new `PyDowncastError` representing a failure to convert the object /// Create a new `PyDowncastError` representing a failure to convert the object
/// `from` into the type named in `to`. /// `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 /// Compatibility API to convert the Bound variant `DowncastError` into the
/// gil-ref variant /// gil-ref variant
#[cfg(feature = "gil-refs")]
pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self { pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self {
#[allow(deprecated)] #[allow(deprecated)]
let from = unsafe { from.py().from_borrowed_ptr(from.as_ptr()) }; 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> {} impl<'a> std::error::Error for PyDowncastError<'a> {}
#[cfg(feature = "gil-refs")]
impl<'a> std::fmt::Display for PyDowncastError<'a> { impl<'a> std::fmt::Display for PyDowncastError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
display_downcast_error(f, &self.from.as_borrowed(), &self.to) 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`, // FIXME remove this: was necessary while `PyTypeInfo` requires `HasPyGilRef`,
// should change in 0.22. // should change in 0.22.
#[cfg(feature = "gil-refs")]
unsafe impl $crate::type_object::HasPyGilRef for $name { unsafe impl $crate::type_object::HasPyGilRef for $name {
type AsRefTarget = $crate::PyAny; type AsRefTarget = $crate::PyAny;
} }

View File

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

View File

@ -1,3 +1,5 @@
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError}, exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError},
ffi, ffi,
@ -7,8 +9,7 @@ use crate::{
pyclass_init::PyObjectInit, pyclass_init::PyObjectInit,
types::any::PyAnyMethods, types::any::PyAnyMethods,
types::PyBool, types::PyBool,
Borrowed, Py, PyAny, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Borrowed, Py, PyAny, PyClass, PyErr, PyMethodDefType, PyResult, PyTypeInfo, Python,
Python,
}; };
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -168,7 +169,12 @@ pub trait PyClassImpl: Sized + 'static {
/// The closest native ancestor. This is `PyAny` by default, and when you declare /// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`. /// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
#[cfg(feature = "gil-refs")]
type BaseNativeType: PyTypeInfo + PyNativeType; 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: /// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing. /// 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::impl_::pycell::PyClassObject;
use crate::pycell::{PyBorrowError, PyBorrowMutError}; use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::{False, True}; use crate::pyclass::boolean_struct::{False, True};
#[cfg(feature = "gil-refs")]
use crate::type_object::HasPyGilRef; use crate::type_object::HasPyGilRef;
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods}; use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
use crate::types::{DerefToPyAny, PyDict, PyString, PyTuple}; use crate::types::{DerefToPyAny, PyDict, PyString, PyTuple};
@ -24,6 +25,7 @@ use std::ptr::NonNull;
/// # Safety /// # Safety
/// ///
/// This trait must only be implemented for types which cannot be accessed without the GIL. /// This trait must only be implemented for types which cannot be accessed without the GIL.
#[cfg(feature = "gil-refs")]
pub unsafe trait PyNativeType: Sized { pub unsafe trait PyNativeType: Sized {
/// The form of this which is stored inside a `Py<T>` smart pointer. /// The form of this which is stored inside a `Py<T>` smart pointer.
type AsRefSource: HasPyGilRef<AsRefTarget = Self>; 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> impl<'py, T> Borrowed<'py, 'py, T>
where where
T: HasPyGilRef, T: HasPyGilRef,
{ {
#[cfg(feature = "gil-refs")]
pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget { pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
// Safety: self is a borrow over `'py`. // Safety: self is a borrow over `'py`.
#[allow(deprecated)] #[allow(deprecated)]
@ -953,6 +955,7 @@ where
} }
} }
#[cfg(feature = "gil-refs")]
impl<T> Py<T> impl<T> Py<T>
where where
T: HasPyGilRef, T: HasPyGilRef,
@ -1000,7 +1003,6 @@ where
/// assert!(my_class_cell.try_borrow().is_ok()); /// assert!(my_class_cell.try_borrow().is_ok());
/// }); /// });
/// ``` /// ```
#[cfg(feature = "gil-refs")]
#[deprecated( #[deprecated(
since = "0.21.0", since = "0.21.0",
note = "use `obj.bind(py)` instead of `obj.as_ref(py)`" note = "use `obj.bind(py)` instead of `obj.as_ref(py)`"
@ -1053,7 +1055,6 @@ where
/// obj.into_ref(py) /// obj.into_ref(py)
/// } /// }
/// ``` /// ```
#[cfg(feature = "gil-refs")]
#[deprecated( #[deprecated(
since = "0.21.0", since = "0.21.0",
note = "use `obj.into_bound(py)` instead of `obj.into_ref(py)`" 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. /// For frozen classes, the simpler [`get`][Self::get] is available.
/// ///
/// Equivalent to `self.as_ref(py).borrow()` - /// Equivalent to `self.bind(py).borrow()` - see [`Bound::borrow`].
/// see [`PyCell::borrow`](crate::pycell::PyCell::borrow).
/// ///
/// # Examples /// # Examples
/// ///
@ -1157,8 +1157,7 @@ where
/// ///
/// This borrow lasts while the returned [`PyRefMut`] exists. /// This borrow lasts while the returned [`PyRefMut`] exists.
/// ///
/// Equivalent to `self.as_ref(py).borrow_mut()` - /// Equivalent to `self.bind(py).borrow_mut()` - see [`Bound::borrow_mut`].
/// see [`PyCell::borrow_mut`](crate::pycell::PyCell::borrow_mut).
/// ///
/// # Examples /// # Examples
/// ///
@ -1202,8 +1201,7 @@ where
/// ///
/// For frozen classes, the simpler [`get`][Self::get] is available. /// For frozen classes, the simpler [`get`][Self::get] is available.
/// ///
/// Equivalent to `self.as_ref(py).borrow_mut()` - /// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`].
/// see [`PyCell::try_borrow`](crate::pycell::PyCell::try_borrow).
#[inline] #[inline]
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> { pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.bind(py).try_borrow() self.bind(py).try_borrow()
@ -1215,8 +1213,7 @@ where
/// ///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
/// ///
/// Equivalent to `self.as_ref(py).try_borrow_mut()` - /// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`].
/// see [`PyCell::try_borrow_mut`](crate::pycell::PyCell::try_borrow_mut).
#[inline] #[inline]
pub fn try_borrow_mut<'py>( pub fn try_borrow_mut<'py>(
&'py self, &'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 impl<T> std::convert::From<&'_ T> for PyObject
where where
T: PyNativeType, T: PyNativeType,
@ -1866,6 +1864,7 @@ where
/// ///
/// However for GIL lifetime reasons, cause() cannot be implemented for `Py<T>`. /// 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. /// 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> impl<T> std::error::Error for Py<T>
where where
T: std::error::Error + PyTypeInfo, T: std::error::Error + PyTypeInfo,
@ -1876,7 +1875,6 @@ where
impl<T> std::fmt::Display for Py<T> impl<T> std::fmt::Display for Py<T>
where where
T: PyTypeInfo, T: PyTypeInfo,
T::AsRefTarget: std::fmt::Display,
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Python::with_gil(|py| std::fmt::Display::fmt(self.bind(py), f)) 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")] #[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::conversion::{FromPyPointer, PyTryFrom, PyTryInto}; pub use crate::conversion::{FromPyPointer, PyTryFrom, PyTryInto};
pub use crate::err::{ #[cfg(feature = "gil-refs")]
DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyErrArguments, PyResult, ToPyErr, pub use crate::err::PyDowncastError;
}; pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr};
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::gil::GILPool; pub use crate::gil::GILPool;
#[cfg(not(any(PyPy, GraalPy)))] #[cfg(not(any(PyPy, GraalPy)))]
pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter}; 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; pub use crate::marker::Python;
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::pycell::PyCell; pub use crate::pycell::PyCell;
pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pycell::{PyRef, PyRefMut};
@ -443,6 +446,7 @@ mod conversions;
pub mod coroutine; pub mod coroutine;
#[macro_use] #[macro_use]
#[doc(hidden)] #[doc(hidden)]
#[cfg(feature = "gil-refs")]
pub mod derive_utils; pub mod derive_utils;
mod err; mod err;
pub mod exceptions; pub mod exceptions;

View File

@ -105,6 +105,7 @@ macro_rules! py_run_impl {
($py:expr, *$dict:expr, $code:expr) => {{ ($py:expr, *$dict:expr, $code:expr) => {{
use ::std::option::Option::*; use ::std::option::Option::*;
#[allow(unused_imports)] #[allow(unused_imports)]
#[cfg(feature = "gil-refs")]
use $crate::PyNativeType; 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.as_borrowed())) {
e.print($py); e.print($py);

View File

@ -15,11 +15,13 @@ pub use crate::conversion::{PyTryFrom, PyTryInto};
pub use crate::err::{PyErr, PyResult}; pub use crate::err::{PyErr, PyResult};
pub use crate::instance::{Borrowed, Bound, Py, PyObject}; pub use crate::instance::{Borrowed, Bound, Py, PyObject};
pub use crate::marker::Python; pub use crate::marker::Python;
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::pycell::PyCell; pub use crate::pycell::PyCell;
pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pycell::{PyRef, PyRefMut};
pub use crate::pyclass_init::PyClassInitializer; pub use crate::pyclass_init::PyClassInitializer;
pub use crate::types::{PyAny, PyModule}; pub use crate::types::{PyAny, PyModule};
#[cfg(feature = "gil-refs")]
pub use crate::PyNativeType; pub use crate::PyNativeType;
#[cfg(feature = "macros")] #[cfg(feature = "macros")]

View File

@ -14,13 +14,13 @@
//! - However, methods and functions in Rust usually *do* need `&mut` references. While PyO3 can //! - 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 //! 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 //! 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. //! similar to std's [`RefCell`](std::cell::RefCell) type.
//! //!
//! # When *not* to use PyCell //! # When *not* to use PyCell
//! //!
//! Usually you can use `&mut` references as method and function receivers and arguments, and you //! 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 //! ```rust
//! use pyo3::prelude::*; //! use pyo3::prelude::*;
@ -39,7 +39,7 @@
//! ``` //! ```
//! //!
//! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more), //! 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 //! ```rust,ignore
//! # use pyo3::prelude::*; //! # use pyo3::prelude::*;
@ -76,7 +76,7 @@
//! # When to use PyCell //! # When to use PyCell
//! ## Using pyclasses from Rust //! ## 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 //! ```rust
//! # use pyo3::prelude::*; //! # use pyo3::prelude::*;
//! # //! #
@ -115,7 +115,7 @@
//! ``` //! ```
//! ## Dealing with possibly overlapping mutable references //! ## 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: //! Suppose the following function that swaps the values of two `Number`s:
//! ``` //! ```
//! # use pyo3::prelude::*; //! # use pyo3::prelude::*;
@ -193,28 +193,30 @@
//! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability" //! [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" //! [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::exceptions::PyRuntimeError;
use crate::ffi_ptr_ext::FfiPtrExt; use crate::ffi_ptr_ext::FfiPtrExt;
use crate::impl_::pyclass::PyClassImpl; use crate::pyclass::{boolean_struct::False, PyClass};
use crate::pyclass::{
boolean_struct::{False, True},
PyClass,
};
use crate::type_object::{PyLayout, PySizedLayout};
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::types::PyAny;
use crate::{ffi, Bound, IntoPy, PyErr, PyNativeType, PyObject, PyTypeCheck, Python};
#[cfg(feature = "gil-refs")] #[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::fmt;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub(crate) mod impl_; pub(crate) mod impl_;
use impl_::PyClassBorrowChecker; #[cfg(feature = "gil-refs")]
use self::impl_::PyClassObject;
use self::impl_::{PyClassObject, PyClassObjectLayout}; use impl_::{PyClassBorrowChecker, PyClassObjectLayout};
/// A container type for (mutably) accessing [`PyClass`] values /// A container type for (mutably) accessing [`PyClass`] values
/// ///
@ -223,7 +225,7 @@ use self::impl_::{PyClassObject, PyClassObjectLayout};
/// # Examples /// # Examples
/// ///
/// This example demonstrates getting a mutable reference of the contained `PyClass`. /// This example demonstrates getting a mutable reference of the contained `PyClass`.
/// ```rust,ignore /// ```rust
/// use pyo3::prelude::*; /// use pyo3::prelude::*;
/// ///
/// #[pyclass] /// #[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 /// For more information on how, when and why (not) to use `PyCell` please see the
/// [module-level documentation](self). /// [module-level documentation](self).
#[cfg_attr( #[cfg(feature = "gil-refs")]
not(feature = "gil-refs"), #[deprecated(
deprecated(
since = "0.21.0", since = "0.21.0",
note = "`PyCell` was merged into `Bound`, use that instead; see the migration guide for more info" note = "`PyCell` was merged into `Bound`, use that instead; see the migration guide for more info"
)
)] )]
#[repr(transparent)] #[repr(transparent)]
pub struct PyCell<T: PyClassImpl>(PyClassObject<T>); pub struct PyCell<T: PyClassImpl>(PyClassObject<T>);
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
unsafe impl<T: PyClass> PyNativeType for PyCell<T> { unsafe impl<T: PyClass> PyNativeType for PyCell<T> {
type AsRefSource = T; type AsRefSource = T;
} }
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
impl<T: PyClass> PyCell<T> { impl<T: PyClass> PyCell<T> {
/// Makes a new `PyCell` on the Python heap and return the reference to it. /// 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 /// 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. /// creation, consider [`Py::new`](crate::Py::new) as a more efficient alternative.
#[cfg(feature = "gil-refs")]
#[deprecated( #[deprecated(
since = "0.21.0", since = "0.21.0",
note = "use `Bound::new(py, value)` or `Py::new(py, value)` instead of `PyCell::new(py, value)`" 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 /// # Examples
/// ///
/// ```ignore /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// #[pyclass] /// #[pyclass]
/// struct Class {} /// struct Class {}
@ -346,7 +347,7 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// # Examples /// # Examples
/// ///
/// ```ignore /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// #[pyclass] /// #[pyclass]
/// struct Class {} /// struct Class {}
@ -379,7 +380,7 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// # Examples /// # Examples
/// ///
/// ```ignore /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// #[pyclass] /// #[pyclass]
/// struct Class {} /// struct Class {}
@ -416,7 +417,7 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// # Examples /// # Examples
/// ///
/// ```ignore /// ```
/// use std::sync::atomic::{AtomicUsize, Ordering}; /// use std::sync::atomic::{AtomicUsize, Ordering};
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// ///
@ -487,11 +488,14 @@ impl<T: PyClass> PyCell<T> {
} }
} }
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {} unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {} impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
impl<T> PyTypeCheck for PyCell<T> impl<T> PyTypeCheck for PyCell<T>
where where
@ -503,7 +507,7 @@ where
<T as PyTypeCheck>::type_check(object) <T as PyTypeCheck>::type_check(object)
} }
} }
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
unsafe impl<T: PyClass> AsPyPointer for PyCell<T> { unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject { 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)] #[allow(deprecated)]
impl<T: PyClass> ToPyObject for &PyCell<T> { impl<T: PyClass> ToPyObject for &PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject { fn to_object(&self, py: Python<'_>) -> PyObject {
@ -542,6 +547,7 @@ impl<T: PyClass> Deref for PyCell<T> {
} }
} }
#[cfg(feature = "gil-refs")]
#[allow(deprecated)] #[allow(deprecated)]
impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> { impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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)] #[allow(deprecated)]
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> { impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> {
type Error = PyBorrowError; 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. /// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: PyClass<Frozen = False>> { 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)] #[allow(deprecated)]
impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>> impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>>
for crate::PyRefMut<'a, 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`. /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowError { 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`. /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowMutError { pub struct PyBorrowMutError {

View File

@ -74,6 +74,7 @@ pub trait PyClassBorrowChecker {
/// Increments immutable borrow count, if possible /// Increments immutable borrow count, if possible
fn try_borrow(&self) -> Result<(), PyBorrowError>; fn try_borrow(&self) -> Result<(), PyBorrowError>;
#[cfg(feature = "gil-refs")]
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>; fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>;
/// Decrements immutable borrow count /// Decrements immutable borrow count
@ -96,6 +97,7 @@ impl PyClassBorrowChecker for EmptySlot {
} }
#[inline] #[inline]
#[cfg(feature = "gil-refs")]
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> { fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
Ok(()) Ok(())
} }
@ -130,6 +132,7 @@ impl PyClassBorrowChecker for BorrowChecker {
} }
} }
#[cfg(feature = "gil-refs")]
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> { fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
let flag = self.0.get(); let flag = self.0.get();
if flag != BorrowFlag::HAS_MUTABLE_BORROW { 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 - /// The `#[pyclass]` attribute implements this trait for your Rust struct -
/// you shouldn't implement this trait directly. /// you shouldn't implement this trait directly.
#[allow(deprecated)] #[allow(deprecated)]
#[cfg(feature = "gil-refs")]
pub trait PyClass: PyTypeInfo<AsRefTarget = crate::PyCell<Self>> + PyClassImpl { pub trait PyClass: PyTypeInfo<AsRefTarget = crate::PyCell<Self>> + PyClassImpl {
/// Whether the pyclass is frozen. /// Whether the pyclass is frozen.
/// ///
@ -23,6 +24,18 @@ pub trait PyClass: PyTypeInfo<AsRefTarget = crate::PyCell<Self>> + PyClassImpl {
type Frozen: 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.
///
/// This can be enabled via `#[pyclass(frozen)]`.
type Frozen: Frozen;
}
/// Operators for the `__richcmp__` method /// Operators for the `__richcmp__` method
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum CompareOp { pub enum CompareOp {

View File

@ -3,7 +3,9 @@
use crate::ffi_ptr_ext::FfiPtrExt; use crate::ffi_ptr_ext::FfiPtrExt;
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyType}; 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. /// `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` /// 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`. /// - `Py<Self>::as_ref` will hand out references to `Self::AsRefTarget`.
/// - `Self::AsRefTarget` must have the same layout as `UnsafeCell<ffi::PyAny>`. /// - `Self::AsRefTarget` must have the same layout as `UnsafeCell<ffi::PyAny>`.
#[cfg(feature = "gil-refs")]
pub unsafe trait HasPyGilRef { pub unsafe trait HasPyGilRef {
/// Utility type to make Py::as_ref work. /// Utility type to make Py::as_ref work.
type AsRefTarget: PyNativeType; type AsRefTarget: PyNativeType;
} }
#[cfg(feature = "gil-refs")]
unsafe impl<T> HasPyGilRef for T unsafe impl<T> HasPyGilRef for T
where where
T: PyNativeType, T: PyNativeType,
@ -54,6 +58,7 @@ where
/// ///
/// Implementations must provide an implementation for `type_object_raw` which infallibly produces a /// Implementations must provide an implementation for `type_object_raw` which infallibly produces a
/// non-null pointer to the corresponding Python type object. /// non-null pointer to the corresponding Python type object.
#[cfg(feature = "gil-refs")]
pub unsafe trait PyTypeInfo: Sized + HasPyGilRef { pub unsafe trait PyTypeInfo: Sized + HasPyGilRef {
/// Class name. /// Class name.
const NAME: &'static str; 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. /// 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 { pub trait PyTypeCheck: HasPyGilRef {
/// Name of self. This is used in error messages, for example. /// Name of self. This is used in error messages, for example.
const NAME: &'static str; const NAME: &'static str;
@ -143,6 +203,18 @@ pub trait PyTypeCheck: HasPyGilRef {
fn type_check(object: &Bound<'_, PyAny>) -> bool; 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 impl<T> PyTypeCheck for T
where where
T: PyTypeInfo, T: PyTypeInfo,

View File

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

View File

@ -122,10 +122,12 @@ pub trait DerefToPyAny {
#[macro_export] #[macro_export]
macro_rules! pyobject_native_type_base( macro_rules! pyobject_native_type_base(
($name:ty $(;$generics:ident)* ) => { ($name:ty $(;$generics:ident)* ) => {
#[cfg(feature = "gil-refs")]
unsafe impl<$($generics,)*> $crate::PyNativeType for $name { unsafe impl<$($generics,)*> $crate::PyNativeType for $name {
type AsRefSource = Self; type AsRefSource = Self;
} }
#[cfg(feature = "gil-refs")]
impl<$($generics,)*> ::std::fmt::Debug for $name { impl<$($generics,)*> ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
-> ::std::result::Result<(), ::std::fmt::Error> -> ::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 { impl<$($generics,)*> ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
-> ::std::result::Result<(), ::std::fmt::Error> -> ::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))] #[cfg(any(not(Py_LIMITED_API), Py_3_11))]
t.compile_fail("tests/ui/invalid_pymethods_buffer.rs"); t.compile_fail("tests/ui/invalid_pymethods_buffer.rs");
// The output is not stable across abi3 / not abi3 and features // 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_pymethods_duplicates.rs");
t.compile_fail("tests/ui/invalid_pymethod_enum.rs"); t.compile_fail("tests/ui/invalid_pymethod_enum.rs");
t.compile_fail("tests/ui/invalid_pymethod_names.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_argument_attributes.rs");
t.compile_fail("tests/ui/invalid_frompy_derive.rs"); t.compile_fail("tests/ui/invalid_frompy_derive.rs");
t.compile_fail("tests/ui/static_ref.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/wrong_aspyref_lifetimes.rs");
t.compile_fail("tests/ui/invalid_pyfunctions.rs"); t.compile_fail("tests/ui/invalid_pyfunctions.rs");
t.compile_fail("tests/ui/invalid_pymethods.rs"); t.compile_fail("tests/ui/invalid_pymethods.rs");
// output changes with async feature // output changes with async feature
#[cfg(all(Py_LIMITED_API, feature = "experimental-async"))] #[cfg(all(Py_LIMITED_API, feature = "experimental-async"))]
t.compile_fail("tests/ui/abi3_nativetype_inheritance.rs"); 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_intern_arg.rs");
t.compile_fail("tests/ui/invalid_frozen_pyclass_borrow.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/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( 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 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 --> 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()); 5 | Python::with_gil(|py| py.import_bound(pyo3::intern!(py, _foo)).unwrap());
| --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | | | | |
| | return type of closure is pyo3::Bound<'2, pyo3::prelude::PyModule> | | return type of closure is pyo3::Bound<'2, PyModule>
| has type `pyo3::Python<'1>` | has type `Python<'1>`

View File

@ -26,23 +26,6 @@ error[E0277]: the trait bound `TwoNew: PyTypeInfo` is not satisfied
PyDate PyDate
and $N others 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____` error[E0592]: duplicate definitions with name `__pymethod___new____`
--> tests/ui/invalid_pymethods_duplicates.rs:8:1 --> 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` | --- ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | | | | |
| | return type of closure is &'2 pyo3::Bound<'_, PyDict> | | return type of closure is &'2 pyo3::Bound<'_, PyDict>
| has type `pyo3::Python<'1>` | has type `Python<'1>`