python: drop support for 3.6

This commit is contained in:
David Hewitt 2021-11-19 10:45:27 +00:00
parent 0dfe6b0274
commit 70030f130d
30 changed files with 104 additions and 272 deletions

View File

@ -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

View File

@ -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

View File

@ -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"]

View File

@ -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.

View File

@ -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

View File

@ -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).

View File

@ -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.

View File

@ -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

View File

@ -21,7 +21,6 @@ default = []
resolve-config = []
abi3 = []
abi3-py36 = ["abi3-py37"]
abi3-py37 = ["abi3-py38"]
abi3-py38 = ["abi3-py39"]
abi3-py39 = ["abi3-py310"]

View File

@ -63,7 +63,7 @@ pub fn abi3_config() -> Option<InterpreterConfig> {
abi3: true,
lib_name: None,
lib_dir: None,
build_flags: BuildFlags::abi3(),
build_flags: BuildFlags::default(),
pointer_width: None,
executable: None,
shared: true,

View File

@ -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<Option<CrossCompileConfig>> {
#[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<Self, Self::Err> {
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<BuildFlag>);
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<Path>) -> Result<Self> {
// 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<Self, Self::Err> {
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<Path>, cross: &CrossCompileConfig) -> Vec<Pat
};
for f in fs::read_dir(path).expect("Path does not exist").into_iter() {
sysconfig_paths.extend(match &f {
// Python 3.6 sysconfigdata without platform specifics
Ok(f) if f.file_name() == "_sysconfigdata.py" => 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<Path>, cross: &CrossCompileConfig) -> Vec<Pat
/// Find cross compilation information from sysconfigdata file
///
/// first find sysconfigdata file which follows the pattern [`_sysconfigdata_{abi}_{platform}_{multiarch}`][1]
/// on python 3.6 or greater. On python 3.5 it is simply `_sysconfigdata.py`.
///
/// [1]: https://github.com/python/cpython/blob/3.8/Lib/sysconfig.py#L348
fn load_cross_compile_from_sysconfigdata(
@ -1001,7 +972,7 @@ fn windows_hardcoded_cross_compile(
lib_dir: cross_compile_config.lib_dir.to_str().map(String::from),
executable: None,
pointer_width: None,
build_flags: BuildFlags::windows_hardcoded(),
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
})
@ -1216,7 +1187,7 @@ mod tests {
fn test_config_file_roundtrip() {
let config = InterpreterConfig {
abi3: true,
build_flags: BuildFlags::abi3(),
build_flags: BuildFlags::default(),
pointer_width: Some(32),
executable: Some("executable".into()),
implementation: PythonImplementation::CPython,
@ -1271,9 +1242,9 @@ mod tests {
fn test_config_file_defaults() {
// Only version is required
assert_eq!(
InterpreterConfig::from_reader(Cursor::new("version=3.6")).unwrap(),
InterpreterConfig::from_reader(Cursor::new("version=3.7")).unwrap(),
InterpreterConfig {
version: PythonVersion { major: 3, minor: 6 },
version: PythonVersion { major: 3, minor: 7 },
implementation: PythonImplementation::CPython,
shared: true,
abi3: false,
@ -1315,45 +1286,26 @@ mod tests {
}
#[test]
fn build_flags_fixup_py36_debug() {
fn build_flags_fixup_py37_debug() {
let mut build_flags = BuildFlags::new();
build_flags.0.insert(BuildFlag::Py_DEBUG);
build_flags = build_flags.fixup(
PythonVersion { major: 3, minor: 6 },
PythonImplementation::CPython,
);
build_flags = build_flags.fixup(PythonVersion { major: 3, minor: 7 });
// On 3.6, Py_DEBUG implies Py_REF_DEBUG and Py_TRACE_REFS
// On 3.7, Py_DEBUG implies Py_REF_DEBUG and Py_TRACE_REFS
assert!(build_flags.0.contains(&BuildFlag::Py_REF_DEBUG));
assert!(build_flags.0.contains(&BuildFlag::Py_TRACE_REFS));
}
#[test]
fn build_flags_fixup_py37_debug() {
fn build_flags_fixup_py38_debug() {
let mut build_flags = BuildFlags::new();
build_flags.0.insert(BuildFlag::Py_DEBUG);
build_flags = build_flags.fixup(PythonVersion::PY37, PythonImplementation::CPython);
build_flags = build_flags.fixup(PythonVersion { major: 3, minor: 8 });
// On 3.7, Py_DEBUG implies Py_REF_DEBUG
// On 3.8, Py_DEBUG implies Py_REF_DEBUG
assert!(build_flags.0.contains(&BuildFlag::Py_REF_DEBUG));
// 3.7 always has WITH_THREAD
assert!(build_flags.0.contains(&BuildFlag::WITH_THREAD));
}
#[test]
fn build_flags_fixup_pypy() {
let mut build_flags = BuildFlags::new();
build_flags = build_flags.fixup(
PythonVersion { major: 3, minor: 6 },
PythonImplementation::PyPy,
);
// PyPy always has WITH_THREAD
assert!(build_flags.0.contains(&BuildFlag::WITH_THREAD));
}
#[test]
@ -1377,7 +1329,7 @@ mod tests {
fn windows_hardcoded_cross_compile() {
let cross_config = CrossCompileConfig {
lib_dir: "C:\\some\\path".into(),
version: Some(PythonVersion { major: 3, minor: 6 }),
version: Some(PythonVersion { major: 3, minor: 7 }),
os: "os".into(),
arch: "arch".into(),
};
@ -1386,14 +1338,14 @@ mod tests {
super::windows_hardcoded_cross_compile(cross_config).unwrap(),
InterpreterConfig {
implementation: PythonImplementation::CPython,
version: PythonVersion { major: 3, minor: 6 },
version: PythonVersion { major: 3, minor: 7 },
shared: true,
abi3: false,
lib_name: Some("python36".into()),
lib_name: Some("python37".into()),
lib_dir: Some("C:\\some\\path".into()),
executable: None,
pointer_width: None,
build_flags: BuildFlags::windows_hardcoded(),
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
}
@ -1405,16 +1357,16 @@ mod tests {
use PythonImplementation::*;
assert_eq!(
super::default_lib_name_windows(
PythonVersion { major: 3, minor: 6 },
PythonVersion { major: 3, minor: 7 },
CPython,
false,
false
),
"python36",
"python37",
);
assert_eq!(
super::default_lib_name_windows(
PythonVersion { major: 3, minor: 6 },
PythonVersion { major: 3, minor: 7 },
CPython,
true,
false
@ -1423,16 +1375,16 @@ mod tests {
);
assert_eq!(
super::default_lib_name_windows(
PythonVersion { major: 3, minor: 6 },
PythonVersion { major: 3, minor: 7 },
CPython,
false,
true
),
"python3.6",
"python3.7",
);
assert_eq!(
super::default_lib_name_windows(
PythonVersion { major: 3, minor: 6 },
PythonVersion { major: 3, minor: 7 },
CPython,
true,
true
@ -1441,12 +1393,12 @@ mod tests {
);
assert_eq!(
super::default_lib_name_windows(
PythonVersion { major: 3, minor: 6 },
PythonVersion { major: 3, minor: 7 },
PyPy,
true,
false
),
"python36",
"python37",
);
}
@ -1455,8 +1407,8 @@ mod tests {
use PythonImplementation::*;
// Defaults to pythonX.Y for CPython
assert_eq!(
super::default_lib_name_unix(PythonVersion { major: 3, minor: 6 }, CPython, None),
"python3.6",
super::default_lib_name_unix(PythonVersion { major: 3, minor: 7 }, CPython, None),
"python3.7",
);
assert_eq!(
super::default_lib_name_unix(PythonVersion { major: 3, minor: 9 }, CPython, None),
@ -1483,7 +1435,7 @@ mod tests {
fn interpreter_version_reduced_to_abi3() {
let mut config = InterpreterConfig {
abi3: true,
build_flags: BuildFlags::new(),
build_flags: BuildFlags::default(),
pointer_width: None,
executable: None,
implementation: PythonImplementation::CPython,
@ -1495,8 +1447,8 @@ mod tests {
extra_build_script_lines: vec![],
};
fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 6 })).unwrap();
assert_eq!(config.version, PythonVersion { major: 3, minor: 6 });
fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 7 })).unwrap();
assert_eq!(config.version, PythonVersion { major: 3, minor: 7 });
}
#[test]
@ -1510,16 +1462,16 @@ mod tests {
lib_dir: None,
lib_name: None,
shared: true,
version: PythonVersion { major: 3, minor: 6 },
version: PythonVersion { major: 3, minor: 7 },
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
};
assert!(
fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 7 }))
fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 8 }))
.unwrap_err()
.to_string()
.contains("cannot set a minimum Python version 3.7 higher than the interpreter version 3.6")
.contains("cannot set a minimum Python version 3.8 higher than the interpreter version 3.7")
);
}

View File

@ -25,7 +25,7 @@ pub use impl_::{BuildFlag, BuildFlags, InterpreterConfig, PythonImplementation,
///
/// | Flag | Description |
/// | ---- | ----------- |
/// | `#[cfg(Py_3_6)]`, `#[cfg(Py_3_7)]`, `#[cfg(Py_3_8)]`, `#[cfg(Py_3_9)]`, `#[cfg(Py_3_10)]` | These attributes mark code only for a given Python version and up. For example, `#[cfg(Py_3_6)]` marks code which can run on Python 3.6 **and newer**. |
/// | `#[cfg(Py_3_7)]`, `#[cfg(Py_3_8)]`, `#[cfg(Py_3_9)]`, `#[cfg(Py_3_10)]` | These attributes mark code only for a given Python version and up. For example, `#[cfg(Py_3_7)]` marks code which can run on Python 3.7 **and newer**. |
/// | `#[cfg(Py_LIMITED_API)]` | This marks code which is run when compiling with PyO3's `abi3` feature enabled. |
/// | `#[cfg(PyPy)]` | This marks code which is run when compiling for PyPy. |
///

View File

@ -28,7 +28,7 @@ pub enum PyMethodDefType {
pub enum PyMethodType {
PyCFunction(PyCFunction),
PyCFunctionWithKeywords(PyCFunctionWithKeywords),
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
#[cfg(not(Py_LIMITED_API))]
PyCFunctionFastWithKeywords(PyCFunctionFastWithKeywords),
}
@ -38,7 +38,7 @@ pub enum PyMethodType {
pub struct PyCFunction(pub ffi::PyCFunction);
#[derive(Clone, Copy, Debug)]
pub struct PyCFunctionWithKeywords(pub ffi::PyCFunctionWithKeywords);
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
#[cfg(not(Py_LIMITED_API))]
#[derive(Clone, Copy, Debug)]
pub struct PyCFunctionFastWithKeywords(pub ffi::_PyCFunctionFastWithKeywords);
#[derive(Clone, Copy, Debug)]
@ -111,7 +111,7 @@ impl PyMethodDef {
}
/// Define a function that can take `*args` and `**kwargs`.
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
#[cfg(not(Py_LIMITED_API))]
pub const fn fastcall_cfunction_with_keywords(
name: &'static str,
cfunction: PyCFunctionFastWithKeywords,
@ -135,7 +135,7 @@ impl PyMethodDef {
let meth = match self.ml_meth {
PyMethodType::PyCFunction(meth) => 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)
},

View File

@ -683,12 +683,7 @@ mod tests {
let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].split(", ");
assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
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(<traceback object at 0x"));

View File

@ -794,19 +794,11 @@ mod tests {
.into_instance(py)
.into_ref(py);
if py.version_info() >= (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");

View File

@ -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]

View File

@ -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;

View File

@ -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,

View File

@ -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(

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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))]

View File

@ -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,

View File

@ -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,

View File

@ -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 dont 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."
);
});
}
}

View File

@ -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

View File

@ -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)]

View File

@ -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);
}

View File

@ -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]