diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0fdcb6ea5..39236e4320e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add support for building for CPython limited API. This required a few minor changes to runtime behaviour of of pyo3 `#[pyclass]` types. See the migration guide for full details. [#1152](https://github.com/PyO3/pyo3/pull/1152) + - Add feature flags `abi3-py*` to set the minimum Python version when using the limited API. [#1263]((https://github.com/PyO3/pyo3/pull/1263)) - Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212) - Add FFI definitions for PEP 587 "Python Initialization Configuration". [#1247](https://github.com/PyO3/pyo3/pull/1247) - Add `PyEval_SetProfile` and `PyEval_SetTrace` to FFI. [#1255](https://github.com/PyO3/pyo3/pull/1255) diff --git a/Cargo.toml b/Cargo.toml index 77c8355bf7e..16b99de07e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,13 @@ rustversion = "1.0" [features] default = ["macros"] macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] -# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for -# more. +# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more. abi3 = [] +# With abi3, we can manually set the minimum Python version. +abi3-py36 = ["abi3-py37"] +abi3-py37 = ["abi3-py38"] +abi3-py38 = ["abi3-py39"] +abi3-py39 = ["abi3"] # Optimizes PyObject to Vec conversion and so on. nightly = [] diff --git a/build.rs b/build.rs index ba226056b45..03ea00f61ae 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,10 @@ use std::{ str::FromStr, }; -const PY3_MIN_MINOR: u8 = 5; +/// Minimum required Python version. +const PY3_MIN_MINOR: u8 = 6; +/// Maximum Python version that can be used as minimum required Python version with abi3. +const ABI3_MAX_MINOR: u8 = 9; const CFG_KEY: &str = "py_sys_config"; type Result = std::result::Result>; @@ -784,12 +787,25 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result { bail!("Python 2 is not supported"); } - if env::var_os("CARGO_FEATURE_ABI3").is_some() { + let minor = if env::var_os("CARGO_FEATURE_ABI3").is_some() { println!("cargo:rustc-cfg=Py_LIMITED_API"); - } + // Check any `abi3-py3*` feature is set. If not, use the interpreter version. + let abi3_minor = (PY3_MIN_MINOR..=ABI3_MAX_MINOR) + .find(|i| env::var_os(format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); + match (abi3_minor, interpreter_config.version.minor) { + (Some(abi3_minor), Some(interpreter_minor)) if abi3_minor > interpreter_minor => bail!( + "You cannot set a mininimum Python version {} higher than the interpreter version {}", + abi3_minor, + interpreter_minor + ), + _ => abi3_minor.or(interpreter_config.version.minor), + } + } else { + interpreter_config.version.minor + }; - if let Some(minor) = interpreter_config.version.minor { - for i in 6..=minor { + if let Some(minor) = minor { + for i in PY3_MIN_MINOR..=minor { println!("cargo:rustc-cfg=Py_3_{}", i); flags += format!("CFG_Py_3_{},", i).as_ref(); } @@ -834,7 +850,26 @@ fn check_target_architecture(interpreter_config: &InterpreterConfig) -> Result<( Ok(()) } +fn abi3_without_interpreter() -> Result<()> { + println!("cargo:rustc-cfg=Py_LIMITED_API"); + let mut flags = "FLAG_WITH_THREAD=1".to_string(); + for minor in PY3_MIN_MINOR..=ABI3_MAX_MINOR { + println!("cargo:rustc-cfg=Py_3_{}", minor); + flags += &format!(",CFG_Py_3_{}", minor); + } + println!("cargo:rustc-cfg=py_sys_config=\"WITH_THREAD\""); + println!("cargo:python_flags={}", flags); + Ok(()) +} + fn main() -> Result<()> { + // If PYO3_NO_PYTHON is set with abi3, we can build PyO3 without calling Python (UNIX only). + // We only check for the abi3-py3{ABI3_MAX_MINOR} because lower versions depend on it. + if env::var_os("PYO3_NO_PYTHON").is_some() + && env::var_os(format!("CARGO_FEATURE_ABI3_PY3{}", ABI3_MAX_MINOR)).is_some() + { + return abi3_without_interpreter(); + } // 1. Setup cfg variables so we can do conditional compilation in this library based on the // python interpeter's compilation flags. This is necessary for e.g. matching the right unicode // and threading interfaces. First check if we're cross compiling, if so, we cannot run the diff --git a/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index b0ed67c13cc..6b013b54bf1 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -58,6 +58,16 @@ pyo3 = { version = "...", features = ["abi3"]} 3. Ensure that the `.whl` is correctly marked as `abi3`. For projects using `setuptools`, this is accomplished by passing `--py-limited-api=cp3x` (where `x` is the minimum Python version supported by the wheel, e.g. `--py-limited-api=cp35` for Python 3.5) to `setup.py bdist_wheel`. +### 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`. +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. +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. + +As an advanced feature, you can build PyO3 wheel without calling Python interpreter with +the environment variable `PYO3_NO_PYTHON` set, but this only works on *NIX. + ## Cross Compiling Cross compiling PyO3 modules is relatively straightforward and requires a few pieces of software: @@ -107,4 +117,3 @@ For an example of how to build python extensions using Bazel, see https://github [maturin]: https://github.com/PyO3/maturin [setuptools-rust]: https://github.com/PyO3/setuptools-rust -