Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ env:

jobs:
build:
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.7", "3.8"]'), inputs.python-version) || contains(fromJSON('["beta", "nightly"]'), inputs.rust) }}
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.8"]'), inputs.python-version) || contains(fromJSON('["beta", "nightly"]'), inputs.rust) }}
runs-on: ${{ inputs.os }}
if: ${{ !(startsWith(inputs.python-version, 'graalpy') && startsWith(inputs.os, 'windows')) }}
steps:
Expand All @@ -44,7 +44,7 @@ jobs:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}

# installs using setup-python do not work for arm macOS 3.9 and below
- if: ${{ !(inputs.os == 'macos-latest' && contains(fromJSON('["3.7", "3.8", "3.9"]'), inputs.python-version) && inputs.python-architecture == 'x64') }}
- if: ${{ !(inputs.os == 'macos-latest' && contains(fromJSON('["3.8", "3.9"]'), inputs.python-version) && inputs.python-architecture == 'x64') }}
name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v6
with:
Expand Down Expand Up @@ -89,8 +89,8 @@ jobs:
# to match windows-2022 images
version: "18"

- name: Install zoneinfo backport for Python 3.7 / 3.8
if: contains(fromJSON('["3.7", "3.8"]'), inputs.python-version)
- name: Install zoneinfo backport for Python 3.8
if: contains(fromJSON('["3.8"]'), inputs.python-version)
run: python -m pip install backports.zoneinfo

- uses: Swatinem/rust-cache@v2
Expand Down
29 changes: 0 additions & 29 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ jobs:
rust: [stable]
python-version:
[
"3.7",
"3.8",
"3.9",
"3.10",
Expand Down Expand Up @@ -306,25 +305,8 @@ jobs:
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
# ubuntu-latest (24.04) no longer supports 3.7, so run on 22.04
- rust: stable
python-version: "3.7"
platform:
{
os: "ubuntu-22.04",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}

# arm64 macOS Python not available on GitHub Actions until 3.10, test older versions on x64
- rust: stable
python-version: "3.7"
platform:
{
os: "macos-15-intel",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
- rust: stable
python-version: "3.8"
platform:
Expand Down Expand Up @@ -359,18 +341,7 @@ jobs:
rust-target: "aarch64-pc-windows-msvc",
}
exclude:
# ubuntu-latest (24.04) no longer supports 3.7
- python-version: "3.7"
platform: { os: "ubuntu-latest" }
# arm64 macOS Python not available on GitHub Actions until 3.10
- rust: stable
python-version: "3.7"
platform:
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
}
- rust: stable
python-version: "3.8"
platform:
Expand Down
14 changes: 8 additions & 6 deletions Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,21 @@ To summarize, there are six main parts to the PyO3 codebase.

[`pyo3-ffi`] contains wrappers of the [Python/C API]. This is currently done by hand rather than
automated tooling because:
- it gives us best control about how to adapt C conventions to Rust, and
- there are many Python interpreter versions we support in a single set of files.

- it gives us best control about how to adapt C conventions to Rust, and
- there are many Python interpreter versions we support in a single set of files.

We aim to provide straight-forward Rust wrappers resembling the file structure of [`cpython/Include`](https://github.com/python/cpython/tree/main/Include).

We are continuously updating the module to match the latest CPython version which PyO3 supports (i.e. as of time of writing Python 3.13). The tracking issue is [#1289](https://github.com/PyO3/pyo3/issues/1289), and contribution is welcome.

In the [`pyo3-ffi`] crate, there is lots of conditional compilation such as `#[cfg(Py_LIMITED_API)]`,
`#[cfg(Py_3_7)]`, and `#[cfg(PyPy)]`.
`#[cfg(Py_3_8)]`, and `#[cfg(PyPy)]`.
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3).
`Py_3_7` means that the API is available from Python >= 3.7.
There are also `Py_3_8`, `Py_3_9`, and so on.
`Py_3_8` means that the API is available from Python >= 3.8.
There are also `Py_3_9`, `Py_3_10`, and so on.
`PyPy` means that the API definition is for PyPy.
Those flags are set in [`build.rs`](#6-buildrs-and-pyo3-build-config).

Expand Down Expand Up @@ -142,8 +143,9 @@ The `pyo3` `build.rs` also runs some safety checks such as ensuring the Python v
actually supported.

Some of the functionality of `pyo3-build-config`:

- Find the interpreter for build and detect the Python version.
- We have to set some version flags like `#[cfg(Py_3_7)]`.
- We have to set some version flags like `#[cfg(Py_3_8)]`.
- If the interpreter is PyPy, we set `#[cfg(PyPy)`.
- If the `PYO3_CONFIG_FILE` environment variable is set then that file's contents will be used
instead of any detected configuration.
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ extension-module = ["pyo3-ffi/extension-module"]
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3"]

# With abi3, we can manually set the minimum Python version.
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37", "pyo3-ffi/abi3-py37"]
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38", "pyo3-ffi/abi3-py38"]
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39", "pyo3-ffi/abi3-py39"]
abi3-py310 = ["abi3-py311", "pyo3-build-config/abi3-py310", "pyo3-ffi/abi3-py310"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
Requires Rust 1.83 or greater.

PyO3 supports the following Python distributions:
- CPython 3.7 or greater
- CPython 3.8 or greater
- PyPy 7.3 (Python 3.11+)
- GraalPy 25.0 or greater (Python 3.12+)

Expand Down
2 changes: 1 addition & 1 deletion examples/maturin-starter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ crate-type = ["cdylib"]
pyo3 = { path = "../../" }

[features]
abi3 = ["pyo3/abi3-py37"]
abi3 = ["pyo3/abi3-py38"]

[workspace]
2 changes: 1 addition & 1 deletion examples/plugin/plugin_api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "plugin_api"
requires-python = ">=3.7"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
Expand Down
32 changes: 16 additions & 16 deletions guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This chapter of the guide goes into detail on how to build and distribute projects using PyO3.
The way to achieve this is very different depending on whether the project is a Python module implemented in Rust, or a Rust binary embedding Python.
For both types of project there are also common problems such as the Python version to build for and the [linker](https://en.wikipedia.org/wiki/Linker_(computing)) arguments to use.
For both types of project there are also common problems such as the Python version to build for and the [linker](<https://en.wikipedia.org/wiki/Linker_(computing)>) arguments to use.

The material in this chapter is intended for users who have already read the PyO3 [README](./index.md).
It covers in turn the choices that can be made for Python modules and for Rust binaries.
Expand All @@ -19,7 +19,7 @@ By default it will attempt to use the following in order:
- 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.7`, `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.8`, `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.

Expand Down Expand Up @@ -109,7 +109,7 @@ You can then open a Python shell in the output directory and you'll be able to r

If you're packaging your library for redistribution, you should indicate the Python interpreter your library is compiled for by including the [platform tag](#platform-tags) in its name.
This prevents incompatible interpreters from trying to import your library.
If you're compiling for PyPy you *must* include the platform tag, or PyPy will ignore the module.
If you're compiling for PyPy you _must_ include the platform tag, or PyPy will ignore the module.

#### Bazel builds

Expand Down Expand Up @@ -245,18 +245,18 @@ There are three steps involved in making use of `abi3` when building Python pack

#### Minimum Python version for `abi3`

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`.
Because a single `abi3` wheel can be used with many different Python versions, PyO3 has feature flags `abi3-py38`, `abi3-py39`, `abi3-py310` etc. to set the minimum required Python version for your `abi3` wheel.
For example, if you set the `abi3-py38` feature, your extension wheel can be used on all Python 3 versions from Python 3.8 and up.
`maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp38-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.md#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime.

PyO3 is only able to link your extension module to abi3 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.
E.g., if you set `abi3-py39` and try to compile the crate with a host of Python 3.8, the build will fail.

> [!NOTE]
> If you set more that one of these `abi3` 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.
> If you set more that one of these `abi3` version feature flags the lowest version always wins. For example, with both `abi3-py38` and `abi3-py39` set, PyO3 would build a wheel which supports Python 3.8 and up.

#### Building `abi3` extensions without a Python interpreter

Expand Down Expand Up @@ -321,17 +321,17 @@ The known complications are:
- To import compiled extension modules (such as other Rust extension modules, or those written in C), your binary must have the correct linker flags set during compilation to export the original contents of `libpython.a` so that extensions can use them (e.g. `-Wl,--export-dynamic`).
- The C compiler and flags which were used to create `libpython.a` must be compatible with your Rust compiler and flags, else you will experience compilation failures.

Significantly different compiler versions may see errors like this:
Significantly different compiler versions may see errors like this:

```text
lto1: fatal error: bytecode stream in file 'rust-numpy/target/release/deps/libpyo3-6a7fb2ed970dbf26.rlib' generated with LTO version 6.0 instead of the expected 6.2
```
```text
lto1: fatal error: bytecode stream in file 'rust-numpy/target/release/deps/libpyo3-6a7fb2ed970dbf26.rlib' generated with LTO version 6.0 instead of the expected 6.2
```

Mismatching flags may lead to errors like this:
Mismatching flags may lead to errors like this:

```text
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libpython3.9.a(zlibmodule.o): relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIE
```
```text
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libpython3.9.a(zlibmodule.o): relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIE
```

If you encounter these or other complications when linking the interpreter statically, discuss them on [issue 416 on PyO3's GitHub](https://github.com/PyO3/pyo3/issues/416).
It is hoped that eventually that discussion will contain enough information and solutions that PyO3 can offer first-class support for static embedding.
Expand Down
20 changes: 10 additions & 10 deletions guide/src/building-and-distribution/multiple-python-versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ PyO3 itself depends on this crate, so by using it you can be sure that you are c
This allows us to write code like the following

```rust,ignore
#[cfg(Py_3_7)]
fn function_only_supported_on_python_3_7_and_up() {}
#[cfg(Py_3_8)]
fn function_only_supported_on_python_3_8_and_up() {}
#[cfg(not(Py_3_8))]
fn function_only_supported_before_python_3_8() {}
Expand Down Expand Up @@ -60,17 +60,17 @@ The `#[cfg]` flags added by `pyo3-build-cfg` can be combined with all of Rust's
The following are some common patterns implemented using these flags:

```text
#[cfg(Py_3_7)]
#[cfg(Py_3_8)]
```

This `#[cfg]` marks code that will only be present on Python 3.7 and upwards.
There are similar options `Py_3_8`, `Py_3_9`, `Py_3_10` and so on for each minor version.
This `#[cfg]` marks code that will only be present on Python 3.8 and upwards.
There are similar options `Py_3_9`, `Py_3_10`, `Py_3_11` and so on for each minor version.

```text
#[cfg(not(Py_3_7))]
#[cfg(not(Py_3_8))]
```

This `#[cfg]` marks code that will only be present on Python versions before (but not including) Python 3.7.
This `#[cfg]` marks code that will only be present on Python versions before (but not including) Python 3.8.

```text
#[cfg(not(Py_LIMITED_API))]
Expand Down Expand Up @@ -109,9 +109,9 @@ This allows you to do the following, for example:
use pyo3::Python;

Python::attach(|py| {
// PyO3 supports Python 3.7 and up.
assert!(py.version_info() >= (3, 7));
assert!(py.version_info() >= (3, 7, 0));
// PyO3 supports Python 3.8 and up.
assert!(py.version_info() >= (3, 8));
assert!(py.version_info() >= (3, 8, 0));
});
```

Expand Down
Loading
Loading