diff --git a/guide/src/free-threading.md b/guide/src/free-threading.md index ac38a236723..2ce6ff955a3 100644 --- a/guide/src/free-threading.md +++ b/guide/src/free-threading.md @@ -225,16 +225,15 @@ Python::attach(|py| { }); ``` -### `GILProtected` is not exposed +### `GILProtected` has been removed -[`GILProtected`] is a (deprecated) PyO3 type that allows mutable access to static data by leveraging the GIL to lock concurrent access from other threads. -In free-threaded Python there is no GIL, so you will need to replace this type with some other form of locking. -In many cases, a type from [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/) or a [`std::sync::Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) will be sufficient. +[`GILProtected`] was a PyO3 type that allowed mutable access to static data by leveraging the GIL to lock concurrent access from other threads. +In free-threaded Python there is no GIL, so this type had to be replaced with alternative forms of locking. +In many cases, a type from [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/) or a [`std::sync::Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) was sufficient. Before: -```rust -# #![allow(deprecated)] +```rust,ignore # fn main() { # #[cfg(not(Py_GIL_DISABLED))] { # use pyo3::prelude::*; @@ -254,7 +253,7 @@ Python::attach(|py| { # }} ``` -After: +After (using a `Mutex`): ```rust # use pyo3::prelude::*; diff --git a/guide/src/migration.md b/guide/src/migration.md index 70463842334..6085aec1dc3 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -266,8 +266,7 @@ It exposes the same API as `GILOnceCell`, so should be a drop-in replacement wit Before: -```rust -# #![allow(deprecated)] +```rust,ignore # use pyo3::prelude::*; # use pyo3::sync::GILOnceCell; # use pyo3::types::PyType; @@ -307,8 +306,7 @@ Prefer to use concurrency primitives which are compatible with free-threaded Pyt Before: -```rust -# #![allow(deprecated)] +```rust,ignore # use pyo3::prelude::*; # fn main() { # #[cfg(not(Py_GIL_DISABLED))] { @@ -494,8 +492,7 @@ impl ToPyObject for MyPyObjectWrapper { After: -```rust,no_run -# #![allow(deprecated)] +```rust,ignore # use pyo3::prelude::*; # #[allow(dead_code)] # struct MyPyObjectWrapper(PyObject); diff --git a/newsfragments/5740.removed.md b/newsfragments/5740.removed.md new file mode 100644 index 00000000000..9632877a8c6 --- /dev/null +++ b/newsfragments/5740.removed.md @@ -0,0 +1 @@ +Remove all functionality deprecated in PyO3 0.25 and 0.26. diff --git a/src/instance.rs b/src/instance.rs index 91a6c21dd8b..ce96b4aa4ce 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -2288,15 +2288,6 @@ impl std::fmt::Debug for Py { } } -/// A commonly-used alias for `Py`. -/// -/// This is an owned reference a Python object without any type information. This value can also be -/// safely sent between threads. -/// -/// See the documentation for [`Py`](struct.Py.html). -#[deprecated(since = "0.26.0", note = "use `Py` instead")] -pub type PyObject = Py; - impl Py { /// Downcast this `Py` to a concrete Python type or pyclass. /// diff --git a/src/interpreter_lifecycle.rs b/src/interpreter_lifecycle.rs index 0ffbff7f0d4..a064e53967d 100644 --- a/src/interpreter_lifecycle.rs +++ b/src/interpreter_lifecycle.rs @@ -19,14 +19,6 @@ pub(crate) fn initialize() { }); } -/// See [Python::initialize] -#[cfg(not(any(PyPy, GraalPy)))] -#[inline] -#[deprecated(note = "use `Python::initialize` instead", since = "0.26.0")] -pub fn prepare_freethreaded_python() { - initialize(); -} - /// Executes the provided closure with an embedded Python interpreter. /// /// This function initializes the Python interpreter, executes the provided closure, and then diff --git a/src/lib.rs b/src/lib.rs index ed9ed27698d..41307496b75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -341,14 +341,9 @@ pub use crate::conversion::{FromPyObject, IntoPyObject, IntoPyObjectExt}; pub use crate::err::{CastError, CastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr}; #[allow(deprecated)] pub use crate::err::{DowncastError, DowncastIntoError}; -#[allow(deprecated)] -pub use crate::instance::PyObject; pub use crate::instance::{Borrowed, Bound, BoundObject, Py}; #[cfg(not(any(PyPy, GraalPy)))] -#[allow(deprecated)] -pub use crate::interpreter_lifecycle::{ - prepare_freethreaded_python, with_embedded_python_interpreter, -}; +pub use crate::interpreter_lifecycle::with_embedded_python_interpreter; pub use crate::marker::Python; pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pyclass::{PyClass, PyClassGuard, PyClassGuardMut}; diff --git a/src/marker.rs b/src/marker.rs index 06289f9bcc5..24604e87f25 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -357,17 +357,6 @@ pub struct Python<'py>(PhantomData<&'py AttachGuard>, PhantomData); struct NotSend(PhantomData<*mut Python<'static>>); impl Python<'_> { - /// See [Python::attach] - #[inline] - #[track_caller] - #[deprecated(note = "use `Python::attach` instead", since = "0.26.0")] - pub fn with_gil(f: F) -> R - where - F: for<'py> FnOnce(Python<'py>) -> R, - { - Self::attach(f) - } - /// Acquires the global interpreter lock, allowing access to the Python interpreter. The /// provided closure `F` will be executed with the acquired `Python` marker token. /// @@ -483,20 +472,6 @@ impl Python<'_> { crate::interpreter_lifecycle::initialize(); } - /// See [Python::attach_unchecked] - /// # Safety - /// - /// If [`Python::attach`] would succeed, it is safe to call this function. - #[inline] - #[track_caller] - #[deprecated(note = "use `Python::attach_unchecked` instead", since = "0.26.0")] - pub unsafe fn with_gil_unchecked(f: F) -> R - where - F: for<'py> FnOnce(Python<'py>) -> R, - { - unsafe { Self::attach_unchecked(f) } - } - /// Like [`Python::attach`] except Python interpreter state checking is skipped. /// /// Normally when attaching to the Python interpreter, PyO3 checks that it is in @@ -519,17 +494,6 @@ impl Python<'_> { } impl<'py> Python<'py> { - /// See [Python::detach] - #[inline] - #[deprecated(note = "use `Python::detach` instead", since = "0.26.0")] - pub fn allow_threads(self, f: F) -> T - where - F: Ungil + FnOnce() -> T, - T: Ungil, - { - self.detach(f) - } - /// Temporarily releases the GIL, thus allowing other Python threads to run. The GIL will be /// reacquired when `F`'s scope ends. /// @@ -806,15 +770,6 @@ impl<'py> Python<'py> { } impl<'unbound> Python<'unbound> { - /// Deprecated version of [`Python::assume_attached`] - /// - /// # Safety - /// See [`Python::assume_attached`] - #[inline] - #[deprecated(since = "0.26.0", note = "use `Python::assume_attached` instead")] - pub unsafe fn assume_gil_acquired() -> Python<'unbound> { - unsafe { Self::assume_attached() } - } /// Unsafely creates a Python token with an unbounded lifetime. /// /// Many of PyO3 APIs use [`Python<'_>`] as proof that the calling thread is attached to the diff --git a/src/prelude.rs b/src/prelude.rs index ccde0c902c3..8311c9f6413 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -10,8 +10,6 @@ pub use crate::conversion::{FromPyObject, FromPyObjectOwned, IntoPyObject}; pub use crate::err::{PyErr, PyResult}; -#[allow(deprecated)] -pub use crate::instance::PyObject; pub use crate::instance::{Borrowed, Bound, Py}; pub use crate::marker::Python; pub use crate::pycell::{PyRef, PyRefMut}; diff --git a/src/sync.rs b/src/sync.rs index 8b3b5216361..cb71ccd10f5 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -12,8 +12,8 @@ use crate::{ internal::state::SuspendAttach, sealed::Sealed, - types::{any::PyAnyMethods, PyAny, PyString}, - Bound, Py, PyResult, PyTypeCheck, Python, + types::{PyAny, PyString}, + Bound, Py, Python, }; use std::{ cell::UnsafeCell, @@ -25,9 +25,6 @@ use std::{ pub mod critical_section; pub(crate) mod once_lock; -#[cfg(not(Py_GIL_DISABLED))] -use crate::PyVisit; - /// Deprecated alias for [`pyo3::sync::critical_section::with_critical_section`][crate::sync::critical_section::with_critical_section] #[deprecated( since = "0.28.0", @@ -53,118 +50,11 @@ where } pub use self::once_lock::PyOnceLock; -/// Value with concurrent access protected by the GIL. -/// -/// This is a synchronization primitive based on Python's global interpreter lock (GIL). -/// It ensures that only one thread at a time can access the inner value via shared references. -/// It can be combined with interior mutability to obtain mutable references. -/// -/// This type is not defined for extensions built against the free-threaded CPython ABI. -/// -/// # Example -/// -/// Combining `GILProtected` with `RefCell` enables mutable access to static data: -/// -/// ``` -/// # #![allow(deprecated)] -/// # use pyo3::prelude::*; -/// use pyo3::sync::GILProtected; -/// use std::cell::RefCell; -/// -/// static NUMBERS: GILProtected>> = GILProtected::new(RefCell::new(Vec::new())); -/// -/// Python::attach(|py| { -/// NUMBERS.get(py).borrow_mut().push(42); -/// }); -/// ``` -#[deprecated( - since = "0.26.0", - note = "Prefer an interior mutability primitive compatible with free-threaded Python, such as `Mutex` in combination with the `MutexExt` trait" -)] -#[cfg(not(Py_GIL_DISABLED))] -pub struct GILProtected { - value: T, -} - -#[allow(deprecated)] -#[cfg(not(Py_GIL_DISABLED))] -impl GILProtected { - /// Place the given value under the protection of the GIL. - pub const fn new(value: T) -> Self { - Self { value } - } - - /// Gain access to the inner value by giving proof of having acquired the GIL. - pub fn get<'py>(&'py self, _py: Python<'py>) -> &'py T { - &self.value - } - - /// Gain access to the inner value by giving proof that garbage collection is happening. - pub fn traverse<'py>(&'py self, _visit: PyVisit<'py>) -> &'py T { - &self.value - } -} - -#[allow(deprecated)] -#[cfg(not(Py_GIL_DISABLED))] -unsafe impl Sync for GILProtected where T: Send {} - -/// A write-once primitive similar to [`std::sync::OnceLock`]. -/// -/// Unlike `OnceLock` which blocks threads to achieve thread safety, `GilOnceCell` -/// allows calls to [`get_or_init`][GILOnceCell::get_or_init] and -/// [`get_or_try_init`][GILOnceCell::get_or_try_init] to race to create an initialized value. -/// (It is still guaranteed that only one thread will ever write to the cell.) -/// -/// On Python versions that run with the Global Interpreter Lock (GIL), this helps to avoid -/// deadlocks between initialization and the GIL. For an example of such a deadlock, see -#[doc = concat!( - "[the FAQ section](https://pyo3.rs/v", - env!("CARGO_PKG_VERSION"), - "/faq.html#im-experiencing-deadlocks-using-pyo3-with-stdsynconcelock-stdsynclazylock-lazy_static-and-once_cell)" -)] -/// of the guide. -/// -/// Note that because the GIL blocks concurrent execution, in practice the means that -/// [`get_or_init`][GILOnceCell::get_or_init] and -/// [`get_or_try_init`][GILOnceCell::get_or_try_init] may race if the initialization -/// function leads to the GIL being released and a thread context switch. This can -/// happen when importing or calling any Python code, as long as it releases the -/// GIL at some point. On free-threaded Python without any GIL, the race is -/// more likely since there is no GIL to prevent races. In the future, PyO3 may change -/// the semantics of GILOnceCell to behave more like the GIL build in the future. -/// -/// # Re-entrant initialization -/// -/// [`get_or_init`][GILOnceCell::get_or_init] and -/// [`get_or_try_init`][GILOnceCell::get_or_try_init] do not protect against infinite recursion -/// from reentrant initialization. -/// -/// # Examples -/// -/// The following example shows how to use `GILOnceCell` to share a reference to a Python list -/// between threads: -/// -/// ``` -/// #![allow(deprecated)] -/// use pyo3::sync::GILOnceCell; -/// use pyo3::prelude::*; -/// use pyo3::types::PyList; -/// -/// static LIST_CELL: GILOnceCell> = GILOnceCell::new(); -/// -/// pub fn get_shared_list(py: Python<'_>) -> &Bound<'_, PyList> { -/// LIST_CELL -/// .get_or_init(py, || PyList::empty(py).unbind()) -/// .bind(py) -/// } -/// # Python::attach(|py| assert_eq!(get_shared_list(py).len(), 0)); -/// ``` #[deprecated( since = "0.26.0", - note = "Prefer `pyo3::sync::PyOnceLock`, which avoids the possibility of racing during initialization." + note = "Now internal only, to be removed after https://github.com/PyO3/pyo3/pull/5341" )] -pub struct GILOnceCell { +pub(crate) struct GILOnceCell { once: Once, data: UnsafeCell>, @@ -229,24 +119,6 @@ impl GILOnceCell { } } - /// Get a reference to the contained value, initializing it if needed using the provided - /// closure. - /// - /// See the type-level documentation for detail on re-entrancy and concurrent initialization. - #[inline] - pub fn get_or_init(&self, py: Python<'_>, f: F) -> &T - where - F: FnOnce() -> T, - { - if let Some(value) = self.get(py) { - return value; - } - - // .unwrap() will never panic because the result is always Ok - self.init(py, || Ok::(f())) - .unwrap() - } - /// Like `get_or_init`, but accepts a fallible initialization function. If it fails, the cell /// is left uninitialized. /// @@ -281,17 +153,6 @@ impl GILOnceCell { Ok(self.get(py).unwrap()) } - /// Get the contents of the cell mutably. This is only possible if the reference to the cell is - /// unique. - pub fn get_mut(&mut self) -> Option<&mut T> { - if self.once.is_completed() { - // SAFETY: the cell has been written. - Some(unsafe { (*self.data.get()).assume_init_mut() }) - } else { - None - } - } - /// Set the value in the cell. /// /// If the cell has already been written, `Err(value)` will be returned containing the new @@ -316,98 +177,6 @@ impl GILOnceCell { None => Ok(()), } } - - /// Takes the value out of the cell, moving it back to an uninitialized state. - /// - /// Has no effect and returns None if the cell has not yet been written. - pub fn take(&mut self) -> Option { - if self.once.is_completed() { - // Reset the cell to its default state so that it won't try to - // drop the value again. - self.once = Once::new(); - // SAFETY: the cell has been written. `self.once` has been reset, - // so when `self` is dropped the value won't be read again. - Some(unsafe { self.data.get_mut().assume_init_read() }) - } else { - None - } - } - - /// Consumes the cell, returning the wrapped value. - /// - /// Returns None if the cell has not yet been written. - pub fn into_inner(mut self) -> Option { - self.take() - } -} - -#[allow(deprecated)] -impl GILOnceCell> { - /// Creates a new cell that contains a new Python reference to the same contained object. - /// - /// Returns an uninitialized cell if `self` has not yet been initialized. - pub fn clone_ref(&self, py: Python<'_>) -> Self { - let cloned = Self { - once: Once::new(), - data: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData, - }; - if let Some(value) = self.get(py) { - let _ = cloned.set(py, value.clone_ref(py)); - } - cloned - } -} - -#[allow(deprecated)] -impl GILOnceCell> -where - T: PyTypeCheck, -{ - /// Get a reference to the contained Python type, initializing the cell if needed. - /// - /// This is a shorthand method for `get_or_init` which imports the type from Python on init. - /// - /// # Example: Using `GILOnceCell` to store a class in a static variable. - /// - /// `GILOnceCell` can be used to avoid importing a class multiple times: - /// ``` - /// #![allow(deprecated)] - /// # use pyo3::prelude::*; - /// # use pyo3::sync::GILOnceCell; - /// # use pyo3::types::{PyDict, PyType}; - /// # use pyo3::intern; - /// # - /// #[pyfunction] - /// fn create_ordered_dict<'py>(py: Python<'py>, dict: Bound<'py, PyDict>) -> PyResult> { - /// // Even if this function is called multiple times, - /// // the `OrderedDict` class will be imported only once. - /// static ORDERED_DICT: GILOnceCell> = GILOnceCell::new(); - /// ORDERED_DICT - /// .import(py, "collections", "OrderedDict")? - /// .call1((dict,)) - /// } - /// - /// # Python::attach(|py| { - /// # let dict = PyDict::new(py); - /// # dict.set_item(intern!(py, "foo"), 42).unwrap(); - /// # let fun = wrap_pyfunction!(create_ordered_dict, py).unwrap(); - /// # let ordered_dict = fun.call1((&dict,)).unwrap(); - /// # assert!(dict.eq(ordered_dict).unwrap()); - /// # }); - /// ``` - pub fn import<'py>( - &self, - py: Python<'py>, - module_name: &str, - attr_name: &str, - ) -> PyResult<&Bound<'py, T>> { - self.get_or_try_init(py, || { - let type_object = py.import(module_name)?.getattr(attr_name)?.cast_into()?; - Ok(type_object.unbind()) - }) - .map(|ty| ty.bind(py)) - } } #[allow(deprecated)] @@ -957,7 +726,7 @@ mod rwlock_ext_sealed { mod tests { use super::*; - use crate::types::{PyDict, PyDictMethods}; + use crate::types::{PyAnyMethods, PyDict, PyDictMethods}; #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "macros")] use std::sync::atomic::{AtomicBool, Ordering}; @@ -997,7 +766,7 @@ mod tests { #[allow(deprecated)] fn test_once_cell() { Python::attach(|py| { - let mut cell = GILOnceCell::new(); + let cell = GILOnceCell::new(); assert!(cell.get(py).is_none()); @@ -1008,14 +777,6 @@ mod tests { assert_eq!(cell.get(py), Some(&2)); assert_eq!(cell.get_or_try_init(py, || Err(5)), Ok(&2)); - - assert_eq!(cell.take(), Some(2)); - assert_eq!(cell.into_inner(), None); - - let cell_py = GILOnceCell::new(); - assert!(cell_py.clone_ref(py).get(py).is_none()); - cell_py.get_or_init(py, || py.None()); - assert!(cell_py.clone_ref(py).get(py).unwrap().is_none(py)); }) } diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 307cb4a8b25..4c0fce18728 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -692,9 +692,12 @@ impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyTime> { /// Values of this type are accessed via PyO3's smart pointers, e.g. as /// [`Py`][crate::Py] or [`Bound<'py, PyTzInfo>`][Bound]. /// -/// This is an abstract base class and cannot be constructed directly. -/// For concrete time zone implementations, see [`timezone_utc`] and -/// the [`zoneinfo` module](https://docs.python.org/3/library/zoneinfo.html). +/// This is an abstract base class, the primary implementations are +/// [`datetime.timezone`](https://docs.python.org/3/library/datetime.html#timezone-objects) +/// and the [`zoneinfo` module](https://docs.python.org/3/library/zoneinfo.html). +/// +/// The constructors [`PyTzInfo::utc`], [`PyTzInfo::fixed_offset`] and [`PyTzInfo::timezone`] +/// create these concrete subclasses. #[repr(transparent)] pub struct PyTzInfo(PyAny); @@ -796,14 +799,6 @@ impl PyTzInfo { } } -/// Equivalent to `datetime.timezone.utc` -#[deprecated(since = "0.25.0", note = "use `PyTzInfo::utc` instead")] -pub fn timezone_utc(py: Python<'_>) -> Bound<'_, PyTzInfo> { - PyTzInfo::utc(py) - .expect("failed to import datetime.timezone.utc") - .to_owned() -} - /// Bindings for `datetime.timedelta`. /// /// Values of this type are accessed via PyO3's smart pointers, e.g. as diff --git a/src/types/mod.rs b/src/types/mod.rs index e8e5517faea..bd2cc27b80c 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -7,10 +7,7 @@ pub use self::bytes::{PyBytes, PyBytesMethods}; pub use self::capsule::{CapsuleName, PyCapsule, PyCapsuleMethods}; pub use self::code::{PyCode, PyCodeInput, PyCodeMethods}; pub use self::complex::{PyComplex, PyComplexMethods}; -#[allow(deprecated)] -pub use self::datetime::{ - timezone_utc, PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo, PyTzInfoAccess, -}; +pub use self::datetime::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo, PyTzInfoAccess}; #[cfg(not(Py_LIMITED_API))] pub use self::datetime::{PyDateAccess, PyDeltaAccess, PyTimeAccess}; pub use self::dict::{IntoPyDict, PyDict, PyDictMethods}; diff --git a/src/types/string.rs b/src/types/string.rs index 6d993a9a530..0f69d18e0be 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -7,7 +7,7 @@ use crate::types::bytes::PyBytesMethods; use crate::types::PyBytes; use crate::{ffi, Bound, Py, PyAny, PyResult, Python}; use std::borrow::Cow; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; use std::str; /// Represents raw data backing a Python `str`. @@ -230,24 +230,6 @@ impl PyString { .cast_into_unchecked() } } - - /// Deprecated form of `PyString::from_encoded_object`. - /// - /// This version took `&str` arguments for `encoding` and `errors`, which required a runtime - /// conversion to `CString` internally. - #[deprecated( - since = "0.25.0", - note = "replaced with to `PyString::from_encoded_object`" - )] - pub fn from_object<'py>( - src: &Bound<'py, PyAny>, - encoding: &str, - errors: &str, - ) -> PyResult> { - let encoding = CString::new(encoding)?; - let errors = CString::new(errors)?; - PyString::from_encoded_object(src, Some(&encoding), Some(&errors)) - } } /// Implementation of functionality for [`PyString`]. @@ -667,12 +649,6 @@ mod tests { let result = py_string.to_cow().unwrap(); assert_eq!(result, "abcd"); - - #[allow(deprecated)] - let py_string = PyString::from_object(&py_bytes, "utf-8", "ignore").unwrap(); - - let result = py_string.to_cow().unwrap(); - assert_eq!(result, "abcd"); }); } @@ -695,14 +671,6 @@ mod tests { err.to_string(), "LookupError: unknown error handler name 'wat'" ); - - #[allow(deprecated)] - let result = PyString::from_object(&py_bytes, "utf\0-8", "ignore"); - assert!(result.is_err()); - - #[allow(deprecated)] - let result = PyString::from_object(&py_bytes, "utf-8", "ign\0ore"); - assert!(result.is_err()); }); }