Update MSRV to 1.63 (#4129)

* Bump MSRV to 1.63

* Drop parking_lot in favor of std::sync

* Make portable-atomic dep conditional

* Remove no longer required cfg
This commit is contained in:
Alex Gaynor 2024-04-28 12:11:28 -04:00 committed by GitHub
parent 6fb972b232
commit 9e1960ea34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 67 additions and 80 deletions

View File

@ -54,7 +54,7 @@ jobs:
name: Prepare LD_LIBRARY_PATH (Ubuntu only) name: Prepare LD_LIBRARY_PATH (Ubuntu only)
run: echo LD_LIBRARY_PATH=${pythonLocation}/lib >> $GITHUB_ENV run: echo LD_LIBRARY_PATH=${pythonLocation}/lib >> $GITHUB_ENV
- if: inputs.rust == '1.56.0' - if: inputs.rust == '1.63.0'
name: Prepare minimal package versions (MSRV only) name: Prepare minimal package versions (MSRV only)
run: nox -s set-minimal-package-versions run: nox -s set-minimal-package-versions

View File

@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.56.0 toolchain: 1.63.0
targets: x86_64-unknown-linux-gnu targets: x86_64-unknown-linux-gnu
components: rust-src components: rust-src
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
@ -255,7 +255,7 @@ jobs:
] ]
include: include:
# Test minimal supported Rust version # Test minimal supported Rust version
- rust: 1.56.0 - rust: 1.63.0
python-version: "3.12" python-version: "3.12"
platform: platform:
{ {

View File

@ -12,14 +12,12 @@ categories = ["api-bindings", "development-tools::ffi"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
exclude = ["/.gitignore", ".cargo/config", "/codecov.yml", "/Makefile", "/pyproject.toml", "/noxfile.py", "/.github", "/tests/test_compile_error.rs", "/tests/ui"] exclude = ["/.gitignore", ".cargo/config", "/codecov.yml", "/Makefile", "/pyproject.toml", "/noxfile.py", "/.github", "/tests/test_compile_error.rs", "/tests/ui"]
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.63"
[dependencies] [dependencies]
cfg-if = "1.0" cfg-if = "1.0"
libc = "0.2.62" libc = "0.2.62"
parking_lot = ">= 0.11, < 0.13"
memoffset = "0.9" memoffset = "0.9"
portable-atomic = "1.0"
# 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.21.2" }
@ -46,6 +44,9 @@ rust_decimal = { version = "1.0.0", default-features = false, optional = true }
serde = { version = "1.0", optional = true } serde = { version = "1.0", optional = true }
smallvec = { version = "1.0", optional = true } smallvec = { version = "1.0", optional = true }
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = "1.0"
[dev-dependencies] [dev-dependencies]
assert_approx_eq = "1.1.0" assert_approx_eq = "1.1.0"
chrono = "0.4.25" chrono = "0.4.25"

View File

@ -4,7 +4,7 @@
[![benchmark](https://img.shields.io/badge/benchmark-✓-Green?logo=github)](https://pyo3.rs/dev/bench/) [![benchmark](https://img.shields.io/badge/benchmark-✓-Green?logo=github)](https://pyo3.rs/dev/bench/)
[![codecov](https://img.shields.io/codecov/c/gh/PyO3/pyo3?logo=codecov)](https://codecov.io/gh/PyO3/pyo3) [![codecov](https://img.shields.io/codecov/c/gh/PyO3/pyo3?logo=codecov)](https://codecov.io/gh/PyO3/pyo3)
[![crates.io](https://img.shields.io/crates/v/pyo3?logo=rust)](https://crates.io/crates/pyo3) [![crates.io](https://img.shields.io/crates/v/pyo3?logo=rust)](https://crates.io/crates/pyo3)
[![minimum rustc 1.56](https://img.shields.io/badge/rustc-1.56+-blue?logo=rust)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![minimum rustc 1.63](https://img.shields.io/badge/rustc-1.63+-blue?logo=rust)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![discord server](https://img.shields.io/discord/1209263839632424990?logo=discord)](https://discord.gg/33kcChzH7f) [![discord server](https://img.shields.io/discord/1209263839632424990?logo=discord)](https://discord.gg/33kcChzH7f)
[![contributing notes](https://img.shields.io/badge/contribute-on%20github-Green?logo=github)](https://github.com/PyO3/pyo3/blob/main/Contributing.md) [![contributing notes](https://img.shields.io/badge/contribute-on%20github-Green?logo=github)](https://github.com/PyO3/pyo3/blob/main/Contributing.md)
@ -18,7 +18,7 @@
PyO3 supports the following software versions: PyO3 supports the following software versions:
- Python 3.7 and up (CPython, PyPy, and GraalPy) - Python 3.7 and up (CPython, PyPy, and GraalPy)
- Rust 1.56 and up - Rust 1.63 and up
You can use PyO3 to write a native Python module in Rust, or to embed Python in a Rust binary. The following sections explain each of these in turn. You can use PyO3 to write a native Python module in Rust, or to embed Python in a Rust binary. The following sections explain each of these in turn.

View File

@ -39,7 +39,7 @@ fn configure_pyo3() -> Result<()> {
println!("{}", cfg) println!("{}", cfg)
} }
// Emit cfgs like `thread_local_const_init` // Emit cfgs like `invalid_from_utf8_lint`
print_feature_cfgs(); print_feature_cfgs();
Ok(()) Ok(())

View File

@ -151,7 +151,7 @@ rustflags = [
] ]
``` ```
Alternatively, on rust >= 1.56, one can include in `build.rs`: Alternatively, one can include in `build.rs`:
```rust ```rust
fn main() { fn main() {

View File

@ -6,7 +6,7 @@ To get started using PyO3 you will need three things: a Rust toolchain, a Python
## Rust ## Rust
First, make sure you have Rust installed on your system. If you haven't already done so, try following the instructions [here](https://www.rust-lang.org/tools/install). PyO3 runs on both the `stable` and `nightly` versions so you can choose whichever one fits you best. The minimum required Rust version is 1.56. First, make sure you have Rust installed on your system. If you haven't already done so, try following the instructions [here](https://www.rust-lang.org/tools/install). PyO3 runs on both the `stable` and `nightly` versions so you can choose whichever one fits you best. The minimum required Rust version is 1.63.
If you can run `rustc --version` and the version is new enough you're good to go! If you can run `rustc --version` and the version is new enough you're good to go!

View File

@ -0,0 +1 @@
Raised the MSRV to 1.63

View File

@ -557,22 +557,11 @@ def set_minimal_package_versions(session: nox.Session):
"examples/word-count", "examples/word-count",
) )
min_pkg_versions = { min_pkg_versions = {
"rust_decimal": "1.26.1", "regex": "1.9.6",
"csv": "1.1.6", "proptest": "1.2.0",
"indexmap": "1.6.2", "trybuild": "1.0.89",
"hashbrown": "0.9.1", "eyre": "0.6.8",
"log": "0.4.17", "allocator-api2": "0.2.10",
"once_cell": "1.17.2",
"rayon": "1.6.1",
"rayon-core": "1.10.2",
"regex": "1.7.3",
"proptest": "1.0.0",
"chrono": "0.4.25",
"byteorder": "1.4.3",
"crossbeam-channel": "0.5.8",
"crossbeam-deque": "0.8.3",
"crossbeam-epoch": "0.9.15",
"crossbeam-utils": "0.8.16",
} }
# run cargo update first to ensure that everything is at highest # run cargo update first to ensure that everything is at highest

View File

@ -145,11 +145,6 @@ pub fn print_feature_cfgs() {
let rustc_minor_version = rustc_minor_version().unwrap_or(0); let rustc_minor_version = rustc_minor_version().unwrap_or(0);
// Enable use of const initializer for thread_local! on Rust 1.59 and greater
if rustc_minor_version >= 59 {
println!("cargo:rustc-cfg=thread_local_const_init");
}
// invalid_from_utf8 lint was added in Rust 1.74 // invalid_from_utf8 lint was added in Rust 1.74
if rustc_minor_version >= 74 { if rustc_minor_version >= 74 {
println!("cargo:rustc-cfg=invalid_from_utf8_lint"); println!("cargo:rustc-cfg=invalid_from_utf8_lint");

View File

@ -14,7 +14,7 @@ Manual][capi] for up-to-date documentation.
PyO3 supports the following software versions: PyO3 supports the following software versions:
- Python 3.7 and up (CPython and PyPy) - Python 3.7 and up (CPython and PyPy)
- Rust 1.56 and up - Rust 1.63 and up
# Example: Building Python Native modules # Example: Building Python Native modules

View File

@ -189,7 +189,7 @@ fn configure_pyo3() -> Result<()> {
println!("{}", line); println!("{}", line);
} }
// Emit cfgs like `thread_local_const_init` // Emit cfgs like `invalid_from_utf8_lint`
print_feature_cfgs(); print_feature_cfgs();
Ok(()) Ok(())

View File

@ -51,7 +51,7 @@
//! //!
//! PyO3 supports the following software versions: //! PyO3 supports the following software versions:
//! - Python 3.7 and up (CPython and PyPy) //! - Python 3.7 and up (CPython and PyPy)
//! - Rust 1.56 and up //! - Rust 1.63 and up
//! //!
//! # Example: Building Python Native modules //! # Example: Building Python Native modules
//! //!

View File

@ -1,8 +1,7 @@
use crate::{Py, PyAny, PyObject}; use crate::{Py, PyAny, PyObject};
use parking_lot::Mutex;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker}; use std::task::{Context, Poll, Waker};
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -25,12 +24,12 @@ impl CancelHandle {
/// Returns whether the associated coroutine has been cancelled. /// Returns whether the associated coroutine has been cancelled.
pub fn is_cancelled(&self) -> bool { pub fn is_cancelled(&self) -> bool {
self.0.lock().exception.is_some() self.0.lock().unwrap().exception.is_some()
} }
/// Poll to retrieve the exception thrown in the associated coroutine. /// Poll to retrieve the exception thrown in the associated coroutine.
pub fn poll_cancelled(&mut self, cx: &mut Context<'_>) -> Poll<PyObject> { pub fn poll_cancelled(&mut self, cx: &mut Context<'_>) -> Poll<PyObject> {
let mut inner = self.0.lock(); let mut inner = self.0.lock().unwrap();
if let Some(exc) = inner.exception.take() { if let Some(exc) = inner.exception.take() {
return Poll::Ready(exc); return Poll::Ready(exc);
} }
@ -69,7 +68,7 @@ pub struct ThrowCallback(Arc<Mutex<Inner>>);
impl ThrowCallback { impl ThrowCallback {
pub(super) fn throw(&self, exc: Py<PyAny>) { pub(super) fn throw(&self, exc: Py<PyAny>) {
let mut inner = self.0.lock(); let mut inner = self.0.lock().unwrap();
inner.exception = Some(exc); inner.exception = Some(exc);
if let Some(waker) = inner.waker.take() { if let Some(waker) = inner.waker.take() {
waker.wake(); waker.wake();

View File

@ -2,29 +2,16 @@
use crate::impl_::not_send::{NotSend, NOT_SEND}; use crate::impl_::not_send::{NotSend, NOT_SEND};
use crate::{ffi, Python}; use crate::{ffi, Python};
use parking_lot::{const_mutex, Mutex, Once};
use std::cell::Cell; use std::cell::Cell;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use std::cell::RefCell; use std::cell::RefCell;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::{mem, ptr::NonNull}; use std::{mem, ptr::NonNull, sync};
static START: Once = Once::new(); static START: sync::Once = sync::Once::new();
cfg_if::cfg_if! { std::thread_local! {
if #[cfg(thread_local_const_init)] {
use std::thread_local as thread_local_const_init;
} else {
macro_rules! thread_local_const_init {
($($(#[$attr:meta])* static $name:ident: $ty:ty = const { $init:expr };)*) => (
thread_local! { $($(#[$attr])* static $name: $ty = $init;)* }
)
}
}
}
thread_local_const_init! {
/// This is an internal counter in pyo3 monitoring whether this thread has the GIL. /// This is an internal counter in pyo3 monitoring whether this thread has the GIL.
/// ///
/// It will be incremented whenever a GILGuard or GILPool is created, and decremented whenever /// It will be incremented whenever a GILGuard or GILPool is created, and decremented whenever
@ -249,26 +236,26 @@ type PyObjVec = Vec<NonNull<ffi::PyObject>>;
/// Thread-safe storage for objects which were inc_ref / dec_ref while the GIL was not held. /// Thread-safe storage for objects which were inc_ref / dec_ref while the GIL was not held.
struct ReferencePool { struct ReferencePool {
// .0 is INCREFs, .1 is DECREFs // .0 is INCREFs, .1 is DECREFs
pointer_ops: Mutex<(PyObjVec, PyObjVec)>, pointer_ops: sync::Mutex<(PyObjVec, PyObjVec)>,
} }
impl ReferencePool { impl ReferencePool {
const fn new() -> Self { const fn new() -> Self {
Self { Self {
pointer_ops: const_mutex((Vec::new(), Vec::new())), pointer_ops: sync::Mutex::new((Vec::new(), Vec::new())),
} }
} }
fn register_incref(&self, obj: NonNull<ffi::PyObject>) { fn register_incref(&self, obj: NonNull<ffi::PyObject>) {
self.pointer_ops.lock().0.push(obj); self.pointer_ops.lock().unwrap().0.push(obj);
} }
fn register_decref(&self, obj: NonNull<ffi::PyObject>) { fn register_decref(&self, obj: NonNull<ffi::PyObject>) {
self.pointer_ops.lock().1.push(obj); self.pointer_ops.lock().unwrap().1.push(obj);
} }
fn update_counts(&self, _py: Python<'_>) { fn update_counts(&self, _py: Python<'_>) {
let mut ops = self.pointer_ops.lock(); let mut ops = self.pointer_ops.lock().unwrap();
if ops.0.is_empty() && ops.1.is_empty() { if ops.0.is_empty() && ops.1.is_empty() {
return; return;
} }
@ -523,9 +510,9 @@ mod tests {
use super::{gil_is_acquired, GIL_COUNT, OWNED_OBJECTS, POOL}; use super::{gil_is_acquired, GIL_COUNT, OWNED_OBJECTS, POOL};
use crate::types::any::PyAnyMethods; use crate::types::any::PyAnyMethods;
use crate::{ffi, gil, PyObject, Python}; use crate::{ffi, gil, PyObject, Python};
#[cfg(not(target_arch = "wasm32"))]
use parking_lot::{const_mutex, Condvar, Mutex};
use std::ptr::NonNull; use std::ptr::NonNull;
#[cfg(not(target_arch = "wasm32"))]
use std::sync;
fn get_object(py: Python<'_>) -> PyObject { fn get_object(py: Python<'_>) -> PyObject {
py.eval_bound("object()", None, None).unwrap().unbind() py.eval_bound("object()", None, None).unwrap().unbind()
@ -543,6 +530,7 @@ mod tests {
!POOL !POOL
.pointer_ops .pointer_ops
.lock() .lock()
.unwrap()
.0 .0
.contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
} }
@ -551,6 +539,7 @@ mod tests {
!POOL !POOL
.pointer_ops .pointer_ops
.lock() .lock()
.unwrap()
.1 .1
.contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
} }
@ -559,6 +548,7 @@ mod tests {
fn pool_dec_refs_contains(obj: &PyObject) -> bool { fn pool_dec_refs_contains(obj: &PyObject) -> bool {
POOL.pointer_ops POOL.pointer_ops
.lock() .lock()
.unwrap()
.1 .1
.contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) }) .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
} }
@ -671,8 +661,8 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
assert_eq!(obj.get_refcnt(py), 1); assert_eq!(obj.get_refcnt(py), 1);
let non_null = unsafe { NonNull::new_unchecked(obj.as_ptr()) }; let non_null = unsafe { NonNull::new_unchecked(obj.as_ptr()) };
assert!(!POOL.pointer_ops.lock().0.contains(&non_null)); assert!(!POOL.pointer_ops.lock().unwrap().0.contains(&non_null));
assert!(!POOL.pointer_ops.lock().1.contains(&non_null)); assert!(!POOL.pointer_ops.lock().unwrap().1.contains(&non_null));
}); });
} }
@ -770,29 +760,30 @@ mod tests {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
struct Event { struct Event {
set: Mutex<bool>, set: sync::Mutex<bool>,
wait: Condvar, wait: sync::Condvar,
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
impl Event { impl Event {
const fn new() -> Self { const fn new() -> Self {
Self { Self {
set: const_mutex(false), set: sync::Mutex::new(false),
wait: Condvar::new(), wait: sync::Condvar::new(),
} }
} }
fn set(&self) { fn set(&self) {
*self.set.lock() = true; *self.set.lock().unwrap() = true;
self.wait.notify_all(); self.wait.notify_all();
} }
fn wait(&self) { fn wait(&self) {
let mut set = self.set.lock(); drop(
while !*set { self.wait
self.wait.wait(&mut set); .wait_while(self.set.lock().unwrap(), |s| !*s)
} .unwrap(),
);
} }
} }
@ -891,16 +882,16 @@ mod tests {
// The pointer should appear once in the incref pool, and once in the // The pointer should appear once in the incref pool, and once in the
// decref pool (for the clone being created and also dropped) // decref pool (for the clone being created and also dropped)
assert!(POOL.pointer_ops.lock().0.contains(&ptr)); assert!(POOL.pointer_ops.lock().unwrap().0.contains(&ptr));
assert!(POOL.pointer_ops.lock().1.contains(&ptr)); assert!(POOL.pointer_ops.lock().unwrap().1.contains(&ptr));
(obj, count, ptr) (obj, count, ptr)
}); });
Python::with_gil(|py| { Python::with_gil(|py| {
// Acquiring the gil clears the pool // Acquiring the gil clears the pool
assert!(!POOL.pointer_ops.lock().0.contains(&ptr)); assert!(!POOL.pointer_ops.lock().unwrap().0.contains(&ptr));
assert!(!POOL.pointer_ops.lock().1.contains(&ptr)); assert!(!POOL.pointer_ops.lock().unwrap().1.contains(&ptr));
// Overall count is still unchanged // Overall count is still unchanged
assert_eq!(count, obj.get_refcnt(py)); assert_eq!(count, obj.get_refcnt(py));

View File

@ -5,9 +5,17 @@ use std::{cell::UnsafeCell, marker::PhantomData};
#[cfg(all( #[cfg(all(
not(any(PyPy, GraalPy)), not(any(PyPy, GraalPy)),
Py_3_9, Py_3_9,
not(all(windows, Py_LIMITED_API, not(Py_3_10))) not(all(windows, Py_LIMITED_API, not(Py_3_10))),
not(target_has_atomic = "64"),
))] ))]
use portable_atomic::{AtomicI64, Ordering}; use portable_atomic::{AtomicI64, Ordering};
#[cfg(all(
not(any(PyPy, GraalPy)),
Py_3_9,
not(all(windows, Py_LIMITED_API, not(Py_3_10))),
target_has_atomic = "64",
))]
use std::sync::atomic::{AtomicI64, Ordering};
#[cfg(not(any(PyPy, GraalPy)))] #[cfg(not(any(PyPy, GraalPy)))]
use crate::exceptions::PyImportError; use crate::exceptions::PyImportError;

View File

@ -132,7 +132,7 @@
//! //!
//! PyO3 supports the following software versions: //! PyO3 supports the following software versions:
//! - Python 3.7 and up (CPython and PyPy) //! - Python 3.7 and up (CPython and PyPy)
//! - Rust 1.56 and up //! - Rust 1.63 and up
//! //!
//! # Example: Building a native Python module //! # Example: Building a native Python module
//! //!

View File

@ -3,6 +3,7 @@
use std::{task::Poll, thread, time::Duration}; use std::{task::Poll, thread, time::Duration};
use futures::{channel::oneshot, future::poll_fn, FutureExt}; use futures::{channel::oneshot, future::poll_fn, FutureExt};
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::{AtomicBool, Ordering}; use portable_atomic::{AtomicBool, Ordering};
use pyo3::{ use pyo3::{
coroutine::CancelHandle, coroutine::CancelHandle,
@ -10,6 +11,8 @@ use pyo3::{
py_run, py_run,
types::{IntoPyDict, PyType}, types::{IntoPyDict, PyType},
}; };
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicBool, Ordering};
#[path = "../src/tests/common.rs"] #[path = "../src/tests/common.rs"]
mod common; mod common;