From 7e8a07c663cab91c37cef826a954e8090ba944b7 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Thu, 18 Nov 2021 11:38:47 +0000 Subject: [PATCH] msrv: bump to 1.48 --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 6 +++ Cargo.toml | 13 ++---- README.md | 4 +- pyo3-macros-backend/src/method.rs | 11 ++--- pyo3-macros-backend/src/proto_method.rs | 4 +- pyo3-macros-backend/src/pymethod.rs | 11 ++--- pyo3-macros-backend/src/utils.rs | 12 ++--- src/buffer.rs | 7 +-- src/class/impl_.rs | 8 ++-- src/ffi/ceval.rs | 1 - src/gil.rs | 51 +++++++++++---------- src/lib.rs | 2 +- src/pyclass.rs | 16 +++---- src/types/string.rs | 5 +- tests/test_compile_error.rs | 11 +---- tests/test_not_msrv.rs | 4 +- tests/ui/abi3_nativetype_inheritance.stderr | 18 ++++---- tests/ui/deprecations.rs | 3 -- tests/ui/pyclass_send.stderr | 4 +- 20 files changed, 80 insertions(+), 113 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46c04767081..b6dba6452ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,7 +123,7 @@ jobs: rust-target: "x86_64-apple-darwin", } # Test minimal supported Rust version - - rust: 1.41.1 + - rust: 1.48.0 python-version: "3.10" platform: { diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a416212b92..d1a3dd63058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Packaging + +- Update MSRV to Rust 1.48. [#2004](https://github.com/PyO3/pyo3/pull/2004) +- Update `indoc` optional dependency to 1.0. [#2004](https://github.com/PyO3/pyo3/pull/2004) +- Update `paste` optional dependency to 1.0. [#2004](https://github.com/PyO3/pyo3/pull/2004) + ### Added - Add implementations for `Py::as_ref()` and `Py::into_ref()` for `Py`, `Py` and `Py`. [#1682](https://github.com/PyO3/pyo3/pull/1682) diff --git a/Cargo.toml b/Cargo.toml index b7790271c62..ce998a9d208 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,8 @@ parking_lot = "0.11.0" # support crates for macros feature pyo3-macros = { path = "pyo3-macros", version = "=0.15.0", optional = true } -# indoc must stay at 0.3.x for Rust 1.41 compatibility -indoc = { version = "0.3.6", optional = true } -paste = { version = "0.1.18", optional = true } +indoc = { version = "1.0.3", optional = true } +paste = { version = "1.0.6", optional = true } unindent = { version = "0.1.4", optional = true } # support crate for multiple-pymethods feature @@ -32,7 +31,7 @@ inventory = { version = "0.1.4", optional = true } # crate integrations that can be added using the eponymous features anyhow = { version = "1.0", optional = true } -eyre = { version = ">= 0.4, < 0.7" , optional = true } +eyre = { version = ">= 0.4, < 0.7", optional = true } hashbrown = { version = ">= 0.9, < 0.12", optional = true } indexmap = { version = ">= 1.6, < 1.8", optional = true } num-bigint = { version = "0.4", optional = true } @@ -41,11 +40,7 @@ serde = { version = "1.0", optional = true } [dev-dependencies] assert_approx_eq = "1.1.0" -# O.3.5 uses the matches! macro, which isn't compatible with Rust 1.41 -criterion = "=0.3.4" -# half and bitflags use if/match in const fn, which isn't compatible with Rust 1.41 -half = "=1.7.1" -bitflags = "=1.2.1" +criterion = "0.3.5" trybuild = "1.0.49" rustversion = "1.0" # 1.0.0 requires Rust 1.50 diff --git a/README.md b/README.md index fdfb1c2a0a5..df1f469f499 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![benchmark](https://github.com/PyO3/pyo3/actions/workflows/bench.yml/badge.svg)](https://pyo3.rs/dev/bench/) [![codecov](https://codecov.io/gh/PyO3/pyo3/branch/main/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3) [![crates.io](https://img.shields.io/crates/v/pyo3)](https://crates.io/crates/pyo3) -[![minimum rustc 1.41](https://img.shields.io/badge/rustc-1.41+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) +[![minimum rustc 1.48](https://img.shields.io/badge/rustc-1.48+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby) [![contributing notes](https://img.shields.io/badge/contribute-on%20github-Green)](https://github.com/PyO3/pyo3/blob/main/Contributing.md) @@ -18,7 +18,7 @@ PyO3 supports the following software versions: - Python 3.6 and up (CPython and PyPy) - - Rust 1.41 and up + - Rust 1.48 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. diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index e57ef33780b..6a7b8432d9f 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -352,15 +352,12 @@ impl<'a> FnSpec<'a> { parse_method_receiver(first_arg) }; - #[allow(clippy::manual_strip)] // for strip_prefix replacement supporting rust < 1.45 // strip get_ or set_ let strip_fn_name = |prefix: &'static str| { - let ident = name.unraw().to_string(); - if ident.starts_with(prefix) { - Some(syn::Ident::new(&ident[prefix.len()..], ident.span())) - } else { - None - } + name.unraw() + .to_string() + .strip_prefix(prefix) + .map(|stripped| syn::Ident::new(stripped, name.span())) }; let (fn_type, skip_first_arg, fixed_convention) = match fn_type_attr { diff --git a/pyo3-macros-backend/src/proto_method.rs b/pyo3-macros-backend/src/proto_method.rs index 3c14843fd78..648a9734258 100644 --- a/pyo3-macros-backend/src/proto_method.rs +++ b/pyo3-macros-backend/src/proto_method.rs @@ -15,13 +15,11 @@ pub struct MethodProto { } impl MethodProto { - // TODO: workaround for no unsized casts in const fn on Rust 1.45 (stable in 1.46) - const EMPTY_ARGS: &'static [&'static str] = &[]; pub const fn new(name: &'static str, proto: &'static str) -> Self { MethodProto { name, proto, - args: MethodProto::EMPTY_ARGS, + args: &[], with_self: false, with_result: true, } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 13a49f92734..f425dec9adf 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -399,14 +399,9 @@ pub fn impl_py_getter_def(cls: &syn::Type, property_type: PropertyType) -> Resul /// Split an argument of pyo3::Python from the front of the arg list, if present fn split_off_python_arg<'a>(args: &'a [FnArg<'a>]) -> (Option<&FnArg>, &[FnArg]) { - if args - .get(0) - .map(|py| utils::is_python(py.ty)) - .unwrap_or(false) - { - (Some(&args[0]), &args[1..]) - } else { - (None, args) + match args { + [py, args @ ..] if utils::is_python(&py.ty) => (Some(py), args), + args => (None, args), } } diff --git a/pyo3-macros-backend/src/utils.rs b/pyo3-macros-backend/src/utils.rs index b0353cdc16a..3c3e510248a 100644 --- a/pyo3-macros-backend/src/utils.rs +++ b/pyo3-macros-backend/src/utils.rs @@ -62,8 +62,6 @@ pub fn option_type_argument(ty: &syn::Type) -> Option<&syn::Type> { #[derive(Clone)] pub struct PythonDoc(TokenStream); -// TODO(#1782) use strip_prefix on Rust 1.45 or greater -#[allow(clippy::manual_strip)] /// Collects all #[doc = "..."] attributes into a TokenStream evaluating to a null-terminated string /// e.g. concat!("...", "\n", "\0") pub fn get_doc( @@ -107,11 +105,11 @@ pub fn get_doc( // Strip single left space from literal strings, if needed. // e.g. `/// Hello world` expands to #[doc = " Hello world"] let doc_line = lit_str.value(); - if doc_line.starts_with(' ') { - syn::LitStr::new(&doc_line[1..], lit_str.span()).to_tokens(tokens) - } else { - lit_str.to_tokens(tokens) - } + doc_line + .strip_prefix(' ') + .map(|stripped| syn::LitStr::new(stripped, lit_str.span())) + .unwrap_or(lit_str) + .to_tokens(tokens); } else { // This is probably a macro doc from Rust 1.54, e.g. #[doc = include_str!(...)] token_stream.to_tokens(tokens) diff --git a/src/buffer.rs b/src/buffer.rs index 07f6321aecd..f155bb11162 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -75,12 +75,7 @@ impl ElementType { pub fn from_format(format: &CStr) -> ElementType { match format.to_bytes() { [char] | [b'@', char] => native_element_type_from_type_char(*char), - [modifier, char] - if (*modifier == b'=' - || *modifier == b'<' - || *modifier == b'>' - || *modifier == b'!') => - { + [modifier, char] if matches!(modifier, b'=' | b'<' | b'>' | b'!') => { standard_element_type_from_type_char(*char) } _ => ElementType::Unknown, diff --git a/src/class/impl_.rs b/src/class/impl_.rs index e9fb6492034..7924b1bf784 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -542,7 +542,6 @@ pub unsafe extern "C" fn alloc_with_freelist( /// # Safety /// - `obj` must be a valid pointer to an instance of T (not a subclass). /// - The GIL must be held. -#[allow(clippy::collapsible_if)] // for if cfg! pub unsafe extern "C" fn free_with_freelist(obj: *mut c_void) { let obj = obj as *mut ffi::PyObject; debug_assert_eq!( @@ -560,10 +559,9 @@ pub unsafe extern "C" fn free_with_freelist(obj: *mut c_ }; free(obj as *mut c_void); - if cfg!(Py_3_8) { - if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 { - ffi::Py_DECREF(ty as *mut ffi::PyObject); - } + #[cfg(Py_3_8)] + if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 { + ffi::Py_DECREF(ty as *mut ffi::PyObject); } } } diff --git a/src/ffi/ceval.rs b/src/ffi/ceval.rs index 202c2b1ec74..4f1dab8b430 100644 --- a/src/ffi/ceval.rs +++ b/src/ffi/ceval.rs @@ -52,7 +52,6 @@ extern "C" { fn _Py_CheckRecursiveCall(_where: *mut c_char) -> c_int; } -// TODO // skipped Py_EnterRecursiveCall // skipped Py_LeaveRecursiveCall diff --git a/src/gil.rs b/src/gil.rs index 588d45f1fe6..0832ec6d31a 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -69,33 +69,36 @@ pub(crate) fn gil_is_acquired() -> bool { /// # } /// ``` #[cfg(not(PyPy))] -#[allow(clippy::collapsible_if)] // for if cfg! pub fn prepare_freethreaded_python() { // Protect against race conditions when Python is not yet initialized and multiple threads // concurrently call 'prepare_freethreaded_python()'. 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 { - if cfg!(not(Py_3_7)) { - // Use call_once_force because if initialization panics, it's okay to try again. - if ffi::Py_IsInitialized() != 0 { - if ffi::PyEval_ThreadsInitialized() == 0 { - // We can only safely initialize threads if this thread holds the GIL. - assert!( - !ffi::PyGILState_GetThisThreadState().is_null(), - "Python threading is not initialized and cannot be initialized by this \ - thread, because it is not the thread which initialized Python." - ); - ffi::PyEval_InitThreads(); - } - } else { - ffi::Py_InitializeEx(0); + // Use call_once_force because if initialization panics, it's okay to try again. + + // TODO(#1782) - Python 3.6 legacy code + #[cfg(not(Py_3_7))] + if ffi::Py_IsInitialized() != 0 { + if ffi::PyEval_ThreadsInitialized() == 0 { + // We can only safely initialize threads if this thread holds the GIL. + assert!( + !ffi::PyGILState_GetThisThreadState().is_null(), + "Python threading is not initialized and cannot be initialized by this \ + thread, because it is not the thread which initialized Python." + ); ffi::PyEval_InitThreads(); - - // Release the GIL. - ffi::PyEval_SaveThread(); } - } else if ffi::Py_IsInitialized() == 0 { - // In Python 3.7 and up PyEval_InitThreads is irrelevant. + } else { + ffi::Py_InitializeEx(0); + ffi::PyEval_InitThreads(); + + // Release the GIL. + ffi::PyEval_SaveThread(); + } + + // In Python 3.7 and up PyEval_InitThreads is irrelevant. + #[cfg(Py_3_7)] + if ffi::Py_IsInitialized() == 0 { ffi::Py_InitializeEx(0); // Release the GIL. @@ -134,7 +137,6 @@ pub fn prepare_freethreaded_python() { /// # } /// ``` #[cfg(not(PyPy))] -#[allow(clippy::collapsible_if)] // for if cfg! pub unsafe fn with_embedded_python_interpreter(f: F) -> R where F: for<'p> FnOnce(Python<'p>) -> R, @@ -149,10 +151,9 @@ where // Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have to // call it yourself anymore. - if cfg!(not(Py_3_7)) { - if ffi::PyEval_ThreadsInitialized() == 0 { - ffi::PyEval_InitThreads(); - } + #[cfg(not(Py_3_7))] + if ffi::PyEval_ThreadsInitialized() == 0 { + ffi::PyEval_InitThreads(); } // Safe: the GIL is already held because of the Py_IntializeEx call. diff --git a/src/lib.rs b/src/lib.rs index 14bc7466d3e..08e476b7d80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ //! //! PyO3 supports the following software versions: //! - Python 3.6 and up (CPython and PyPy) -//! - Rust 1.41 and up +//! - Rust 1.48 and up //! //! # Example: Building a native Python module //! diff --git a/src/pyclass.rs b/src/pyclass.rs index fc8a4efae93..132fe599ede 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -83,7 +83,8 @@ where slots.push(ffi::Py_tp_free, free as _); } - if cfg!(Py_3_9) { + #[cfg(Py_3_9)] + { let members = py_class_members::(); if !members.is_empty() { slots.push(ffi::Py_tp_members, into_raw(members)) @@ -155,7 +156,8 @@ fn tp_init_additional(type_object: *mut ffi::PyTypeObject) { // Setting buffer protocols via slots doesn't work until Python 3.9, so on older versions we // must manually fixup the type object. - if cfg!(not(Py_3_9)) { + #[cfg(not(Py_3_9))] + { if let Some(buffer) = T::get_buffer() { unsafe { (*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer; @@ -166,7 +168,8 @@ fn tp_init_additional(type_object: *mut ffi::PyTypeObject) { // Setting tp_dictoffset and tp_weaklistoffset via slots doesn't work until Python 3.9, so on // older versions again we must fixup the type object. - if cfg!(not(Py_3_9)) { + #[cfg(not(Py_3_9))] + { // __dict__ support if let Some(dict_offset) = PyCell::::dict_offset() { unsafe { @@ -258,12 +261,6 @@ fn py_class_members() -> Vec { members } -// Stub needed since the `if cfg!()` above still compiles contained code. -#[cfg(not(Py_3_9))] -fn py_class_members() -> Vec { - vec![] -} - const PY_GET_SET_DEF_INIT: ffi::PyGetSetDef = ffi::PyGetSetDef { name: ptr::null_mut(), get: None, @@ -272,7 +269,6 @@ const PY_GET_SET_DEF_INIT: ffi::PyGetSetDef = ffi::PyGetSetDef { closure: ptr::null_mut(), }; -#[allow(clippy::collapsible_if)] // for if cfg! fn py_class_properties( is_dummy: bool, for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])), diff --git a/src/types/string.rs b/src/types/string.rs index 4c5ff52d91d..28eb89f6163 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -227,8 +227,9 @@ impl PyString { pub unsafe fn data(&self) -> PyResult> { let ptr = self.as_ptr(); - if cfg!(not(Py_3_12)) { - #[allow(deprecated)] + #[cfg(not(Py_3_12))] + #[allow(deprecated)] + { let ready = ffi::PyUnicode_READY(ptr); if ready != 0 { // Exception was created on failure. diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 4751f774d0e..4daab2679c4 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -23,22 +23,15 @@ fn _test_compile_errors() { t.compile_fail("tests/ui/invalid_pymethods.rs"); t.compile_fail("tests/ui/invalid_pymethod_names.rs"); t.compile_fail("tests/ui/invalid_argument_attributes.rs"); + t.compile_fail("tests/ui/missing_clone.rs"); t.compile_fail("tests/ui/reject_generics.rs"); + t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); - tests_rust_1_48(&t); tests_rust_1_49(&t); tests_rust_1_54(&t); tests_rust_1_55(&t); tests_rust_1_56(&t); - #[rustversion::since(1.48)] - fn tests_rust_1_48(t: &trybuild::TestCases) { - t.compile_fail("tests/ui/missing_clone.rs"); - t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); - } - #[rustversion::before(1.48)] - fn tests_rust_1_48(_t: &trybuild::TestCases) {} - #[rustversion::since(1.49)] fn tests_rust_1_49(t: &trybuild::TestCases) { t.compile_fail("tests/ui/deprecations.rs"); diff --git a/tests/test_not_msrv.rs b/tests/test_not_msrv.rs index d432c98012a..b484520e7bf 100644 --- a/tests/test_not_msrv.rs +++ b/tests/test_not_msrv.rs @@ -2,9 +2,7 @@ //! but can't even be cfg-ed out on MSRV because the compiler doesn't support //! the syntax. -// TODO(#1782) rustversion attribute can't go on modules until Rust 1.42, so this -// funky dance has to happen... +#[rustversion::since(1.54)] mod requires_1_54 { - #[rustversion::since(1.54)] include!("not_msrv/requires_1_54.rs"); } diff --git a/tests/ui/abi3_nativetype_inheritance.stderr b/tests/ui/abi3_nativetype_inheritance.stderr index a90d40442f5..6faab088dbb 100644 --- a/tests/ui/abi3_nativetype_inheritance.stderr +++ b/tests/ui/abi3_nativetype_inheritance.stderr @@ -6,15 +6,15 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied | = note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict` note: required by a bound in `PyClassBaseType` - --> src/class/impl_.rs:766:1 + --> src/class/impl_.rs | -766 | / pub trait PyClassBaseType: Sized { -767 | | type Dict; -768 | | type WeakRef; -769 | | type LayoutAsBase: PyCellLayout; + | / pub trait PyClassBaseType: Sized { + | | type Dict; + | | type WeakRef; + | | type LayoutAsBase: PyCellLayout; ... | -772 | | type Initializer: PyObjectInit; -773 | | } + | | type Initializer: PyObjectInit; + | | } | |_^ required by this bound in `PyClassBaseType` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -26,8 +26,8 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied | = note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict` note: required by a bound in `ThreadCheckerInherited` - --> src/class/impl_.rs:753:47 + --> src/class/impl_.rs | -753 | pub struct ThreadCheckerInherited(PhantomData, U::ThreadChecker); + | pub struct ThreadCheckerInherited(PhantomData, U::ThreadChecker); | ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/deprecations.rs b/tests/ui/deprecations.rs index 50236b39a82..7938fbe69d2 100644 --- a/tests/ui/deprecations.rs +++ b/tests/ui/deprecations.rs @@ -50,6 +50,3 @@ fn my_module(_py: Python, m: &PyModule) -> PyResult<()> { fn main() { } - - -// TODO: ensure name deprecated on #[pyfunction] and #[pymodule] diff --git a/tests/ui/pyclass_send.stderr b/tests/ui/pyclass_send.stderr index 56217f1e916..531a4e9df05 100644 --- a/tests/ui/pyclass_send.stderr +++ b/tests/ui/pyclass_send.stderr @@ -11,8 +11,8 @@ note: required because it appears within the type `NotThreadSafe` 5 | struct NotThreadSafe { | ^^^^^^^^^^^^^ note: required by a bound in `ThreadCheckerStub` - --> src/class/impl_.rs:710:33 + --> src/class/impl_.rs | -710 | pub struct ThreadCheckerStub(PhantomData); + | pub struct ThreadCheckerStub(PhantomData); | ^^^^ required by this bound in `ThreadCheckerStub` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)