diff --git a/build.rs b/build.rs index 1077d45e3b7..75ebec2d115 100644 --- a/build.rs +++ b/build.rs @@ -53,7 +53,19 @@ fn configure_pyo3() -> Result<()> { Ok(()) } +/// Enables a faux `std` feature by default. +/// +/// Set env var `PYO3_WIP_NO_STD` to `1` to disable it. +fn configure_wip_no_std() { + println!("cargo:rustc-check-cfg=cfg(wip_feature_std)"); + match cargo_env_var("PYO3_WIP_NO_STD") { + Some(no_std) if no_std.trim() == "1" || no_std.trim().eq_ignore_ascii_case("true") => (), + _ => println!("cargo:rustc-cfg=wip_feature_std"), + } +} + fn main() { + configure_wip_no_std(); pyo3_build_config::print_expected_cfgs(); if let Err(e) = configure_pyo3() { eprintln!("error: {}", e.report()); diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000000..c9f313f2603 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,7 @@ +[[disallowed-types]] +path = "std::sync::Mutex" +reason = "use crate::platform::sync::non_poison::Mutex for `no_std` compatibility" + +[[disallowed-types]] +path = "std::sync::Once" +reason = "use crate::platform::sync::Once for no_std compatibility" diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index ac92290fd1f..7eaa640c0c6 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -3075,9 +3075,9 @@ impl<'a> PyClassImplsBuilder<'a> { quote! { impl #pyo3_path::impl_::pyclass::PyClassWithFreeList for #cls { #[inline] - fn get_free_list(py: #pyo3_path::Python<'_>) -> &'static ::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList> { - static FREELIST: #pyo3_path::sync::PyOnceLock<::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList>> = #pyo3_path::sync::PyOnceLock::new(); - &FREELIST.get_or_init(py, || ::std::sync::Mutex::new(#pyo3_path::impl_::freelist::PyObjectFreeList::with_capacity(#freelist))) + fn get_free_list(py: #pyo3_path::Python<'_>) -> &'static #pyo3_path::platform::sync::non_poison::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList> { + static FREELIST: #pyo3_path::sync::PyOnceLock<#pyo3_path::platform::sync::non_poison::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList>> = #pyo3_path::sync::PyOnceLock::new(); + &FREELIST.get_or_init(py, || #pyo3_path::platform::sync::non_poison::Mutex::new(#pyo3_path::impl_::freelist::PyObjectFreeList::with_capacity(#freelist))) } } } diff --git a/src/coroutine/cancel.rs b/src/coroutine/cancel.rs index 8607076b9e5..98c10e4961d 100644 --- a/src/coroutine/cancel.rs +++ b/src/coroutine/cancel.rs @@ -1,8 +1,9 @@ +use crate::platform::sync::non_poison::Mutex; use crate::{Py, PyAny}; use alloc::sync::Arc; use core::future::poll_fn; +use core::panic::AssertUnwindSafe; use core::task::{Context, Poll, Waker}; -use std::sync::Mutex; #[derive(Debug, Default)] struct Inner { @@ -14,7 +15,7 @@ struct Inner { /// /// Only the last exception thrown can be retrieved. #[derive(Debug, Default)] -pub struct CancelHandle(Arc>); +pub struct CancelHandle(Arc>>); impl CancelHandle { /// Create a new `CoroutineCancel`. @@ -24,12 +25,12 @@ impl CancelHandle { /// Returns whether the associated coroutine has been cancelled. pub fn is_cancelled(&self) -> bool { - self.0.lock().unwrap().exception.is_some() + self.0.lock().exception.is_some() } /// Poll to retrieve the exception thrown in the associated coroutine. pub fn poll_cancelled(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.0.lock().unwrap(); + let mut inner = self.0.lock(); if let Some(exc) = inner.exception.take() { return Poll::Ready(exc); } @@ -54,11 +55,11 @@ impl CancelHandle { } #[doc(hidden)] -pub struct ThrowCallback(Arc>); +pub struct ThrowCallback(Arc>>); impl ThrowCallback { pub(super) fn throw(&self, exc: Py) { - let mut inner = self.0.lock().unwrap(); + let mut inner = self.0.lock(); inner.exception = Some(exc); if let Some(waker) = inner.waker.take() { waker.wake(); diff --git a/src/err/err_state.rs b/src/err/err_state.rs index 2efc43ed4f5..eece359c3d5 100644 --- a/src/err/err_state.rs +++ b/src/err/err_state.rs @@ -2,11 +2,11 @@ #![allow(clippy::undocumented_unsafe_blocks)] use crate::platform::prelude::*; -use core::cell::UnsafeCell; -use std::{ - sync::{Mutex, Once}, - thread::ThreadId, -}; +#[cfg(not(Py_3_12))] +use crate::platform::sync::non_poison::Mutex; +use crate::platform::sync::Once; +use core::cell::{Cell, UnsafeCell}; +use std::thread::ThreadId; #[cfg(not(Py_3_12))] use crate::sync::MutexExt; @@ -23,7 +23,7 @@ pub(crate) struct PyErrState { // after normalization. normalized: Once, // Guard against re-entrancy when normalizing the exception state. - normalizing_thread: Mutex>, + normalizing_thread: Cell>, inner: UnsafeCell>, } @@ -68,7 +68,7 @@ impl PyErrState { fn from_inner(inner: PyErrStateInner) -> Self { Self { normalized: Once::new(), - normalizing_thread: Mutex::new(None), + normalizing_thread: Cell::new(None), inner: UnsafeCell::new(Some(inner)), } } @@ -96,9 +96,10 @@ impl PyErrState { // Guard against re-entrant normalization, because `Once` does not provide // re-entrancy guarantees. - if let Some(thread) = self.normalizing_thread.lock().unwrap().as_ref() { - assert!( - !(*thread == std::thread::current().id()), + if let Some(thread) = self.normalizing_thread.get() { + assert_ne!( + thread, + std::thread::current().id(), "Re-entrant normalization of PyErrState detected" ); } @@ -107,9 +108,7 @@ impl PyErrState { py.detach(|| { self.normalized.call_once(|| { self.normalizing_thread - .lock() - .unwrap() - .replace(std::thread::current().id()); + .set(Some(std::thread::current().id())); // Safety: no other thread can access the inner value while we are normalizing it. let state = unsafe { @@ -143,7 +142,7 @@ pub(crate) struct PyErrStateNormalized { ptype: Py, pub pvalue: Py, #[cfg(not(Py_3_12))] - ptraceback: std::sync::Mutex>>, + ptraceback: Mutex>>, } impl PyErrStateNormalized { @@ -177,7 +176,6 @@ impl PyErrStateNormalized { pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option> { self.ptraceback .lock_py_attached(py) - .unwrap() .as_ref() .map(|traceback| traceback.bind(py).clone()) } @@ -193,7 +191,7 @@ impl PyErrStateNormalized { #[cfg(not(Py_3_12))] pub(crate) fn set_ptraceback<'py>(&self, py: Python<'py>, tb: Option>) { - *self.ptraceback.lock_py_attached(py).unwrap() = tb.map(Bound::unbind); + *self.ptraceback.lock_py_attached(py) = tb.map(Bound::unbind); } #[cfg(Py_3_12)] @@ -251,7 +249,7 @@ impl PyErrStateNormalized { ptype.map(|ptype| PyErrStateNormalized { ptype: ptype.unbind(), pvalue: pvalue.expect("normalized exception value missing").unbind(), - ptraceback: std::sync::Mutex::new(ptraceback.map(Bound::unbind)), + ptraceback: Mutex::new(ptraceback.map(Bound::unbind)), }) } } @@ -291,10 +289,9 @@ impl PyErrStateNormalized { ptype: self.ptype.clone_ref(py), pvalue: self.pvalue.clone_ref(py), #[cfg(not(Py_3_12))] - ptraceback: std::sync::Mutex::new( + ptraceback: Mutex::new( self.ptraceback .lock_py_attached(py) - .unwrap() .as_ref() .map(|ptraceback| ptraceback.clone_ref(py)), ), @@ -350,7 +347,6 @@ impl PyErrStateInner { pvalue.into_ptr(), ptraceback .into_inner() - .unwrap() .map_or(core::ptr::null_mut(), Py::into_ptr), ), }; diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 273bf25c3be..1a48f464f97 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -3,6 +3,7 @@ #[allow(unused_imports, reason = "conditionally used")] use crate::platform::prelude::*; +use crate::platform::sync::non_poison::Mutex; use crate::{ exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError}, ffi, @@ -24,7 +25,7 @@ use core::{ marker::PhantomData, ptr::{self, NonNull}, }; -use std::{sync::Mutex, thread}; +use std::thread; mod assertions; pub mod doc; @@ -951,7 +952,7 @@ pub unsafe extern "C" fn alloc_with_freelist( // If this type is a variable type or the subtype is not equal to this type, we cannot use the // freelist if nitems == 0 && ptr::eq(subtype, self_type) { - let mut free_list = T::get_free_list(py).lock().unwrap(); + let mut free_list = T::get_free_list(py).lock(); if let Some(obj) = free_list.pop() { drop(free_list); unsafe { ffi::PyObject_Init(obj.as_ptr(), subtype) }; @@ -976,7 +977,7 @@ pub unsafe extern "C" fn free_with_freelist(obj: *mut c_ T::type_object_raw(Python::assume_attached()), ffi::Py_TYPE(obj.as_ptr()) ); - let mut free_list = T::get_free_list(Python::assume_attached()).lock().unwrap(); + let mut free_list = T::get_free_list(Python::assume_attached()).lock(); if let Some(obj) = free_list.insert(obj) { drop(free_list); let ty = ffi::Py_TYPE(obj.as_ptr()); diff --git a/src/impl_/pyclass/lazy_type_object.rs b/src/impl_/pyclass/lazy_type_object.rs index 2142b1d4568..55f1663d8be 100644 --- a/src/impl_/pyclass/lazy_type_object.rs +++ b/src/impl_/pyclass/lazy_type_object.rs @@ -7,6 +7,7 @@ use std::thread::{self, ThreadId}; #[cfg(Py_3_14)] use crate::err::error_on_minusone; +use crate::platform::sync::non_poison::Mutex; #[allow(deprecated)] use crate::sync::GILOnceCell; #[cfg(Py_3_14)] @@ -20,8 +21,6 @@ use crate::{ Bound, Py, PyAny, PyClass, PyErr, PyResult, Python, }; -use std::sync::Mutex; - use super::PyClassItemsIter; /// Lazy type object for PyClass. @@ -136,7 +135,7 @@ impl LazyTypeObjectInner { let thread_id = thread::current().id(); { - let mut threads = self.initializing_threads.lock().unwrap(); + let mut threads = self.initializing_threads.lock(); if threads.contains(&thread_id) { // Reentrant call: just return the type object, even if the // `tp_dict` is not filled yet. @@ -151,7 +150,7 @@ impl LazyTypeObjectInner { } impl Drop for InitializationGuard<'_> { fn drop(&mut self) { - let mut threads = self.initializing_threads.lock().unwrap(); + let mut threads = self.initializing_threads.lock(); threads.retain(|id| *id != self.thread_id); } } @@ -218,7 +217,7 @@ impl LazyTypeObjectInner { // (No further calls to get_or_init() will try to init, on any thread.) let mut threads = { drop(guard); - self.initializing_threads.lock().unwrap() + self.initializing_threads.lock() }; threads.clear(); Ok(type_object.clone().unbind()) diff --git a/src/internal/state.rs b/src/internal/state.rs index 51dcb75a9f8..7340d1bfbb2 100644 --- a/src/internal/state.rs +++ b/src/internal/state.rs @@ -8,11 +8,13 @@ use crate::impl_::panic::PanicTrap; use crate::platform::prelude::*; use crate::{ffi, Python}; +#[cfg(not(pyo3_disable_reference_pool))] +use crate::platform::sync::non_poison::Mutex; use core::cell::Cell; #[cfg_attr(pyo3_disable_reference_pool, allow(unused_imports))] use core::{mem, ptr::NonNull}; #[cfg(not(pyo3_disable_reference_pool))] -use std::sync::{Mutex, OnceLock}; +use std::sync::OnceLock; std::thread_local! { /// This is an internal counter in pyo3 monitoring whether this thread is attached to the interpreter. @@ -202,11 +204,11 @@ impl ReferencePool { } fn register_decref(&self, obj: NonNull) { - self.pending_decrefs.lock().unwrap().push(obj); + self.pending_decrefs.lock().push(obj); } fn drop_deferred_references(&self, _py: Python<'_>) { - let mut pending_decrefs = self.pending_decrefs.lock().unwrap(); + let mut pending_decrefs = self.pending_decrefs.lock(); if pending_decrefs.is_empty() { return; } @@ -382,7 +384,6 @@ mod tests { !get_pool() .pending_decrefs .lock() - .unwrap() .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) } @@ -393,7 +394,6 @@ mod tests { get_pool() .pending_decrefs .lock() - .unwrap() .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) } diff --git a/src/interpreter_lifecycle.rs b/src/interpreter_lifecycle.rs index f596da343cf..77223142c97 100644 --- a/src/interpreter_lifecycle.rs +++ b/src/interpreter_lifecycle.rs @@ -1,17 +1,19 @@ // TODO https://github.com/PyO3/pyo3/issues/5487 #![allow(clippy::undocumented_unsafe_blocks)] +use crate::platform::sync::Once; + #[cfg(not(any(PyPy, GraalPy)))] use crate::{ffi, internal::state::AttachGuard, Python}; -static START: std::sync::Once = std::sync::Once::new(); +static START: Once = Once::new(); #[cfg(not(any(PyPy, GraalPy)))] pub(crate) fn initialize() { // Protect against race conditions when Python is not yet initialized and multiple threads // concurrently call 'initialize()'. Note that we do not protect against // concurrent initialization of the Python runtime by other users of the Python C API. - START.call_once_force(|_| unsafe { + START.call_once_force(|| unsafe { // Use call_once_force because if initialization panics, it's okay to try again. if ffi::Py_IsInitialized() == 0 { ffi::Py_InitializeEx(0); @@ -127,7 +129,7 @@ pub(crate) fn ensure_initialized() { initialize(); } - START.call_once_force(|_| unsafe { + START.call_once_force(|| unsafe { // Use call_once_force because if there is a panic because the interpreter is // not initialized, it's fine for the user to initialize the interpreter and // retry. diff --git a/src/lib.rs b/src/lib.rs index b9fce6cf463..175766556b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -364,8 +364,6 @@ pub(crate) mod ffi_ptr_ext; pub(crate) mod py_result_ext; pub(crate) mod sealed; -mod platform; - /// Old module which contained some implementation details of the `#[pyproto]` module. /// /// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::CompareOp` instead @@ -415,6 +413,9 @@ mod internal; #[macro_use] mod internal_tricks; +#[doc(hidden)] +pub mod platform; + // Macro dependencies, also contains macros exported for use across the codebase and // in expanded macros. #[doc(hidden)] diff --git a/src/platform.rs b/src/platform.rs index 924f818dc55..02c222b448b 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -14,6 +14,8 @@ pub(crate) mod prelude { pub use std::eprintln; } +pub mod sync; + #[cfg(feature = "hashbrown")] pub use hashbrown::{HashMap, HashSet}; diff --git a/src/platform/sync.rs b/src/platform/sync.rs new file mode 100644 index 00000000000..8c2417e12ec --- /dev/null +++ b/src/platform/sync.rs @@ -0,0 +1,146 @@ +use crate::sealed; +use crate::sync::OnceExt; + +// TODO replace with cfg_select when MSRV >= 1.95.0 + +#[cfg(wip_feature_std)] +#[allow(clippy::disallowed_types)] +type OnceInner = std::sync::Once; + +#[cfg(all(not(wip_feature_std), feature = "parking_lot"))] +type OnceInner = parking_lot::Once; + +#[cfg(all(not(wip_feature_std), not(feature = "parking_lot")))] +compile_error!("Please enable at least one of the following features: std, parking_lot"); + +pub struct Once(OnceInner); + +impl Default for Once { + fn default() -> Self { + Self::new() + } +} + +impl Once { + /// Creates a new `Once` value. + #[inline(always)] + #[must_use] + pub const fn new() -> Once { + Once(OnceInner::new()) + } + + #[inline] + pub fn call_once(&self, f: impl FnOnce()) { + self.0.call_once(f); + } + + #[inline] + pub fn call_once_force(&self, f: impl FnOnce()) { + self.0.call_once_force(move |_| f()); + } + + #[inline(always)] + pub fn is_completed(&self) -> bool { + #[cfg(wip_feature_std)] + { + self.0.is_completed() + } + #[cfg(all(not(wip_feature_std), feature = "parking_lot"))] + { + matches!(self.0.state(), parking_lot::OnceState::Done) + } + #[cfg(all(not(wip_feature_std), feature = "parking_lot"))] + { + compile_error!("Please enable at least one of the following features: std, parking_lot") + } + } +} + +impl sealed::Sealed for Once {} +impl OnceExt for Once { + type OnceState = (); + + fn call_once_py_attached(&self, py: crate::prelude::Python<'_>, f: impl FnOnce()) { + self.0.call_once_force_py_attached(py, move |_| f()); + } + + fn call_once_force_py_attached( + &self, + py: crate::prelude::Python<'_>, + f: impl FnOnce(&Self::OnceState), + ) { + self.0.call_once_force_py_attached(py, |_| f(&())); + } +} + +pub mod non_poison { + // TODO replace with cfg_select when MSRV >= 1.95.0 + #[cfg(wip_feature_std)] + pub use std::sync::MutexGuard; + + #[cfg(all(not(wip_feature_std), feature = "parking_lot"))] + pub use parking_lot::{Mutex, MutexGuard}; + + #[cfg(all(not(wip_feature_std), not(feature = "parking_lot")))] + compile_error!("Please enable at least one of the following features: std, parking_lot"); + + #[cfg(wip_feature_std)] + #[derive(Default, Debug)] + pub struct Mutex { + #[allow(clippy::disallowed_types)] + inner: std::sync::Mutex, + } + + #[cfg(wip_feature_std)] + impl Mutex { + #[inline(always)] + pub const fn new(t: T) -> Mutex { + Mutex { + #[allow(clippy::disallowed_types)] + inner: std::sync::Mutex::new(t), + } + } + + pub fn into_inner(self) -> T { + self.inner.into_inner().unwrap_or_else(|e| e.into_inner()) + } + } + + #[cfg(wip_feature_std)] + impl Mutex { + #[inline(always)] + pub fn lock(&self) -> std::sync::MutexGuard<'_, T> { + self.inner.lock().unwrap_or_else(|e| e.into_inner()) + } + + // TODO try_lock + } + + #[cfg(wip_feature_std)] + impl From for Mutex { + #[inline(always)] + fn from(t: T) -> Self { + Mutex { + #[allow(clippy::disallowed_types)] + inner: std::sync::Mutex::new(t), + } + } + } + + #[cfg(wip_feature_std)] + impl crate::sealed::Sealed for Mutex {} + + #[cfg(wip_feature_std)] + impl crate::sync::MutexExt for Mutex { + type LockResult<'a> + = MutexGuard<'a, T> + where + T: 'a; + + fn lock_py_attached(&self, py: crate::prelude::Python<'_>) -> Self::LockResult<'_> { + self.inner + .lock_py_attached(py) + .unwrap_or_else(|e| e.into_inner()) + } + } +} diff --git a/src/sealed.rs b/src/sealed.rs index 0155aa93349..f6d15d946d9 100644 --- a/src/sealed.rs +++ b/src/sealed.rs @@ -61,7 +61,9 @@ impl Sealed for ModuleDef {} impl Sealed for PyNativeTypeInitializer {} impl Sealed for PyClassInitializer {} +#[allow(clippy::disallowed_types)] impl Sealed for std::sync::Once {} +#[allow(clippy::disallowed_types)] impl Sealed for std::sync::Mutex {} #[cfg(feature = "lock_api")] impl Sealed for lock_api::Mutex {} diff --git a/src/sync.rs b/src/sync.rs index 0ec45ff9c2b..bb17cc959ca 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -12,6 +12,7 @@ //! interpreter. //! //! This module provides synchronization primitives which are able to synchronize under these conditions. +use crate::platform::sync::Once; use crate::{ internal::state::SuspendAttach, sealed::Sealed, @@ -19,7 +20,6 @@ use crate::{ Bound, Py, Python, }; use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit}; -use std::sync::{Once, OnceState}; pub mod critical_section; pub(crate) mod once_lock; @@ -163,7 +163,7 @@ impl GILOnceCell { // NB this can block, but since this is only writing a single value and // does not call arbitrary python code, we don't need to worry about // deadlocks with the GIL. - self.once.call_once_force(|_| { + self.once.call_once_force(|| { // SAFETY: no other threads can be writing this value, because we are // inside the `call_once_force` closure. unsafe { @@ -338,8 +338,9 @@ pub trait RwLockExt: rwlock_ext_sealed::Sealed { fn write_py_attached(&self, py: Python<'_>) -> Self::WriteLockResult<'_>; } -impl OnceExt for Once { - type OnceState = OnceState; +#[allow(clippy::disallowed_types)] +impl OnceExt for std::sync::Once { + type OnceState = std::sync::OnceState; fn call_once_py_attached(&self, py: Python<'_>, f: impl FnOnce()) { if self.is_completed() { @@ -349,7 +350,7 @@ impl OnceExt for Once { init_once_py_attached(self, py, f) } - fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&OnceState)) { + fn call_once_force_py_attached(&self, py: Python<'_>, f: impl FnOnce(&std::sync::OnceState)) { if self.is_completed() { return; } @@ -404,6 +405,7 @@ impl OnceLockExt for std::sync::OnceLock { } } +#[allow(clippy::disallowed_types)] impl MutexExt for std::sync::Mutex { type LockResult<'a> = std::sync::LockResult> @@ -654,7 +656,8 @@ where } #[cold] -fn init_once_py_attached(once: &Once, _py: Python<'_>, f: F) +#[allow(clippy::disallowed_types)] +fn init_once_py_attached(once: &std::sync::Once, _py: Python<'_>, f: F) where F: FnOnce() -> T, { @@ -670,9 +673,10 @@ where } #[cold] -fn init_once_force_py_attached(once: &Once, _py: Python<'_>, f: F) +#[allow(clippy::disallowed_types)] +fn init_once_force_py_attached(once: &std::sync::Once, _py: Python<'_>, f: F) where - F: FnOnce(&OnceState) -> T, + F: FnOnce(&std::sync::OnceState) -> T, { // SAFETY: detach from the runtime right before a possibly blocking call // then reattach when the blocking call completes and before calling @@ -723,6 +727,7 @@ mod rwlock_ext_sealed { impl Sealed for alloc::sync::Arc> {} } +#[allow(clippy::disallowed_types, reason = "tests")] #[cfg(test)] mod tests { use super::*; @@ -736,6 +741,8 @@ mod tests { use std::sync::Barrier; #[cfg(not(target_arch = "wasm32"))] use std::sync::Mutex; + #[cfg(not(target_arch = "wasm32"))] + use std::sync::{Once, OnceState}; #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "macros")] diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 56ec3a82b29..bdd187fb03e 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -335,6 +335,7 @@ impl<'py> TryFrom<&Bound<'py, PyAny>> for Bound<'py, PyByteArray> { } } +#[allow(clippy::disallowed_types, reason = "tests")] #[cfg(test)] mod tests { use crate::types::{PyAnyMethods, PyByteArray, PyByteArrayMethods}; diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 822168096a5..d471d962e33 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -5,14 +5,14 @@ use pyo3::class::PyTraverseError; use pyo3::class::PyVisit; use pyo3::ffi; +use pyo3::platform::sync::{non_poison::Mutex, Once}; use pyo3::prelude::*; #[cfg(not(Py_GIL_DISABLED))] use pyo3::py_run; #[cfg(not(target_arch = "wasm32"))] use std::cell::Cell; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Once; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; mod test_utils; @@ -498,7 +498,7 @@ struct DropDuringTraversal { impl DropDuringTraversal { #[expect(clippy::unnecessary_wraps)] fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { - let mut cycle_ref = self.cycle.lock().unwrap(); + let mut cycle_ref = self.cycle.lock(); *cycle_ref = None; Ok(()) } @@ -520,7 +520,7 @@ fn drop_during_traversal_with_gil() { ) .unwrap(); - *inst.borrow_mut(py).cycle.lock().unwrap() = Some(inst.clone_ref(py)); + *inst.borrow_mut(py).cycle.lock() = Some(inst.clone_ref(py)); check.assert_not_dropped(); let ptr = inst.as_ptr(); @@ -554,7 +554,7 @@ fn drop_during_traversal_without_gil() { ) .unwrap(); - *inst.borrow_mut(py).cycle.lock().unwrap() = Some(inst.clone_ref(py)); + *inst.borrow_mut(py).cycle.lock() = Some(inst.clone_ref(py)); check.assert_not_dropped(); inst @@ -756,7 +756,7 @@ fn test_drop_buffer_during_traversal_without_gil() { impl BufferDropDuringTraversal { #[expect(clippy::unnecessary_wraps)] fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { - self.inner.lock().unwrap().take(); + self.inner.lock().take(); Ok(()) } diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index c172d954a7a..20468787b90 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -1,10 +1,10 @@ #![cfg(feature = "macros")] use pyo3::exceptions::{PyAttributeError, PyIndexError, PyValueError}; +use pyo3::platform::sync::non_poison::Mutex; use pyo3::types::{PyDict, PyList, PyMapping, PySequence, PySlice, PyType}; use pyo3::{prelude::*, py_run}; use std::iter; -use std::sync::Mutex; mod test_utils; @@ -429,7 +429,7 @@ impl Iterator { } fn __next__(slf: PyRefMut<'_, Self>) -> Option { - slf.iter.lock().unwrap().next() + slf.iter.lock().next() } } diff --git a/tests/test_utils/mod.rs b/tests/test_utils/mod.rs index 5a4605cbb07..5b8fa0e90cc 100644 --- a/tests/test_utils/mod.rs +++ b/tests/test_utils/mod.rs @@ -33,7 +33,7 @@ mod inner { use pyo3::types::{IntoPyDict, PyList}; #[cfg(any(not(all(Py_GIL_DISABLED, Py_3_14)), feature = "macros"))] - use std::sync::{Mutex, PoisonError}; + use pyo3::platform::sync::non_poison::Mutex; use uuid::Uuid; @@ -150,10 +150,7 @@ mod inner { // unraisablehook is a global, so only one thread can be using this struct at a time. static UNRAISABLE_HOOK_MUTEX: Mutex<()> = Mutex::new(()); - // NB this is best-effort, other tests could always modify sys.unraisablehook directly. - let mutex_guard = UNRAISABLE_HOOK_MUTEX - .lock_py_attached(py) - .unwrap_or_else(PoisonError::into_inner); + let mutex_guard = UNRAISABLE_HOOK_MUTEX.lock_py_attached(py); let guard = Self { hook: UnraisableCaptureHook::install(py), @@ -169,7 +166,7 @@ mod inner { /// Takes the captured unraisable error, if any. pub fn take_capture(&self) -> Option<(PyErr, Bound<'py, PyAny>)> { - let mut guard = self.hook.get().capture.lock().unwrap(); + let mut guard = self.hook.get().capture.lock(); guard.take().map(|(e, o)| (e, o.into_bound(self.hook.py()))) } } @@ -195,7 +192,7 @@ mod inner { pub fn hook(&self, unraisable: Bound<'_, PyAny>) { let err = PyErr::from_value(unraisable.getattr("exc_value").unwrap()); let instance = unraisable.getattr("object").unwrap(); - self.capture.lock().unwrap().replace((err, instance.into())); + self.capture.lock().replace((err, instance.into())); } } @@ -237,9 +234,7 @@ mod inner { ) -> PyResult { // NB this is best-effort, other tests could always call the warnings API directly. #[cfg(not(all(Py_GIL_DISABLED, Py_3_14)))] - let _mutex_guard = CATCH_WARNINGS_MUTEX - .lock_py_attached(py) - .unwrap_or_else(PoisonError::into_inner); + let _mutex_guard = CATCH_WARNINGS_MUTEX.lock_py_attached(py); let warnings = py.import("warnings")?; let kwargs = [("record", true)].into_py_dict(py)?; let catch_warnings = warnings