From 7f2a8f25b2451132b2bc772d0c3b57d363800730 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 1 Nov 2025 19:55:23 +0000 Subject: [PATCH 1/9] replace `extension-module` feature in docs --- Architecture.md | 5 +-- Cargo.toml | 6 +-- README.md | 2 +- examples/Cargo.toml | 2 +- examples/decorator/.template/Cargo.toml | 2 +- examples/decorator/Cargo.toml | 2 +- examples/getitem/.template/Cargo.toml | 2 +- examples/getitem/Cargo.toml | 2 +- examples/maturin-starter/.template/Cargo.toml | 2 +- examples/maturin-starter/Cargo.toml | 2 +- .../plugin/.template/plugin_api/Cargo.toml | 5 --- examples/plugin/README.md | 8 ++-- examples/plugin/plugin_api/Cargo.toml | 5 --- .../.template/Cargo.toml | 2 +- examples/setuptools-rust-starter/Cargo.toml | 2 +- examples/word-count/.template/Cargo.toml | 2 +- examples/word-count/Cargo.toml | 2 +- guide/src/building-and-distribution.md | 24 +++++++----- guide/src/faq.md | 38 ++----------------- guide/src/features.md | 4 +- guide/src/getting-started.md | 4 +- pyo3-build-config/Cargo.toml | 2 +- pyo3-build-config/src/impl_.rs | 2 +- pyo3-build-config/src/lib.rs | 2 +- pyo3-ffi-check/Cargo.toml | 1 - pyo3-ffi/Cargo.toml | 4 +- pyo3-ffi/README.md | 1 - .../examples/sequential/.template/Cargo.toml | 2 +- pyo3-ffi/examples/sequential/Cargo.toml | 2 +- .../examples/string-sum/.template/Cargo.toml | 2 +- pyo3-ffi/examples/string-sum/Cargo.toml | 2 +- pyo3-ffi/src/lib.rs | 4 -- src/lib.rs | 4 -- 33 files changed, 51 insertions(+), 100 deletions(-) diff --git a/Architecture.md b/Architecture.md index 2b081a487e4..dfd4d4b959d 100644 --- a/Architecture.md +++ b/Architecture.md @@ -151,9 +151,8 @@ Some of the functionality of `pyo3-build-config`: entirely and only abi3 extensions can be built. - Check if we are building a Python extension. - If we are building an extension (e.g., Python library installable by `pip`), - we don't link `libpython`. - Currently we use the `extension-module` feature for this purpose. This may change in the future. - See [#1123](https://github.com/PyO3/pyo3/pull/1123). + we don't link `libpython` on most platforms (to allow for statically-linked Python interpreters). + The `PYO3_BUILD_EXTENSION_MODULE` environment variable suppresses linking. - Cross-compiling configuration - If `TARGET` architecture and `HOST` architecture differ, we can find cross compile information from environment variables (`PYO3_CROSS_LIB_DIR`, `PYO3_CROSS_PYTHON_VERSION` and diff --git a/Cargo.toml b/Cargo.toml index 3c2e5372fbb..8b4030cd529 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,9 +103,9 @@ macros = ["pyo3-macros", "indoc", "unindent"] # Enables multiple #[pymethods] per #[pyclass] multiple-pymethods = ["inventory", "pyo3-macros/multiple-pymethods"] -# Use this feature when building an extension module. -# It tells the linker to keep the python symbols unresolved, -# so that the module can also be used with statically linked python interpreters. +# Deprecated: use the `PYO3_BUILD_EXTENSION_MODULE` environment variable when +# building a Python extension module (set automatically by `setuptools-rust` and +# `maturin`). extension-module = ["pyo3-ffi/extension-module"] # Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more. diff --git a/README.md b/README.md index afac97a4a53..880e790958d 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.27.0", features = ["extension-module"] } +pyo3 = "0.27.0" ``` **`src/lib.rs`** diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 68aa8be7a76..5ce9d547625 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" rust-version = "1.83" [dev-dependencies] -pyo3 = { path = "..", features = ["auto-initialize", "extension-module"] } +pyo3 = { path = "..", features = ["auto-initialize"] } [[example]] name = "decorator" diff --git a/examples/decorator/.template/Cargo.toml b/examples/decorator/.template/Cargo.toml index 95c3f0f14ae..f25820012db 100644 --- a/examples/decorator/.template/Cargo.toml +++ b/examples/decorator/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "decorator" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3 = { version = "{{PYO3_VERSION}}" } diff --git a/examples/decorator/Cargo.toml b/examples/decorator/Cargo.toml index ef7e5dc2e1c..01d3cf0e0d7 100644 --- a/examples/decorator/Cargo.toml +++ b/examples/decorator/Cargo.toml @@ -9,6 +9,6 @@ name = "decorator" crate-type = ["cdylib"] [dependencies] -pyo3 = { path = "../../", features = ["extension-module"] } +pyo3 = { path = "../../" } [workspace] diff --git a/examples/getitem/.template/Cargo.toml b/examples/getitem/.template/Cargo.toml index 47dbb7b0ed5..682db12141e 100644 --- a/examples/getitem/.template/Cargo.toml +++ b/examples/getitem/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "getitem" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3 = { version = "{{PYO3_VERSION}}" } diff --git a/examples/getitem/Cargo.toml b/examples/getitem/Cargo.toml index c31047eba24..61f456bd08f 100644 --- a/examples/getitem/Cargo.toml +++ b/examples/getitem/Cargo.toml @@ -9,6 +9,6 @@ name = "getitem" crate-type = ["cdylib"] [dependencies] -pyo3 = { path = "../../", features = ["extension-module"] } +pyo3 = { path = "../../" } [workspace] diff --git a/examples/maturin-starter/.template/Cargo.toml b/examples/maturin-starter/.template/Cargo.toml index e67e1240519..452cc53540b 100644 --- a/examples/maturin-starter/.template/Cargo.toml +++ b/examples/maturin-starter/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "maturin_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3 = { version = "{{PYO3_VERSION}}" } diff --git a/examples/maturin-starter/Cargo.toml b/examples/maturin-starter/Cargo.toml index 53e464bbffe..c5aedf197e8 100644 --- a/examples/maturin-starter/Cargo.toml +++ b/examples/maturin-starter/Cargo.toml @@ -9,7 +9,7 @@ name = "maturin_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { path = "../../", features = ["extension-module"] } +pyo3 = { path = "../../" } [features] abi3 = ["pyo3/abi3-py37", "generate-import-lib"] diff --git a/examples/plugin/.template/plugin_api/Cargo.toml b/examples/plugin/.template/plugin_api/Cargo.toml index dfbd855aebc..9a7c5fb9ac1 100644 --- a/examples/plugin/.template/plugin_api/Cargo.toml +++ b/examples/plugin/.template/plugin_api/Cargo.toml @@ -9,9 +9,4 @@ name = "plugin_api" crate-type = ["cdylib", "rlib"] [dependencies] -#!!! Important - DO NOT ENABLE extension-module FEATURE HERE!!! pyo3 = "{{PYO3_VERSION}}" - -[features] -# instead extension-module feature for pyo3 is enabled conditionally when we want to build a standalone extension module to test our plugins without "main" program -extension-module = ["pyo3/extension-module"] diff --git a/examples/plugin/README.md b/examples/plugin/README.md index 17f2cf740bd..b88c2f0277d 100644 --- a/examples/plugin/README.md +++ b/examples/plugin/README.md @@ -5,7 +5,7 @@ interface types that can be used to exchange data between Rust and Python. This # Building and Testing ## Host application -To run the app itself, you only need to run +To run the app itself, you only need to run ```shell cargo run @@ -14,7 +14,7 @@ It will build the app, as well as the plugin API, then run the app, load the plu ## Plugin API testing -The plugin API is in a separate crate `plugin_api`, so you can test it separately from the main app. +The plugin API is in a separate crate `plugin_api`, so you can test it separately from the main app. To build the API only package, first install `maturin`: @@ -22,12 +22,12 @@ To build the API only package, first install `maturin`: pip install maturin ``` -When building the plugin, simply using `maturin develop` will fail to produce a viable extension module due to the features arrangement of PyO3. +When building the plugin, simply using `maturin develop` will fail to produce a viable extension module due to the features arrangement of PyO3. Instead, one needs to enable the optional feature as follows: ```shell cd plugin_api -maturin build --features "extension-module" +maturin build" ``` Alternatively, install nox and run the tests inside an isolated environment: diff --git a/examples/plugin/plugin_api/Cargo.toml b/examples/plugin/plugin_api/Cargo.toml index 870ad76a377..dd8a1f51d37 100644 --- a/examples/plugin/plugin_api/Cargo.toml +++ b/examples/plugin/plugin_api/Cargo.toml @@ -9,9 +9,4 @@ name = "plugin_api" crate-type = ["cdylib", "rlib"] [dependencies] -#!!! Important - DO NOT ENABLE extension-module FEATURE HERE!!! pyo3 = { path = "../../../" } - -[features] -# instead extension-module feature for pyo3 is enabled conditionally when we want to build a standalone extension module to test our plugins without "main" program -extension-module = ["pyo3/extension-module"] diff --git a/examples/setuptools-rust-starter/.template/Cargo.toml b/examples/setuptools-rust-starter/.template/Cargo.toml index d0e43066ac5..dadee3e1014 100644 --- a/examples/setuptools-rust-starter/.template/Cargo.toml +++ b/examples/setuptools-rust-starter/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "setuptools_rust_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3 = { version = "{{PYO3_VERSION}}" } diff --git a/examples/setuptools-rust-starter/Cargo.toml b/examples/setuptools-rust-starter/Cargo.toml index e7ae56a7612..02b399c3779 100644 --- a/examples/setuptools-rust-starter/Cargo.toml +++ b/examples/setuptools-rust-starter/Cargo.toml @@ -9,6 +9,6 @@ name = "setuptools_rust_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { path = "../../", features = ["extension-module"] } +pyo3 = { path = "../../" } [workspace] diff --git a/examples/word-count/.template/Cargo.toml b/examples/word-count/.template/Cargo.toml index 5d6bffafcbb..1d9749c9291 100644 --- a/examples/word-count/.template/Cargo.toml +++ b/examples/word-count/.template/Cargo.toml @@ -9,5 +9,5 @@ name = "word_count" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3 = { version = "{{PYO3_VERSION}}" } rayon = "1.0.2" diff --git a/examples/word-count/Cargo.toml b/examples/word-count/Cargo.toml index 816e3dd9a34..c1bb4552114 100644 --- a/examples/word-count/Cargo.toml +++ b/examples/word-count/Cargo.toml @@ -9,7 +9,7 @@ name = "word_count" crate-type = ["cdylib"] [dependencies] -pyo3 = { path = "../..", features = ["extension-module"] } +pyo3 = { path = "../.." } rayon = "1.0.2" [workspace] diff --git a/guide/src/building-and-distribution.md b/guide/src/building-and-distribution.md index 9364c95b749..320a1fd3f1d 100644 --- a/guide/src/building-and-distribution.md +++ b/guide/src/building-and-distribution.md @@ -73,11 +73,11 @@ The PyO3 ecosystem has two packaging tools, [`maturin`] and [`setuptools-rust`], PyO3 has some Cargo features to configure projects for building Python extension modules: -- The `extension-module` feature, which must be enabled when building Python extension modules. +- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. - The `abi3` feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel. This section describes each of these packaging tools before describing how to build manually without them. -It then proceeds with an explanation of the `extension-module` feature. +It then proceeds with an explanation of the `PYO3_BUILD_EXTENSION_MODULE` environment variable. Finally, there is a section describing PyO3's `abi3` features. ### Packaging tools @@ -97,7 +97,7 @@ There are also [`maturin-starter`] and [`setuptools-rust-starter`] examples in t ### Manual builds -To build a PyO3-based Python extension manually, start by running `cargo build` as normal in a library project which uses PyO3's `extension-module` feature and has the [`cdylib` crate type](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field). +To build a PyO3-based Python extension manually, start by running `cargo build` as normal in a library project with the [`cdylib` crate type](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field) while the `PYO3_BUILD_EXTENSION_MODULE` environment variable is set. Once built, symlink (or copy) and rename the shared library from Cargo's `target/` directory to your desired output directory: @@ -142,7 +142,7 @@ See [PEP 3149](https://peps.python.org/pep-3149/) for more background on platfor #### macOS -On macOS, because the `extension-module` feature disables linking to `libpython` ([see the next section](#the-extension-module-feature)), some additional linker arguments need to be set. `maturin` and `setuptools-rust` both pass these arguments for PyO3 automatically, but projects using manual builds will need to set these directly in order to support macOS. +On macOS, because the `PYO3_BUILD_EXTENSION_MODULE` environment variable disables linking to `libpython` ([see the next section](#the-extension-module-feature)), some additional linker arguments need to be set. `maturin` and `setuptools-rust` both pass these arguments for PyO3 automatically, but projects using manual builds will need to set these directly in order to support macOS. The easiest way to set the correct linker arguments is to add a [`build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html) with the following content: @@ -193,17 +193,23 @@ For more discussion on and workarounds for MacOS linking problems [see this issu Finally, don't forget that on MacOS the `extension-module` feature will cause `cargo test` to fail without the `--no-default-features` flag (see [the FAQ](https://pyo3.rs/main/faq.html#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror)). -### The `extension-module` feature +### The `PYO3_BUILD_EXTENSION_MODULE` environment variable -PyO3's `extension-module` feature is used to disable [linking](https://en.wikipedia.org/wiki/Linker_(computing)) to `libpython` on Unix targets. + -This is necessary because by default PyO3 links to `libpython`. +By default PyO3 links to `libpython`. This makes binaries, tests, and examples "just work". However, Python extensions on Unix must not link to libpython for [manylinux](https://www.python.org/dev/peps/pep-0513/) compliance. The downside of not linking to `libpython` is that binaries, tests, and examples (which usually embed Python) will fail to build. -If you have an extension module as well as other outputs in a single project, you need to use optional Cargo features to disable the `extension-module` when you're not building the extension module. -See [the FAQ](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) for an example workaround. +As a result, PyO3 uses an envionment variable `PYO3_BUILD_EXTENSION_MODULE` to disable linking to `libpython`. +This should only be set when building a library for distribution. +`maturin >= 1.9.4` and `setuptools-rust >= 1.12` will set this for you automatically. + +> Note: historically PyO3 used an `extension-module` feature to perform the same function now done by the `PYO3_BUILD_EXTENSION_MODULE` env var. +> This feature caused linking to be disabled for all compile targets, including Rust tests and benchmarks. +> +> Projects are encouraged to migrate off the feature, as it caused [major development pain](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) due to the lack of linking. ### `Py_LIMITED_API`/`abi3` diff --git a/guide/src/faq.md b/guide/src/faq.md index e0031bebafc..cebe4d0fa4d 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -26,47 +26,17 @@ See the documentation for [`PyOnceLock`] and [`OnceExt`] for further details and ## I can't run `cargo test`; or I can't build in a Cargo workspace: I'm having linker issues like "Symbol not found" or "Undefined reference to _PyExc_SystemError" -Currently, [#340](https://github.com/PyO3/pyo3/issues/340) causes `cargo test` to fail with linking errors when the `extension-module` feature is activated. -Linking errors can also happen when building in a cargo workspace where a different crate also uses PyO3 (see [#2521](https://github.com/PyO3/pyo3/issues/2521)). -For now, there are three ways we can work around these issues. +The `extension-module` feature (now deprecated) disables linking to `libpython`. This breaks binaries and tests which need to load symbols from `libpython` to execute. -1. Make the `extension-module` feature optional. - Build with `maturin develop --features "extension-module"` +Remove the `extension-module` feature and upgrade to `maturin >= 1.9.4` or `setuptools-rust 1.12`. - ```toml - [dependencies.pyo3] - {{#PYO3_CRATE_VERSION}} - - [features] - extension-module = ["pyo3/extension-module"] - ``` - -2. Make the `extension-module` feature optional and default. - Run tests with `cargo test --no-default-features`: - - ```toml - [dependencies.pyo3] - {{#PYO3_CRATE_VERSION}} - - [features] - extension-module = ["pyo3/extension-module"] - default = ["extension-module"] - ``` - -3. If you are using a [`pyproject.toml`](https://maturin.rs/metadata.html) file to control maturin settings, add the following section: - - ```toml - [tool.maturin] - features = ["pyo3/extension-module"] - # Or for maturin 0.12: - # cargo-extra-args = ["--features", "pyo3/extension-module"] - ``` +If building manually, see the [`PYO3_BUILD_EXTENSION_MODULE` environment variable](./building-and-distribution.md#the-pyo3_build_extension_module-environment-variable). ## I can't run `cargo test`: my crate cannot be found for tests in `tests/` directory The Rust book suggests to [put integration tests inside a `tests/` directory](https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests). -For a PyO3 `extension-module` project where the `crate-type` is set to `"cdylib"` in your `Cargo.toml`, the compiler won't be able to find your crate and will display errors such as `E0432` or `E0463`: +For a PyO3 project where the `crate-type` is set to `"cdylib"` in your `Cargo.toml`, the compiler won't be able to find your crate and will display errors such as `E0432` or `E0463`: ```text error[E0432]: unresolved import `my_crate` diff --git a/guide/src/features.md b/guide/src/features.md index bc6d674fcac..84b0eaf4b49 100644 --- a/guide/src/features.md +++ b/guide/src/features.md @@ -9,9 +9,7 @@ By default, only the `macros` feature is enabled. ### `extension-module` -This feature is required when building a Python extension module using PyO3. - -It tells PyO3's build script to skip linking against `libpython.so` on Unix platforms, where this must not be done. +Deprecated, users should remove this feature and upgrade to `maturin >= 1.9.4` or `setuptools-rust >= 1.12`. See the [building and distribution](building-and-distribution.md#the-extension-module-feature) section for further detail. diff --git a/guide/src/getting-started.md b/guide/src/getting-started.md index 42691ffeb7d..10663b55a82 100644 --- a/guide/src/getting-started.md +++ b/guide/src/getting-started.md @@ -138,7 +138,7 @@ name = "pyo3_example" crate-type = ["cdylib"] [dependencies] -pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["extension-module"] } +pyo3 = {{#PYO3_CRATE_VERSION}} ``` ## pyproject.toml @@ -147,7 +147,7 @@ You should also create a `pyproject.toml` with the following contents: ```toml [build-system] -requires = ["maturin>=1,<2"] +requires = ["maturin>=1.9.4,<2"] build-backend = "maturin" [project] diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 2f711915b8d..db9a549c131 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -26,7 +26,7 @@ default = [] # script. If this feature isn't enabled, the build script no-ops. resolve-config = [] -# This feature is enabled by pyo3 when building an extension module. +# deprecated extension-module = [] # Automatically generates `python3.dll` import libraries for Windows targets. diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index b977ac79479..237c36c639c 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -872,7 +872,7 @@ pub fn get_abi3_version() -> Option { /// Checks if the `extension-module` feature is enabled for the PyO3 crate. /// /// This can be triggered either by: -/// - The `extension-module` Cargo feature +/// - The `extension-module` Cargo feature (deprecated) /// - Setting the `PYO3_BUILD_EXTENSION_MODULE` environment variable /// /// Must be called from a PyO3 crate build script. diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 0544376e3b4..21db26a6b3e 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -49,7 +49,7 @@ pub fn use_pyo3_cfgs() { } } -/// Adds linker arguments suitable for PyO3's `extension-module` feature. +/// Adds linker arguments suitable for linking an extension module. /// /// This should be called from a build script. /// diff --git a/pyo3-ffi-check/Cargo.toml b/pyo3-ffi-check/Cargo.toml index 874da3d2bef..79fa08c5699 100644 --- a/pyo3-ffi-check/Cargo.toml +++ b/pyo3-ffi-check/Cargo.toml @@ -10,7 +10,6 @@ memoffset = "0.9.0" [dependencies.pyo3-ffi] path = "../pyo3-ffi" -features = ["extension-module"] # A lazy way of skipping linking in most cases (as we don't use any runtime symbols) [build-dependencies] bindgen = "0.69.4" diff --git a/pyo3-ffi/Cargo.toml b/pyo3-ffi/Cargo.toml index 03de6584643..6e9af2f0aa8 100644 --- a/pyo3-ffi/Cargo.toml +++ b/pyo3-ffi/Cargo.toml @@ -19,9 +19,7 @@ libc = "0.2.62" default = [] -# Use this feature when building an extension module. -# It tells the linker to keep the python symbols unresolved, -# so that the module can also be used with statically linked python interpreters. +# deprecated extension-module = ["pyo3-build-config/extension-module"] # Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more. diff --git a/pyo3-ffi/README.md b/pyo3-ffi/README.md index 41b9546ac50..ad3b11bc0f0 100644 --- a/pyo3-ffi/README.md +++ b/pyo3-ffi/README.md @@ -42,7 +42,6 @@ crate-type = ["cdylib"] [dependencies.pyo3-ffi] version = "0.27.0" -features = ["extension-module"] [build-dependencies] # This is only necessary if you need to configure your build based on diff --git a/pyo3-ffi/examples/sequential/.template/Cargo.toml b/pyo3-ffi/examples/sequential/.template/Cargo.toml index cd0ece4291f..d5165c429d9 100644 --- a/pyo3-ffi/examples/sequential/.template/Cargo.toml +++ b/pyo3-ffi/examples/sequential/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "sequential" crate-type = ["cdylib", "lib"] [dependencies] -pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3-ffi = { version = "{{PYO3_VERSION}}" } diff --git a/pyo3-ffi/examples/sequential/Cargo.toml b/pyo3-ffi/examples/sequential/Cargo.toml index 21e87c09496..1ee76c48114 100644 --- a/pyo3-ffi/examples/sequential/Cargo.toml +++ b/pyo3-ffi/examples/sequential/Cargo.toml @@ -9,7 +9,7 @@ name = "sequential" crate-type = ["cdylib", "lib"] [dependencies] -pyo3-ffi = { path = "../../", features = ["extension-module"] } +pyo3-ffi = { path = "../../" } [build-dependencies] pyo3-build-config = { path = "../../../pyo3-build-config" } diff --git a/pyo3-ffi/examples/string-sum/.template/Cargo.toml b/pyo3-ffi/examples/string-sum/.template/Cargo.toml index 40afa2504c3..48c3fdc53c5 100644 --- a/pyo3-ffi/examples/string-sum/.template/Cargo.toml +++ b/pyo3-ffi/examples/string-sum/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies] -pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } +pyo3-ffi = { version = "{{PYO3_VERSION}}" } diff --git a/pyo3-ffi/examples/string-sum/Cargo.toml b/pyo3-ffi/examples/string-sum/Cargo.toml index 173521eaab5..e4baa38abec 100644 --- a/pyo3-ffi/examples/string-sum/Cargo.toml +++ b/pyo3-ffi/examples/string-sum/Cargo.toml @@ -9,7 +9,7 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies] -pyo3-ffi = { path = "../../", features = ["extension-module"] } +pyo3-ffi = { path = "../../" } [build-dependencies] pyo3-build-config = { path = "../../../pyo3-build-config" } diff --git a/pyo3-ffi/src/lib.rs b/pyo3-ffi/src/lib.rs index cb26f52c5d1..1af413bd317 100644 --- a/pyo3-ffi/src/lib.rs +++ b/pyo3-ffi/src/lib.rs @@ -34,9 +34,6 @@ //! //! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by //! [PEP 384] to be forward-compatible with future Python versions. -//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that -//! your module can also be used with statically linked Python interpreters. Use this feature when -//! building an extension module. //! //! ## `rustc` environment flags //! @@ -107,7 +104,6 @@ //! //! [dependencies.pyo3-ffi] #![doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")] -//! features = ["extension-module"] //! //! [build-dependencies] //! # This is only necessary if you need to configure your build based on diff --git a/src/lib.rs b/src/lib.rs index e62365d4296..45c5b2a6f2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,9 +93,6 @@ //! [PEP 384] to be forward-compatible with future Python versions. //! - `auto-initialize`: Changes [`Python::attach`] to automatically initialize the Python //! interpreter if needed. -//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that -//! your module can also be used with statically linked Python interpreters. Use this feature when -//! building an extension module. //! - `multiple-pymethods`: Enables the use of multiple [`#[pymethods]`](macro@crate::pymethods) //! blocks per [`#[pyclass]`](macro@crate::pyclass). This adds a dependency on the [inventory] //! crate, which is not supported on all platforms. @@ -183,7 +180,6 @@ //! //! [dependencies.pyo3] #![doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")] -//! features = ["extension-module"] //! ``` //! //! **`src/lib.rs`** From 033af5fddd016c5082e4d6e2c42c8c4ec3839f37 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 1 Nov 2025 20:38:03 +0000 Subject: [PATCH 2/9] fix guide fmt --- guide/src/faq.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/guide/src/faq.md b/guide/src/faq.md index cebe4d0fa4d..6b1b3eae082 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -26,7 +26,8 @@ See the documentation for [`PyOnceLock`] and [`OnceExt`] for further details and ## I can't run `cargo test`; or I can't build in a Cargo workspace: I'm having linker issues like "Symbol not found" or "Undefined reference to _PyExc_SystemError" -The `extension-module` feature (now deprecated) disables linking to `libpython`. This breaks binaries and tests which need to load symbols from `libpython` to execute. +The `extension-module` feature (now deprecated) disables linking to `libpython`. +This breaks binaries and tests which need to load symbols from `libpython` to execute. Remove the `extension-module` feature and upgrade to `maturin >= 1.9.4` or `setuptools-rust 1.12`. From 7e8882e109af01d601024a63bb586a21b9008ae8 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 3 Nov 2025 08:48:01 +0000 Subject: [PATCH 3/9] fix copy in bullets --- guide/src/building-and-distribution.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guide/src/building-and-distribution.md b/guide/src/building-and-distribution.md index 320a1fd3f1d..9733873b342 100644 --- a/guide/src/building-and-distribution.md +++ b/guide/src/building-and-distribution.md @@ -71,12 +71,12 @@ Package vendors can opt-in to the "abi3" limited Python API which allows their w There are many ways to go about this: it is possible to use `cargo` to build the extension module (along with some manual work, which varies with OS). The PyO3 ecosystem has two packaging tools, [`maturin`] and [`setuptools-rust`], which abstract over the OS difference and also support building wheels for PyPI upload. -PyO3 has some Cargo features to configure projects for building Python extension modules: +PyO3 has some functionality for configuring projects when building Python extension modules: - The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. -- The `abi3` feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel. +- The `abi3` Cargo feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel. -This section describes each of these packaging tools before describing how to build manually without them. +This section describes the packaging tools before describing how to build manually without them. It then proceeds with an explanation of the `PYO3_BUILD_EXTENSION_MODULE` environment variable. Finally, there is a section describing PyO3's `abi3` features. From 9add771f1e2f0baf0e8c63a28d790d58f346e8c0 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 3 Nov 2025 08:54:01 +0000 Subject: [PATCH 4/9] make toml syntax for referring to pyo3 more consistent --- README.md | 1 + examples/decorator/.template/Cargo.toml | 2 +- examples/getitem/.template/Cargo.toml | 2 +- examples/maturin-starter/.template/Cargo.toml | 2 +- examples/setuptools-rust-starter/.template/Cargo.toml | 2 +- examples/word-count/.template/Cargo.toml | 2 +- pyo3-ffi-check/Cargo.toml | 4 ++-- pyo3-ffi/README.md | 4 ++-- pyo3-ffi/src/lib.rs | 4 ++-- src/lib.rs | 4 ++-- 10 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 45f27b13541..f6a92771b71 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th ```toml [dependencies.pyo3] version = "0.27.1" +# Enabling this cargo feature will cause PyO3 to start a Python interpreter on first call to `Python::attach` features = ["auto-initialize"] ``` diff --git a/examples/decorator/.template/Cargo.toml b/examples/decorator/.template/Cargo.toml index f25820012db..e0aa857a0d9 100644 --- a/examples/decorator/.template/Cargo.toml +++ b/examples/decorator/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "decorator" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}" } +pyo3 = "{{PYO3_VERSION}}" diff --git a/examples/getitem/.template/Cargo.toml b/examples/getitem/.template/Cargo.toml index 682db12141e..e40f51caacd 100644 --- a/examples/getitem/.template/Cargo.toml +++ b/examples/getitem/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "getitem" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}" } +pyo3 = "{{PYO3_VERSION}}" diff --git a/examples/maturin-starter/.template/Cargo.toml b/examples/maturin-starter/.template/Cargo.toml index 452cc53540b..ad324cf7cb0 100644 --- a/examples/maturin-starter/.template/Cargo.toml +++ b/examples/maturin-starter/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "maturin_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}" } +pyo3 = "{{PYO3_VERSION}}" diff --git a/examples/setuptools-rust-starter/.template/Cargo.toml b/examples/setuptools-rust-starter/.template/Cargo.toml index dadee3e1014..7448e278347 100644 --- a/examples/setuptools-rust-starter/.template/Cargo.toml +++ b/examples/setuptools-rust-starter/.template/Cargo.toml @@ -9,4 +9,4 @@ name = "setuptools_rust_starter" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}" } +pyo3 = "{{PYO3_VERSION}}" diff --git a/examples/word-count/.template/Cargo.toml b/examples/word-count/.template/Cargo.toml index 1d9749c9291..011f5e776d6 100644 --- a/examples/word-count/.template/Cargo.toml +++ b/examples/word-count/.template/Cargo.toml @@ -9,5 +9,5 @@ name = "word_count" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "{{PYO3_VERSION}}" } +pyo3 = "{{PYO3_VERSION}}" rayon = "1.0.2" diff --git a/pyo3-ffi-check/Cargo.toml b/pyo3-ffi-check/Cargo.toml index 79fa08c5699..b022689f484 100644 --- a/pyo3-ffi-check/Cargo.toml +++ b/pyo3-ffi-check/Cargo.toml @@ -8,8 +8,8 @@ publish = false pyo3-ffi-check-macro = { path = "./macro" } memoffset = "0.9.0" -[dependencies.pyo3-ffi] -path = "../pyo3-ffi" +[dependencies] +pyo3-ffi = { path = "../pyo3-ffi" } [build-dependencies] bindgen = "0.69.4" diff --git a/pyo3-ffi/README.md b/pyo3-ffi/README.md index fa3921c9332..8fed234eff0 100644 --- a/pyo3-ffi/README.md +++ b/pyo3-ffi/README.md @@ -40,8 +40,8 @@ name = "string_sum" # crate-type = ["cdylib", "rlib"] crate-type = ["cdylib"] -[dependencies.pyo3-ffi] -version = "0.27.1" +[dependencies] +pyo3 = "0.27.1" [build-dependencies] # This is only necessary if you need to configure your build based on diff --git a/pyo3-ffi/src/lib.rs b/pyo3-ffi/src/lib.rs index 1af413bd317..c88e7151af1 100644 --- a/pyo3-ffi/src/lib.rs +++ b/pyo3-ffi/src/lib.rs @@ -102,8 +102,8 @@ //! # crate-type = ["cdylib", "rlib"] //! crate-type = ["cdylib"] //! -//! [dependencies.pyo3-ffi] -#![doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")] +//! [dependencies] +#![doc = concat!("pyo3-ffi = \"", env!("CARGO_PKG_VERSION"), "\"")] //! //! [build-dependencies] //! # This is only necessary if you need to configure your build based on diff --git a/src/lib.rs b/src/lib.rs index 45c5b2a6f2f..f952ea7e6ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -178,8 +178,8 @@ //! # crate-type = ["cdylib", "rlib"] //! crate-type = ["cdylib"] //! -//! [dependencies.pyo3] -#![doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")] +//! [dependencies] +#![doc = concat!("pyo3 = \"", env!("CARGO_PKG_VERSION"), "\"")] //! ``` //! //! **`src/lib.rs`** From 2e65a8fe42538182067f82288e07f9dadf0fbb05 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 3 Nov 2025 08:54:56 +0000 Subject: [PATCH 5/9] note packaging tools set env var automatically --- guide/src/building-and-distribution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/building-and-distribution.md b/guide/src/building-and-distribution.md index 9733873b342..a9b7df8150c 100644 --- a/guide/src/building-and-distribution.md +++ b/guide/src/building-and-distribution.md @@ -73,7 +73,7 @@ The PyO3 ecosystem has two packaging tools, [`maturin`] and [`setuptools-rust`], PyO3 has some functionality for configuring projects when building Python extension modules: -- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. +- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. `maturin` and `setuptools-rust` set this automatically. - The `abi3` Cargo feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel. This section describes the packaging tools before describing how to build manually without them. From 2b2cfde80ebf97e2a02706a64552ad4ad8f60550 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 4 Nov 2025 14:21:32 +0000 Subject: [PATCH 6/9] fix invalid toml --- pyo3-ffi-check/Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyo3-ffi-check/Cargo.toml b/pyo3-ffi-check/Cargo.toml index b022689f484..7a1be1ac6cc 100644 --- a/pyo3-ffi-check/Cargo.toml +++ b/pyo3-ffi-check/Cargo.toml @@ -6,10 +6,8 @@ publish = false [dependencies] pyo3-ffi-check-macro = { path = "./macro" } -memoffset = "0.9.0" - -[dependencies] pyo3-ffi = { path = "../pyo3-ffi" } +memoffset = "0.9.0" [build-dependencies] bindgen = "0.69.4" From da892968744d05aad05a6789158c52797e76cc43 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 8 Nov 2025 18:04:26 +0000 Subject: [PATCH 7/9] Update pyo3-ffi/README.md Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com> --- pyo3-ffi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-ffi/README.md b/pyo3-ffi/README.md index 8fed234eff0..1a5d7bcf604 100644 --- a/pyo3-ffi/README.md +++ b/pyo3-ffi/README.md @@ -41,7 +41,7 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.27.1" +pyo3-ffi = "0.27.1" [build-dependencies] # This is only necessary if you need to configure your build based on From e7826fb281af1fc54adcb259d3ade7a3274fc1d4 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 30 Nov 2025 09:02:07 +0000 Subject: [PATCH 8/9] Update guide/src/building-and-distribution.md Co-authored-by: Matthijs Kok --- guide/src/building-and-distribution.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/guide/src/building-and-distribution.md b/guide/src/building-and-distribution.md index a9b7df8150c..d788dd013a5 100644 --- a/guide/src/building-and-distribution.md +++ b/guide/src/building-and-distribution.md @@ -206,7 +206,8 @@ As a result, PyO3 uses an envionment variable `PYO3_BUILD_EXTENSION_MODULE` to d This should only be set when building a library for distribution. `maturin >= 1.9.4` and `setuptools-rust >= 1.12` will set this for you automatically. -> Note: historically PyO3 used an `extension-module` feature to perform the same function now done by the `PYO3_BUILD_EXTENSION_MODULE` env var. +> [!NOTE] +> Historically PyO3 used an `extension-module` feature to perform the same function now done by the `PYO3_BUILD_EXTENSION_MODULE` env var. > This feature caused linking to be disabled for all compile targets, including Rust tests and benchmarks. > > Projects are encouraged to migrate off the feature, as it caused [major development pain](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) due to the lack of linking. From 23ac49a660ab439467f610a5ffda3d31b10be3ef Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 30 Nov 2025 09:05:41 +0000 Subject: [PATCH 9/9] fixup plugin example README --- examples/plugin/README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/plugin/README.md b/examples/plugin/README.md index b88c2f0277d..3d980368fc0 100644 --- a/examples/plugin/README.md +++ b/examples/plugin/README.md @@ -16,18 +16,12 @@ It will build the app, as well as the plugin API, then run the app, load the plu The plugin API is in a separate crate `plugin_api`, so you can test it separately from the main app. -To build the API only package, first install `maturin`: +To build the API only package, install and build with `maturin`: ```shell pip install maturin -``` - -When building the plugin, simply using `maturin develop` will fail to produce a viable extension module due to the features arrangement of PyO3. -Instead, one needs to enable the optional feature as follows: - -```shell cd plugin_api -maturin build" +maturin build ``` Alternatively, install nox and run the tests inside an isolated environment: