From 70030f130dd9ad67e49af12a1ac9cb04c2ec0ca7 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Fri, 19 Nov 2021 10:45:27 +0000 Subject: [PATCH] python: drop support for 3.6 --- .github/workflows/ci.yml | 33 ++---- CHANGELOG.md | 1 + Cargo.toml | 1 - README.md | 2 +- guide/src/building_and_distribution.md | 10 +- guide/src/debugging.md | 2 +- guide/src/features.md | 2 +- guide/src/migration.md | 6 ++ pyo3-build-config/Cargo.toml | 1 - pyo3-build-config/build.rs | 2 +- pyo3-build-config/src/impl_.rs | 138 ++++++++----------------- pyo3-build-config/src/lib.rs | 2 +- src/class/methods.rs | 8 +- src/err/mod.rs | 7 +- src/exceptions.rs | 12 +-- src/ffi/abstract_.rs | 4 +- src/ffi/ceval.rs | 1 - src/ffi/cpython/import.rs | 16 +-- src/ffi/cpython/unicodeobject.rs | 5 - src/ffi/datetime.rs | 12 +-- src/ffi/intrcheck.rs | 5 +- src/ffi/methodobject.rs | 6 +- src/ffi/pystate.rs | 2 +- src/ffi/sliceobject.rs | 12 --- src/ffi/unicodeobject.rs | 5 +- src/gil.rs | 50 +-------- src/lib.rs | 4 +- src/python.rs | 10 +- tests/test_py36_init.rs | 9 -- tox.ini | 8 +- 30 files changed, 104 insertions(+), 272 deletions(-) delete mode 100644 tests/test_py36_init.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6dba6452ae..60668064148 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: Run cargo checks run: | set -x - VERSIONS=("3.6" "3.7" "3.8" "3.9" "3.10") + VERSIONS=("3.7" "3.8" "3.9" "3.10") for VERSION in ${VERSIONS[@]}; do echo "version=$VERSION" > config.txt echo "suppress_build_script_link_lines=true" >> config.txt @@ -76,7 +76,7 @@ jobs: fail-fast: false # If one platform fails, allow the rest to keep testing. matrix: rust: [stable] - python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy-3.6, pypy-3.7, pypy-3.8] + python-version: [3.7, 3.8, 3.9, "3.10", pypy-3.7, pypy-3.8] platform: [ { @@ -101,27 +101,12 @@ jobs: }, ] exclude: - # PyPy 3.6 is EOL and not working on macos-latest (now macos-11) - - python-version: pypy-3.6 - platform: { os: "macos-latest", python-architecture: "x64" } - # There is no 64-bit pypy on windows for pypy-3.6 - - python-version: pypy-3.6 - platform: { os: "windows-latest", python-architecture: "x64" } - # PyPy 3.7 on Windows doesn't release 32-bit builds any more + # PyPy doesn't release 32-bit Windows builds any more - python-version: pypy-3.7 platform: { os: "windows-latest", python-architecture: "x86" } - python-version: pypy-3.8 platform: { os: "windows-latest", python-architecture: "x86" } include: - # PyPy3.6 still runs on macos-10.15 - - rust: stable - python-version: pypy-3.6 - platform: - { - os: "macos-10.15", - python-architecture: "x64", - rust-target: "x86_64-apple-darwin", - } # Test minimal supported Rust version - rust: 1.48.0 python-version: "3.10" @@ -212,8 +197,8 @@ jobs: run: cargo build --lib --tests --no-default-features --features "${{ steps.settings.outputs.all_additive_features }}" - if: ${{ startsWith(matrix.python-version, 'pypy') }} - name: Build PyPy (abi3-py36) - run: cargo build --lib --tests --no-default-features --features "abi3-py36 ${{ steps.settings.outputs.all_additive_features }}" + name: Build PyPy (abi3-py37) + run: cargo build --lib --tests --no-default-features --features "abi3-py37 ${{ steps.settings.outputs.all_additive_features }}" # Run tests (except on PyPy, because no embedding API). - if: ${{ !startsWith(matrix.python-version, 'pypy') }} @@ -225,10 +210,10 @@ jobs: name: Test (abi3) run: cargo test --no-default-features --features "abi3 ${{ steps.settings.outputs.all_additive_features }}" - # Run tests again, for abi3-py36 (the minimal Python version) - - if: ${{ (!startsWith(matrix.python-version, 'pypy')) && (matrix.python-version != '3.6') }} - name: Test (abi3-py36) - run: cargo test --no-default-features --features "abi3-py36 ${{ steps.settings.outputs.all_additive_features }}" + # Run tests again, for abi3-py37 (the minimal Python version) + - if: ${{ (!startsWith(matrix.python-version, 'pypy')) && (matrix.python-version != '3.7') }} + name: Test (abi3-py37) + run: cargo test --no-default-features --features "abi3-py37 ${{ steps.settings.outputs.all_additive_features }}" - name: Test proc-macro code run: cargo test --manifest-path=pyo3-macros-backend/Cargo.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index df479ff1894..bb88dd882e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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) +- Drop support for Python 3.6, remove `abi3-py36` feature. [#2006](https://github.com/PyO3/pyo3/pull/2006) - `pyo3-build-config` no longer enables the `resolve-config` feature by default. [#2008](https://github.com/PyO3/pyo3/pull/2008) ### Added diff --git a/Cargo.toml b/Cargo.toml index cd8fb632283..4b21a6c2cdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,6 @@ extension-module = [] abi3 = ["pyo3-build-config/abi3"] # With abi3, we can manually set the minimum Python version. -abi3-py36 = ["abi3-py37", "pyo3-build-config/abi3-py36"] abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37"] abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"] abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"] diff --git a/README.md b/README.md index 34d8630e8e9..c07787b2691 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ## Usage PyO3 supports the following software versions: - - Python 3.6 and up (CPython and PyPy) + - Python 3.7 and up (CPython and PyPy) - 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/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index 0d63dd6c326..0644859d820 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -13,7 +13,7 @@ PyO3 uses a build script (backed by the [`pyo3-build-config`] crate) to determin - The `python` executable (if it's a Python 3 interpreter). - The `python3` executable. -You can override the Python interpreter by setting the `PYO3_PYTHON` environment variable, e.g. `PYO3_PYTHON=python3.6`, `PYO3_PYTHON=/usr/bin/python3.9`, or even a PyPy interpreter `PYO3_PYTHON=pypy3`. +You can override the Python interpreter by setting the `PYO3_PYTHON` environment variable, e.g. `PYO3_PYTHON=python3.7`, `PYO3_PYTHON=/usr/bin/python3.9`, or even a PyPy interpreter `PYO3_PYTHON=pypy3`. Once the Python interpreter is located, `pyo3-build-config` executes it to query the information in the `sysconfig` module which is needed to configure the rest of the compilation. @@ -145,16 +145,16 @@ See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https:// #### Minimum Python version for `abi3` -Because a single `abi3` wheel can be used with many different Python versions, PyO3 has feature flags `abi3-py36`, `abi3-py37`, `abi-py38` etc. to set the minimum required Python version for your `abi3` wheel. -For example, if you set the `abi3-py36` feature, your extension wheel can be used on all Python 3 versions from Python 3.6 and up. `maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp36-abi3-manylinux2020_x86_64.whl`. +Because a single `abi3` wheel can be used with many different Python versions, PyO3 has feature flags `abi3-py37`, `abi3-py38`, `abi3-py39` etc. to set the minimum required Python version for your `abi3` wheel. +For example, if you set the `abi3-py37` feature, your extension wheel can be used on all Python 3 versions from Python 3.7 and up. `maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp37-abi3-manylinux2020_x86_64.whl`. As your extension module may be run with multiple different Python versions you may occasionally find you need to check the Python version at runtime to customize behavior. See [the relevant section of this guide](./building_and_distribution/multiple_python_versions.html#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime. -PyO3 is only able to link your extension module to api3 version up to and including your host Python version. E.g., if you set `abi3-py38` and try to compile the crate with a host of Python 3.6, the build will fail. +PyO3 is only able to link your extension module to api3 version up to and including your host Python version. E.g., if you set `abi3-py38` and try to compile the crate with a host of Python 3.7, the build will fail. As an advanced feature, you can build PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set. On unix systems this works unconditionally; on Windows you must also set the `RUSTFLAGS` evironment variable to contain `-L native=/path/to/python/libs` so that the linker can find `python3.lib`. -> Note: If you set more that one of these api version feature flags the highest version always wins. For example, with both `abi3-py36` and `abi3-py38` set, PyO3 would build a wheel which supports Python 3.8 and up. +> Note: If you set more that one of these api version feature flags the lowest version always wins. For example, with both `abi3-py37` and `abi3-py38` set, PyO3 would build a wheel which supports Python 3.7 and up. #### Missing features diff --git a/guide/src/debugging.md b/guide/src/debugging.md index b7df5b40c9f..3a624d2d392 100644 --- a/guide/src/debugging.md +++ b/guide/src/debugging.md @@ -24,7 +24,7 @@ Valgrind is a tool to detect memory management bugs such as memory leaks. You first need to install a debug build of Python, otherwise Valgrind won't produce usable results. In Ubuntu there's e.g. a `python3-dbg` package. -Activate an environment with the debug interpreter and recompile. If you're on Linux, use `ldd` with the name of your binary and check that you're linking e.g. `libpython3.6dm.so.1.0` instead of `libpython3.6m.so.1.0`. +Activate an environment with the debug interpreter and recompile. If you're on Linux, use `ldd` with the name of your binary and check that you're linking e.g. `libpython3.7d.so.1.0` instead of `libpython3.7.so.1.0`. [Download the suppressions file for cpython](https://raw.githubusercontent.com/python/cpython/master/Misc/valgrind-python.supp). diff --git a/guide/src/features.md b/guide/src/features.md index 73ec50b97d9..5af4f3ec769 100644 --- a/guide/src/features.md +++ b/guide/src/features.md @@ -24,7 +24,7 @@ See the [building and distribution](building_and_distribution.md#py_limited_apia ### The `abi3-pyXY` features -(`abi3-py36`, `abi3-py37`, `abi3-py38`, `abi3-py39`, and `abi3-py310`) +(`abi3-py37`, `abi3-py38`, `abi3-py39`, and `abi3-py310`) These features are extensions of the `abi3` feature to specify the exact minimum Python version which the multiple-version-wheel will support. diff --git a/guide/src/migration.md b/guide/src/migration.md index 56a714accc6..5431d0093f4 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -3,6 +3,12 @@ This guide can help you upgrade code through breaking changes from one PyO3 version to the next. For a detailed list of all changes, see the [CHANGELOG](changelog.md). +## from 0.15.* to 0.16 + +### Drop support for older technogies + +PyO3 0.16 has increased minimum Rust version to 1.48 and minimum Python version to 3.7. This enables ore use of newer language features (enabling some of the other additions in 0.16) and simplifies maintenance of the project. + ## from 0.14.* to 0.15 ### Changes in sequence indexing diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 7e248083f8b..9dfdcd8279a 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -21,7 +21,6 @@ default = [] resolve-config = [] abi3 = [] -abi3-py36 = ["abi3-py37"] abi3-py37 = ["abi3-py38"] abi3-py38 = ["abi3-py39"] abi3-py39 = ["abi3-py310"] diff --git a/pyo3-build-config/build.rs b/pyo3-build-config/build.rs index 7e2cae9bc03..45a5bb7107b 100644 --- a/pyo3-build-config/build.rs +++ b/pyo3-build-config/build.rs @@ -63,7 +63,7 @@ pub fn abi3_config() -> Option { abi3: true, lib_name: None, lib_dir: None, - build_flags: BuildFlags::abi3(), + build_flags: BuildFlags::default(), pointer_width: None, executable: None, shared: true, diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 6a73b3b97d6..7fc37126b2e 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -18,7 +18,7 @@ use crate::{ }; /// Minimum Python version PyO3 supports. -const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 6 }; +const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 7 }; /// Maximum Python version that can be used as minimum required Python version with abi3. const ABI3_MAX_MINOR: u8 = 9; @@ -131,7 +131,10 @@ impl InterpreterConfig { pub fn emit_pyo3_cfgs(&self) { // This should have been checked during pyo3-build-config build time. assert!(self.version >= MINIMUM_SUPPORTED_VERSION); - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { + + // pyo3-build-config was released when Python 3.6 was supported, so minimum flag to emit is + // Py_3_6 (to avoid silently breaking users who depend on this cfg). + for i in 6..=self.version.minor { println!("cargo:rustc-cfg=Py_3_{}", i); } @@ -258,7 +261,7 @@ print("mingw", get_platform().startswith("mingw")) lib_dir, executable: map.get("executable").cloned(), pointer_width: Some(calcsize_pointer * 8), - build_flags: BuildFlags::from_interpreter(interpreter)?.fixup(version, implementation), + build_flags: BuildFlags::from_interpreter(interpreter)?.fixup(version), suppress_build_script_link_lines: false, extra_build_script_lines: vec![], }) @@ -280,7 +283,7 @@ print("mingw", get_platform().startswith("mingw")) macro_rules! parse_value { ($variable:ident, $value:ident) => { - $variable = Some($value.parse().context(format!( + $variable = Some($value.trim().parse().context(format!( concat!( "failed to parse ", stringify!($variable), @@ -347,14 +350,7 @@ print("mingw", get_platform().startswith("mingw")) lib_dir, executable, pointer_width, - build_flags: build_flags.unwrap_or_else(|| { - if abi3 { - BuildFlags::abi3() - } else { - BuildFlags::default() - } - .fixup(version, implementation) - }), + build_flags: build_flags.unwrap_or_default(), suppress_build_script_link_lines: suppress_build_script_link_lines.unwrap_or(false), extra_build_script_lines, }) @@ -557,7 +553,6 @@ fn cross_compiling() -> Result> { #[allow(non_camel_case_types)] #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum BuildFlag { - WITH_THREAD, Py_DEBUG, Py_REF_DEBUG, Py_TRACE_REFS, @@ -578,7 +573,6 @@ impl FromStr for BuildFlag { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { match s { - "WITH_THREAD" => Ok(BuildFlag::WITH_THREAD), "Py_DEBUG" => Ok(BuildFlag::Py_DEBUG), "Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG), "Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS), @@ -605,9 +599,7 @@ impl FromStr for BuildFlag { pub struct BuildFlags(pub HashSet); impl BuildFlags { - const ALL: [BuildFlag; 5] = [ - // TODO: Remove WITH_THREAD once Python 3.6 support dropped (as it's always on). - BuildFlag::WITH_THREAD, + const ALL: [BuildFlag; 4] = [ BuildFlag::Py_DEBUG, BuildFlag::Py_REF_DEBUG, BuildFlag::Py_TRACE_REFS, @@ -636,9 +628,10 @@ impl BuildFlags { /// the interpreter and printing variables of interest from /// sysconfig.get_config_vars. fn from_interpreter(interpreter: impl AsRef) -> Result { - // If we're on a Windows host, then Python won't have any useful config vars + // sysconfig is missing all the flags on windows, so we can't actually + // query the interpreter directly for its build flags. if cfg!(windows) { - return Ok(Self::windows_hardcoded()); + return Ok(Self::new()); } let mut script = String::from("import sysconfig\n"); @@ -665,21 +658,7 @@ impl BuildFlags { Ok(Self(flags)) } - fn windows_hardcoded() -> Self { - // sysconfig is missing all the flags on windows, so we can't actually - // query the interpreter directly for its build flags. - let mut flags = HashSet::new(); - flags.insert(BuildFlag::WITH_THREAD); - Self(flags) - } - - pub fn abi3() -> Self { - let mut flags = HashSet::new(); - flags.insert(BuildFlag::WITH_THREAD); - Self(flags) - } - - fn fixup(mut self, version: PythonVersion, implementation: PythonImplementation) -> Self { + fn fixup(mut self, version: PythonVersion) -> Self { if self.0.contains(&BuildFlag::Py_DEBUG) { self.0.insert(BuildFlag::Py_REF_DEBUG); if version <= PythonVersion::PY37 { @@ -688,11 +667,6 @@ impl BuildFlags { } } - // WITH_THREAD is always on for Python 3.7, and for PyPy. - if implementation == PythonImplementation::PyPy || version >= PythonVersion::PY37 { - self.0.insert(BuildFlag::WITH_THREAD); - } - self } } @@ -717,7 +691,7 @@ impl FromStr for BuildFlags { fn from_str(value: &str) -> Result { let mut flags = HashSet::new(); - for flag in value.split(',') { + for flag in value.split_terminator(',') { flags.insert(flag.parse().unwrap()); } Ok(BuildFlags(flags)) @@ -821,7 +795,7 @@ for key in KEYS: )), executable: None, pointer_width, - build_flags: BuildFlags::from_config_map(&sysconfigdata).fixup(version, implementation), + build_flags: BuildFlags::from_config_map(&sysconfigdata).fixup(version), suppress_build_script_link_lines: false, extra_build_script_lines: vec![], }) @@ -913,8 +887,6 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec vec![f.path()], // Python 3.7+ sysconfigdata with platform specifics Ok(f) if starts_with(f, "_sysconfigdata_") && ends_with(f, "py") => vec![f.path()], Ok(f) if f.metadata().map_or(false, |metadata| metadata.is_dir()) => { @@ -969,7 +941,6 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec meth.0, PyMethodType::PyCFunctionWithKeywords(meth) => unsafe { std::mem::transmute(meth.0) }, - #[cfg(all(Py_3_7, not(Py_LIMITED_API)))] + #[cfg(not(Py_LIMITED_API))] PyMethodType::PyCFunctionFastWithKeywords(meth) => unsafe { std::mem::transmute(meth.0) }, diff --git a/src/err/mod.rs b/src/err/mod.rs index db23c6782c5..a5c89a478fa 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -683,12 +683,7 @@ mod tests { let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].split(", "); assert_eq!(fields.next().unwrap(), "type: "); - if py.version_info() >= (3, 7) { - assert_eq!(fields.next().unwrap(), "value: Exception('banana')"); - } else { - // Python 3.6 and below formats the repr differently - assert_eq!(fields.next().unwrap(), ("value: Exception('banana',)")); - } + assert_eq!(fields.next().unwrap(), "value: Exception('banana')"); let traceback = fields.next().unwrap(); assert!(traceback.starts_with("traceback: Some(= (3, 7) { - assert_eq!(format!("{:?}", exc), "Exception('banana')"); - } else { - assert_eq!(format!("{:?}", exc), "Exception('banana',)"); - } + assert_eq!(format!("{:?}", exc), "Exception('banana')"); let source = exc.source().expect("cause should exist"); - if py.version_info() >= (3, 7) { - assert_eq!(format!("{:?}", source), "TypeError('peach')"); - } else { - assert_eq!(format!("{:?}", source), "TypeError('peach',)"); - } + assert_eq!(format!("{:?}", source), "TypeError('peach')"); let source_source = source.source(); assert!(source_source.is_none(), "source_source should be None"); diff --git a/src/ffi/abstract_.rs b/src/ffi/abstract_.rs index 3fa035b3b1c..07e02da4a52 100644 --- a/src/ffi/abstract_.rs +++ b/src/ffi/abstract_.rs @@ -91,7 +91,7 @@ extern "C" { pub fn PyObject_GetIter(arg1: *mut PyObject) -> *mut PyObject; } -// Defined as this macro in Python 3.6, 3.7 limited API, but relies on +// Defined as this macro in Python limited API, but relies on // non-limited PyTypeObject. Don't expose this since it cannot be used. #[cfg(not(any(Py_LIMITED_API, PyPy)))] #[inline] @@ -156,7 +156,7 @@ extern "C" { pub fn PyNumber_Or(o1: *mut PyObject, o2: *mut PyObject) -> *mut PyObject; } -// Defined as this macro in Python 3.6, 3.7 limited API, but relies on +// Defined as this macro in Python limited API, but relies on // non-limited PyTypeObject. Don't expose this since it cannot be used. #[cfg(not(any(Py_LIMITED_API, PyPy)))] #[inline] diff --git a/src/ffi/ceval.rs b/src/ffi/ceval.rs index 4f1dab8b430..4031daa7b47 100644 --- a/src/ffi/ceval.rs +++ b/src/ffi/ceval.rs @@ -67,7 +67,6 @@ extern "C" { pub fn PyEval_RestoreThread(arg1: *mut PyThreadState); } -#[cfg(py_sys_config = "WITH_THREAD")] extern "C" { #[cfg_attr(PyPy, link_name = "PyPyEval_ThreadsInitialized")] pub fn PyEval_ThreadsInitialized() -> c_int; diff --git a/src/ffi/cpython/import.rs b/src/ffi/cpython/import.rs index 00fe424b78c..f342c0e3bd0 100644 --- a/src/ffi/cpython/import.rs +++ b/src/ffi/cpython/import.rs @@ -6,33 +6,19 @@ use std::os::raw::{c_char, c_int, c_uchar}; extern "C" { pub fn _PyImport_IsInitialized(state: *mut PyInterpreterState) -> c_int; // skipped _PyImport_GetModuleId - #[cfg(Py_3_7)] pub fn _PyImport_SetModule(name: *mut PyObject, module: *mut PyObject) -> c_int; - #[cfg(Py_3_7)] pub fn _PyImport_SetModuleString(name: *const c_char, module: *mut PyObject) -> c_int; pub fn _PyImport_AcquireLock(); pub fn _PyImport_ReleaseLock() -> c_int; - #[cfg(not(Py_3_7))] - pub fn _PyImport_FindBuiltin(name: *const c_char) -> *mut PyObject; - #[cfg(all(Py_3_7, not(Py_3_9)))] + #[cfg(not(Py_3_9))] pub fn _PyImport_FindBuiltin(name: *const c_char, modules: *mut PyObject) -> *mut PyObject; #[cfg(not(Py_3_11))] pub fn _PyImport_FindExtensionObject(a: *mut PyObject, b: *mut PyObject) -> *mut PyObject; - #[cfg(not(Py_3_7))] - pub fn _PyImport_FixupBuiltin(module: *mut PyObject, name: *const c_char) -> c_int; - #[cfg(Py_3_7)] pub fn _PyImport_FixupBuiltin( module: *mut PyObject, name: *const c_char, modules: *mut PyObject, ) -> c_int; - #[cfg(not(Py_3_7))] - pub fn _PyImport_FixupExtensionObject( - a: *mut PyObject, - b: *mut PyObject, - c: *mut PyObject, - ) -> c_int; - #[cfg(Py_3_7)] pub fn _PyImport_FixupExtensionObject( a: *mut PyObject, b: *mut PyObject, diff --git a/src/ffi/cpython/unicodeobject.rs b/src/ffi/cpython/unicodeobject.rs index f357d91d9f5..00b832fcaac 100644 --- a/src/ffi/cpython/unicodeobject.rs +++ b/src/ffi/cpython/unicodeobject.rs @@ -323,14 +323,9 @@ extern "C" { extern "C" { // skipped _PyUnicode_AsStringAndSize - #[cfg(Py_3_7)] #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8")] pub fn PyUnicode_AsUTF8(unicode: *mut PyObject) -> *const c_char; - #[cfg(not(Py_3_7))] - #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8")] - pub fn PyUnicode_AsUTF8(unicode: *mut PyObject) -> *mut c_char; - // skipped _PyUnicode_AsString pub fn PyUnicode_Encode( diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index 8510b96edc4..a7b97ca178a 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -361,7 +361,7 @@ pub struct PyDateTime_CAPI { pub TimeType: *mut PyTypeObject, pub DeltaType: *mut PyTypeObject, pub TZInfoType: *mut PyTypeObject, - #[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] + #[cfg(not(all(PyPy, not(Py_3_8))))] pub TimeZone_UTC: *mut PyObject, pub Date_FromDate: unsafe extern "C" fn( year: c_int, @@ -395,7 +395,7 @@ pub struct PyDateTime_CAPI { normalize: c_int, cls: *mut PyTypeObject, ) -> *mut PyObject, - #[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] + #[cfg(not(all(PyPy, not(Py_3_8))))] pub TimeZone_FromTimeZone: unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, @@ -451,7 +451,7 @@ pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl { /// /// The type obtained by dereferencing this object is `&'static PyObject`. This may change in the /// future to be a more specific type representing that this is a `datetime.timezone` object. -#[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] +#[cfg(not(all(PyPy, not(Py_3_8))))] pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl { inner: &PyDateTimeAPI, }; @@ -609,12 +609,12 @@ impl Deref for _PyDateTimeAPI_impl { } #[doc(hidden)] -#[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] +#[cfg(not(all(PyPy, not(Py_3_8))))] pub struct _PyDateTime_TimeZone_UTC_impl { inner: &'static _PyDateTimeAPI_impl, } -#[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] +#[cfg(not(all(PyPy, not(Py_3_8))))] impl Deref for _PyDateTime_TimeZone_UTC_impl { type Target = crate::PyObject; @@ -661,7 +661,7 @@ mod tests { } #[test] - #[cfg(all(Py_3_7, any(not(PyPy), Py_3_8)))] + #[cfg(not(all(PyPy, not(Py_3_8))))] fn test_utc_timezone() { Python::with_gil(|py| { let utc_timezone = PyDateTime_TimeZone_UTC.as_ref(py); diff --git a/src/ffi/intrcheck.rs b/src/ffi/intrcheck.rs index ffd6c79c9d3..3b2738bbbd6 100644 --- a/src/ffi/intrcheck.rs +++ b/src/ffi/intrcheck.rs @@ -10,13 +10,10 @@ extern "C" { )] pub fn PyOS_InitInterrupts(); - #[cfg(any(not(Py_LIMITED_API), Py_3_7))] pub fn PyOS_BeforeFork(); - #[cfg(any(not(Py_LIMITED_API), Py_3_7))] pub fn PyOS_AfterFork_Parent(); - #[cfg(any(not(Py_LIMITED_API), Py_3_7))] pub fn PyOS_AfterFork_Child(); - #[cfg_attr(Py_3_7, deprecated(note = "use PyOS_AfterFork_Child instead"))] + #[deprecated(note = "use PyOS_AfterFork_Child instead")] #[cfg_attr(PyPy, link_name = "PyPyOS_AfterFork")] pub fn PyOS_AfterFork(); diff --git a/src/ffi/methodobject.rs b/src/ffi/methodobject.rs index b1b435d7c51..24dac732440 100644 --- a/src/ffi/methodobject.rs +++ b/src/ffi/methodobject.rs @@ -31,7 +31,7 @@ pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int { pub type PyCFunction = unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject; -#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +#[cfg(not(Py_LIMITED_API))] pub type _PyCFunctionFast = unsafe extern "C" fn( slf: *mut PyObject, args: *mut *mut PyObject, @@ -45,7 +45,7 @@ pub type PyCFunctionWithKeywords = unsafe extern "C" fn( kwds: *mut PyObject, ) -> *mut PyObject; -#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +#[cfg(not(Py_LIMITED_API))] pub type _PyCFunctionFastWithKeywords = unsafe extern "C" fn( slf: *mut PyObject, args: *const *mut PyObject, @@ -119,7 +119,7 @@ pub const METH_COEXIST: c_int = 0x0040; /* METH_FASTCALL indicates the PEP 590 Vectorcall calling format. It may be specified alone or with METH_KEYWORDS. */ -#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +#[cfg(not(Py_LIMITED_API))] pub const METH_FASTCALL: c_int = 0x0080; // skipped METH_STACKLESS diff --git a/src/ffi/pystate.rs b/src/ffi/pystate.rs index a00af79d8c4..4eb2049f114 100644 --- a/src/ffi/pystate.rs +++ b/src/ffi/pystate.rs @@ -25,7 +25,7 @@ extern "C" { #[cfg(all(Py_3_8, not(PyPy)))] pub fn PyInterpreterState_GetDict(arg1: *mut PyInterpreterState) -> *mut PyObject; - #[cfg(all(Py_3_7, not(PyPy)))] + #[cfg(not(PyPy))] pub fn PyInterpreterState_GetID(arg1: *mut PyInterpreterState) -> i64; #[cfg(not(PyPy))] diff --git a/src/ffi/sliceobject.rs b/src/ffi/sliceobject.rs index fa73102d7b7..31488bb443a 100644 --- a/src/ffi/sliceobject.rs +++ b/src/ffi/sliceobject.rs @@ -52,20 +52,8 @@ extern "C" { stop: *mut Py_ssize_t, step: *mut Py_ssize_t, ) -> c_int; - - #[cfg(not(Py_3_7))] - #[cfg_attr(PyPy, link_name = "PyPySlice_GetIndicesEx")] - pub fn PySlice_GetIndicesEx( - r: *mut PyObject, - length: Py_ssize_t, - start: *mut Py_ssize_t, - stop: *mut Py_ssize_t, - step: *mut Py_ssize_t, - slicelength: *mut Py_ssize_t, - ) -> c_int; } -#[cfg(Py_3_7)] #[inline] pub unsafe fn PySlice_GetIndicesEx( slice: *mut PyObject, diff --git a/src/ffi/unicodeobject.rs b/src/ffi/unicodeobject.rs index 61a60f8c101..2b02b37e5bb 100644 --- a/src/ffi/unicodeobject.rs +++ b/src/ffi/unicodeobject.rs @@ -165,12 +165,9 @@ extern "C" { ) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8String")] pub fn PyUnicode_AsUTF8String(unicode: *mut PyObject) -> *mut PyObject; - #[cfg(any(Py_3_10, all(Py_3_7, not(Py_LIMITED_API))))] + #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8AndSize")] pub fn PyUnicode_AsUTF8AndSize(unicode: *mut PyObject, size: *mut Py_ssize_t) -> *const c_char; - #[cfg(not(any(Py_3_7, Py_LIMITED_API)))] - #[cfg_attr(PyPy, link_name = "PyPyUnicode_AsUTF8AndSize")] - pub fn PyUnicode_AsUTF8AndSize(unicode: *mut PyObject, size: *mut Py_ssize_t) -> *mut c_char; #[cfg_attr(PyPy, link_name = "PyPyUnicode_DecodeUTF32")] pub fn PyUnicode_DecodeUTF32( string: *const c_char, diff --git a/src/gil.rs b/src/gil.rs index 0832ec6d31a..38e1db34e29 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -45,20 +45,12 @@ pub(crate) fn gil_is_acquired() -> bool { /// signal handling depends on the notion of a 'main thread', which must be the thread that /// initializes the Python interpreter. /// -/// If both the Python interpreter and Python threading are already initialized, this function has -/// no effect. +/// If the Python interpreter is already initialized, this function has no effect. /// /// This function is unavailable under PyPy because PyPy cannot be embedded in Rust (or any other /// software). Support for this is tracked on the /// [PyPy issue tracker](https://foss.heptapod.net/pypy/pypy/-/issues/3286). /// -/// Python 3.6 only: If the Python interpreter is initialized but Python threading is not, this -/// function will initialize Python threading. -/// -/// # Panics -/// - Python 3.6 only: If this function needs to initialize Python threading but the calling thread -/// is not the thread which initialized Python, this function will panic. -/// /// # Examples /// ```rust /// use pyo3::prelude::*; @@ -75,29 +67,6 @@ pub fn prepare_freethreaded_python() { // concurrent initialization of the Python runtime by other users of the Python C API. START.call_once_force(|_| unsafe { // 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(); - } - } 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); @@ -149,14 +118,7 @@ where ffi::Py_InitializeEx(0); - // Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have to - // call it yourself anymore. - #[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. + // Safety: the GIL is already held because of the Py_IntializeEx call. let pool = GILPool::new(); // Import the threading module - this ensures that it will associate this thread as the "main" @@ -231,14 +193,6 @@ impl GILGuard { Consider calling `pyo3::prepare_freethreaded_python()` before attempting \ to use Python APIs." ); - assert_ne!( - ffi::PyEval_ThreadsInitialized(), - 0, - "Python threading is not initalized and the `auto-initialize` feature is \ - not enabled.\n\n\ - Consider calling `pyo3::prepare_freethreaded_python()` before attempting \ - to use Python APIs." - ); }); } } diff --git a/src/lib.rs b/src/lib.rs index 08e476b7d80..7d7d44b7dcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ //! PyO3 uses `rustc`'s `--cfg` flags to enable or disable code used for different Python versions. //! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`] crate. //! -//! - `Py_3_6`, `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when +//! - `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when //! compiling for a given minimum Python version. //! - `Py_LIMITED_API`: Marks code enabled when the `abi3` feature flag is enabled. //! - `PyPy` - Marks code enabled when compiling for PyPy. @@ -110,7 +110,7 @@ //! # Minimum supported Rust and Python versions //! //! PyO3 supports the following software versions: -//! - Python 3.6 and up (CPython and PyPy) +//! - Python 3.7 and up (CPython and PyPy) //! - Rust 1.48 and up //! //! # Example: Building a native Python module diff --git a/src/python.rs b/src/python.rs index 911bf6bda48..52de8c256e7 100644 --- a/src/python.rs +++ b/src/python.rs @@ -550,9 +550,9 @@ impl<'py> Python<'py> { /// ```rust /// # use pyo3::Python; /// Python::with_gil(|py| { - /// // PyO3 supports Python 3.6 and up. - /// assert!(py.version_info() >= (3, 6)); - /// assert!(py.version_info() >= (3, 6, 0)); + /// // PyO3 supports Python 3.7 and up. + /// assert!(py.version_info() >= (3, 7)); + /// assert!(py.version_info() >= (3, 7, 0)); /// }); /// ``` pub fn version_info(self) -> PythonVersionInfo<'py> { @@ -891,10 +891,6 @@ mod tests { fn test_python_version_info() { Python::with_gil(|py| { let version = py.version_info(); - #[cfg(Py_3_6)] - assert!(version >= (3, 6)); - #[cfg(Py_3_6)] - assert!(version >= (3, 6, 0)); #[cfg(Py_3_7)] assert!(version >= (3, 7)); #[cfg(Py_3_7)] diff --git a/tests/test_py36_init.rs b/tests/test_py36_init.rs deleted file mode 100644 index d4f1c8e9ef1..00000000000 --- a/tests/test_py36_init.rs +++ /dev/null @@ -1,9 +0,0 @@ -// This test checks Python initialization on python 3.6, so needs to be standalone in its own process. - -#[cfg(not(PyPy))] -#[test] -fn test_py36_init_threads() { - unsafe { pyo3::ffi::Py_InitializeEx(0) }; - pyo3::prepare_freethreaded_python(); - assert_eq!(unsafe { pyo3::ffi::PyEval_ThreadsInitialized() }, 1); -} diff --git a/tox.ini b/tox.ini index 6bbebd29ec0..a1cfba86b94 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] -envlist = py35, - py36, - py37, +envlist = py37, py38, -minversion = 3.4.0 + py39, + py310, +minversion = 3.7.0 skip_missing_interpreters = true [testenv]