From 597119d40a92b3c9bb751107544bb9533b88fa49 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 31 Oct 2020 16:35:14 +0900 Subject: [PATCH 1/6] Add abi3-py* features --- Cargo.toml | 8 ++++++-- build.rs | 26 +++++++++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72a907135a7..270b9390302 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,9 +35,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"] +abi3-py37 = ["abi3-py36"] +abi3-py38 = ["abi3-py37"] +abi3-py39 = ["abi3-py38"] # Optimizes PyObject to Vec conversion and so on. nightly = [] diff --git a/build.rs b/build.rs index 1d4d78f61ab..8b87fa2b9a3 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>; @@ -770,12 +773,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(); } From 93282e9a702ecf0bac5a1ece4337b88bff9e4f71 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 16 Nov 2020 00:40:05 +0900 Subject: [PATCH 2/6] Note abi3-py* features in the guide and Add CHANGELOG --- CHANGELOG.md | 1 + guide/src/building_and_distribution.md | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b58c9c1c85..94c84460752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,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) + - Relatedly, `abi3-py*` feature flags are added. [#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 `PyEval_SetProfile` and `PyEval_SetTrace` to FFI. [#1255](https://github.com/PyO3/pyo3/pull/1255) - Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259) diff --git a/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index b0ed67c13cc..49c857db551 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -58,6 +58,14 @@ 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` + +We provide `abi3-py36`/`abi3-py37`/... features to set the minimum required Python version for abi3 wheel. +E.g., if you set `abi3-py36` feature, you can build `cp36-abi3-manylinux2020_x86_64.whl` using Python 3.8. + +To ensure ABI compatibility, we don't allow setting a minimum version higher than the system Python version. +E.g., if you set `abi3-py38` and try to compile the crate with Python 3.6, it just fails. + ## Cross Compiling Cross compiling PyO3 modules is relatively straightforward and requires a few pieces of software: From eae0d8682b7dbcafdc2765648f2f9bfa49669f8d Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 16 Nov 2020 02:23:44 +0900 Subject: [PATCH 3/6] Correct the definitions of abi3-py* features --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 270b9390302..896dd697ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,10 +38,10 @@ macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] # 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"] -abi3-py37 = ["abi3-py36"] -abi3-py38 = ["abi3-py37"] -abi3-py39 = ["abi3-py38"] +abi3-py36 = ["abi3-py37"] +abi3-py37 = ["abi3-py38"] +abi3-py38 = ["abi3-py39"] +abi3-py39 = ["abi3"] # Optimizes PyObject to Vec conversion and so on. nightly = [] From 1b838504b7a23a4313290d36cea56f83637a3c23 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 1 Dec 2020 23:40:49 +0900 Subject: [PATCH 4/6] Introduce PYO3_NO_INTERPRETER variable for build script --- build.rs | 18 ++++++++++++++++++ guide/src/building_and_distribution.md | 3 +++ 2 files changed, 21 insertions(+) diff --git a/build.rs b/build.rs index 8b87fa2b9a3..60f8cc046c5 100644 --- a/build.rs +++ b/build.rs @@ -836,7 +836,25 @@ 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_INTEPRETER is set with abi3, we can build PyO3 without calling Python (UNIX only). + if env::var_os("PYO3_NO_INTERPRETER").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 49c857db551..02f1ba5eef8 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -66,6 +66,9 @@ E.g., if you set `abi3-py36` feature, you can build `cp36-abi3-manylinux2020_x86 To ensure ABI compatibility, we don't allow setting a minimum version higher than the system Python version. E.g., if you set `abi3-py38` and try to compile the crate with Python 3.6, it just fails. +As an advanced feature, you can build PyO3 wheel without calling Python interpreter with +the environment variable `PYO3_NO_INTERPRETER` set, but this only works on *NIX. + ## Cross Compiling Cross compiling PyO3 modules is relatively straightforward and requires a few pieces of software: From 6da6bc9461660ea3eff356fd86e10d9662033595 Mon Sep 17 00:00:00 2001 From: Yuji Kanagawa Date: Sun, 6 Dec 2020 16:06:10 +0900 Subject: [PATCH 5/6] Apply suggestions from @davidhewitt Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com> --- CHANGELOG.md | 2 +- guide/src/building_and_distribution.md | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94c84460752..af49d279e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,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) - - Relatedly, `abi3-py*` feature flags are added. [#1263]((https://github.com/PyO3/pyo3/pull/1263)) + - 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 `PyEval_SetProfile` and `PyEval_SetTrace` to FFI. [#1255](https://github.com/PyO3/pyo3/pull/1255) - Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259) diff --git a/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index 02f1ba5eef8..2940b1c3e55 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -60,11 +60,10 @@ pyo3 = { version = "...", features = ["abi3"]} ### Minimum Python version for `abi3` -We provide `abi3-py36`/`abi3-py37`/... features to set the minimum required Python version for abi3 wheel. -E.g., if you set `abi3-py36` feature, you can build `cp36-abi3-manylinux2020_x86_64.whl` using Python 3.8. - -To ensure ABI compatibility, we don't allow setting a minimum version higher than the system Python version. -E.g., if you set `abi3-py38` and try to compile the crate with Python 3.6, it just fails. +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_INTERPRETER` set, but this only works on *NIX. @@ -118,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 - From 49143724d54d78c880f6ef1cd0df9795c2b9927e Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 6 Dec 2020 16:08:18 +0900 Subject: [PATCH 6/6] Rename PYO3_NO_INTERPRETER by PYO3_NO_PYTHON --- build.rs | 5 +++-- guide/src/building_and_distribution.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 60f8cc046c5..b896ebd0a64 100644 --- a/build.rs +++ b/build.rs @@ -849,8 +849,9 @@ fn abi3_without_interpreter() -> Result<()> { } fn main() -> Result<()> { - // If PYO3_NO_INTEPRETER is set with abi3, we can build PyO3 without calling Python (UNIX only). - if env::var_os("PYO3_NO_INTERPRETER").is_some() + // 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(); diff --git a/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index 2940b1c3e55..6b013b54bf1 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -66,7 +66,7 @@ If you set more that one of these api version feature flags the highest version 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_INTERPRETER` set, but this only works on *NIX. +the environment variable `PYO3_NO_PYTHON` set, but this only works on *NIX. ## Cross Compiling