diff --git a/README.md b/README.md index 07953cb4f1b..f7492ed17ab 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ ## Usage -PyO3 supports Python 3.6 and up. The minimum required Rust version is 1.41. - -PyPy is also supported. Some minor features are unavailable on PyPy - please refer to the [pypy section in the guide](https://pyo3.rs/latest/building_and_distribution/pypy.html) for more information. +PyO3 supports the following software versions: + - Python 3.6 and up (CPython and PyPy) + - Rust 1.41 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. diff --git a/build.rs b/build.rs index 282949cf0c2..7b455aa688e 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ use std::{env, process::Command}; use pyo3_build_config::{ - bail, ensure, + bail, cargo_env_var, ensure, env_var, errors::{Context, Result}, InterpreterConfig, PythonImplementation, PythonVersion, }; @@ -22,7 +22,10 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { fn ensure_target_architecture(interpreter_config: &InterpreterConfig) -> Result<()> { // Try to check whether the target architecture matches the python library - let rust_target = match env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap().as_str() { + let rust_target = match cargo_env_var("CARGO_CFG_TARGET_POINTER_WIDTH") + .unwrap() + .as_str() + { "64" => "64-bit", "32" => "32-bit", x => bail!("unexpected Rust target pointer width: {}", x), @@ -55,14 +58,14 @@ fn ensure_target_architecture(interpreter_config: &InterpreterConfig) -> Result< } fn get_rustc_link_lib(config: &InterpreterConfig) -> Result { - let link_name = if env::var_os("CARGO_CFG_TARGET_OS").unwrap() == "windows" { + let link_name = if cargo_env_var("CARGO_CFG_TARGET_OS").unwrap() == "windows" { if config.abi3 { // Link against python3.lib for the stable ABI on Windows. // See https://www.python.org/dev/peps/pep-0384/#linkage // // This contains only the limited ABI symbols. "pythonXY:python3".to_owned() - } else if env::var_os("CARGO_CFG_TARGET_ENV").unwrap() == "gnu" { + } else if cargo_env_var("CARGO_CFG_TARGET_ENV").unwrap() == "gnu" { // https://packages.msys2.org/base/mingw-w64-python format!( "pythonXY:python{}.{}", @@ -103,8 +106,8 @@ fn rustc_minor_version() -> Option { } fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<()> { - let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some(); + let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap(); + let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some(); match (is_extension_module, target_os.as_str()) { (_, "windows") => { // always link on windows, even with extension module @@ -144,7 +147,7 @@ fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<() _ => {} } - if env::var_os("CARGO_FEATURE_AUTO_INITIALIZE").is_some() { + if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() { if !interpreter_config.shared { bail!( "The `auto-initialize` feature is enabled, but your python installation only supports \ @@ -179,6 +182,9 @@ fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<() /// (including `pyo3-macros-backend` during macro expansion). fn configure_pyo3() -> Result<()> { let interpreter_config = pyo3_build_config::make_interpreter_config()?; + if env_var("PYO3_PRINT_CONFIG").map_or(false, |os_str| os_str == "1") { + print_config_and_exit(&interpreter_config); + } ensure_python_version(&interpreter_config)?; ensure_target_architecture(&interpreter_config)?; emit_cargo_configuration(&interpreter_config)?; @@ -207,6 +213,20 @@ fn configure_pyo3() -> Result<()> { Ok(()) } +fn print_config_and_exit(config: &InterpreterConfig) { + println!("\n-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --"); + println!("implementation: {}", config.implementation); + println!("interpreter version: {}", config.version); + println!("interpreter path: {:?}", config.executable); + println!("libdir: {:?}", config.libdir); + println!("shared: {}", config.shared); + println!("base prefix: {:?}", config.base_prefix); + println!("ld_version: {:?}", config.ld_version); + println!("pointer width: {:?}", config.calcsize_pointer); + + std::process::exit(101); +} + fn main() { // Print out error messages using display, to get nicer formatting. if let Err(e) = configure_pyo3() { diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 4d2df2f977f..383f3310b95 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -20,7 +20,6 @@ - [Advanced Topics](advanced.md) - [Building and Distribution](building_and_distribution.md) - [Supporting multiple Python versions](building_and_distribution/multiple_python_versions.md) - - [PyPy support](building_and_distribution/pypy.md) - [Useful Crates](ecosystem.md) - [Logging](ecosystem/logging.md) - [Async / Await](ecosystem/async-await.md) diff --git a/guide/src/building_and_distribution.md b/guide/src/building_and_distribution.md index fd720d8f334..54a81f30400 100644 --- a/guide/src/building_and_distribution.md +++ b/guide/src/building_and_distribution.md @@ -1,58 +1,100 @@ # Building and Distribution -## Python version +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. + +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. There is also a section at the end about cross-compiling projects using PyO3. + +There is an additional sub-chapter dedicated to [supporting multiple Python versions](./building_and_distribution/multiple_python_versions.html). + +## Configuring the Python version + +PyO3 uses a build script (backed by the [`pyo3-build-config`] crate) to determine the Python version and set the correct linker arguments. By default it will attempt to use the following in order: + - Any active Python virtualenv. + - 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`. + +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. + +To validate the configuration which PyO3 will use, you can run a compilation with the environment variable `PYO3_PRINT_CONFIG=1` set. An example output of doing this is shown below: + +```console +$ PYO3_PRINT_CONFIG=1 cargo build + Compiling pyo3 v0.14.1 (/home/david/dev/pyo3) +error: failed to run custom build command for `pyo3 v0.14.1 (/home/david/dev/pyo3)` + +Caused by: + process didn't exit successfully: `/home/david/dev/pyo3/target/debug/build/pyo3-7a8cf4fe22e959b7/build-script-build` (exit status: 101) + --- stdout + cargo:rerun-if-env-changed=PYO3_CROSS + cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR + cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_VERSION + cargo:rerun-if-env-changed=PYO3_PYTHON + cargo:rerun-if-env-changed=VIRTUAL_ENV + cargo:rerun-if-env-changed=CONDA_PREFIX + cargo:rerun-if-env-changed=PATH + cargo:rerun-if-env-changed=PYO3_PRINT_CONFIG + + -- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile -- + implementation: CPython + interpreter version: 3.8 + interpreter path: Some("/usr/bin/python") + libdir: Some("/usr/lib") + shared: true + base prefix: Some("/usr") + ld_version: Some("3.8") + pointer width: Some(8) +``` -PyO3 uses a build script to determine the Python version and set the correct linker arguments. By default it uses the `python3` executable. You can override the Python interpreter by setting `PYO3_PYTHON`, e.g., `PYO3_PYTHON=python3.6`. +## Building Python extension modules -## Linking +Python extension modules need to be compiled differently depending on the OS (and architecture) that they are being compiled for. As well as multiple OSes (and architectures), there are also many different Python versions which are actively supported. Packages uploaded to [PyPI](https://pypi.org/) usually want to upload prebuilt "wheels" covering many OS/arch/version combinations so that users on all these different platforms don't have to compile the package themselves. Package vendors can opt-in to the "abi3" limited Python API which allows their wheels to be used on multiple Python versions, reducing the number of wheels they need to compile, but restricts the functionality they can use. -Different linker arguments must be set for libraries/extension modules and binaries, which includes both standalone binaries and tests. (More specifically, binaries must be told where to find libpython and libraries must not link to libpython for [manylinux](https://www.python.org/dev/peps/pep-0513/) compliance). +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. -Since PyO3's build script can't know whether you're building a binary or a library, you have to activate the `extension-module` feature to get the build options for a library, or it'll default to binary. +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 `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. -If you have e.g. a library crate and a profiling crate alongside, you need to use optional features. E.g. you put the following in the library crate: +This section describes each of these packaging tools before describiing how to build manually without them. It then proceeds with an explanation of the `extension-module` feature. Finally, there is a section describing PyO3's `abi3` features. -```toml -[dependencies] -pyo3 = { {{#PYO3_CRATE_VERSION}} } +### Packaging tools +The PyO3 ecosystem has two main choices to abstract the process of developing Python extension modules: +- [`maturin`] is a command-line tool to build, package and upload Python modules. It makes opinionated choices about project layout meaning it needs very little configuration. This makes it a great choice for users who are building a Python extension from scratch and don't need flexibility. +- [`setuptools-rust`] is an add-on for `setuptools` which adds extra keyword arguments to the `setup.py` configuration file. It requires more configuration than `maturin`, however this gives additional flexibility for users adding Rust to an existing Python package that can't satisfy `maturin`'s constraints. -[lib] -name = "hyperjson" -crate-type = ["rlib", "cdylib"] +Consult each project's documentation for full details on how to get started using them and how to upload wheels to PyPI. -[features] -default = ["pyo3/extension-module"] -``` +There are also [`maturin-starter`] and [`setuptools-rust-starter`] examples in the PyO3 repository. -And this in the profiling crate: +### Manual builds -```toml -[dependencies] -my_main_crate = { path = "..", default-features = false } -pyo3 = { {{#PYO3_CRATE_VERSION}} } -``` +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). -On Linux/macOS you might have to change `LD_LIBRARY_PATH` to include libpython, while on windows you might need to set `LIB` to include `pythonxy.lib` (where x and y are major and minor version), which is normally either in the `libs` or `Lib` folder of a Python installation. +Once built, symlink (or copy) and rename the shared library from Cargo's `target/` directory to your desired output directory: +- on macOS, rename `libyour_module.dylib` to `your_module.so`. +- on Windows, rename `libyour_module.dll` to `your_module.pyd`. +- on Linux, rename `libyour_module.so` to `your_module.so`. -## Testing, building and distribution +You can then open a Python shell in the output directory and you'll be able to run `import your_module`. -There are two main ways to test, build and distribute your module as a Python package: [setuptools-rust] and [maturin]. setuptools-rust needs several configuration files (`setup.py`, `MANIFEST.in`, `build-wheels.sh`, etc.). It allows (and sometimes requires) writing custom workflows in python. maturin has only few options and works without any additional configuration, instead it requires a rigid project structure and does not support some functionality of setuptools such as package data ([pyo3/maturin#258](https://github.com/PyO3/maturin/issues/258)), multiple extensions or running python scripts at build time. +See, as an example, Bazel rules to build PyO3 on Linux at https://github.com/TheButlah/rules_pyo3. -### Manual builds +### The `extension-module` feature -You can also symlink (or copy) and rename the shared library from the `target` folder: -- on macOS, rename `libyour_module.dylib` to `your_module.so`. -- on Windows, rename `libyour_module.dll` to `your_module.pyd`. -- on Linux, rename `libyour_module.so` to `your_module.so`. +PyO3's `extension-module` feature is used to disable [linking](https://en.wikipedia.org/wiki/Linker_(computing)) to `libpython` on unix targets. -You can then open a Python shell in the same folder and you'll be able to run `import your_module`. +This is necessary because 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. -## `Py_LIMITED_API`/`abi3` +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-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) for an example workaround. -By default, Python extension modules can only be used with the same Python version they were compiled against -- if you build an extension module with Python 3.5, you can't import it using Python 3.8. [PEP 384](https://www.python.org/dev/peps/pep-0384/) introduced the idea of the limited Python API, which would have a stable ABI enabling extension modules built with it to be used against multiple Python versions. This is also known as `abi3`. +### `Py_LIMITED_API`/`abi3` -The advantage of building extension module using the limited Python API is that you only need to build and distribute a single copy (for each OS / architecture), and your users can install it on all Python versions from your [minimum version](#minimum-python-version-for-abi3) and up. The downside of this is that PyO3 can't use optimizations which rely on being compiled against a known exact Python version. It's up to you to decide whether this matters for your extension module. It's also possible to design your extension module such that you can distribute `abi3` wheels but allow users compiling from source to benefit from additional optimizations - see the [support for multiple python versions](./building_and_distribution/multiple_python_versions.html) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag. +By default, Python extension modules can only be used with the same Python version they were compiled against. For example, an extension module built for Python 3.5 can't be imported in Python 3.8. [PEP 384](https://www.python.org/dev/peps/pep-0384/) introduced the idea of the limited Python API, which would have a stable ABI enabling extension modules built with it to be used against multiple Python versions. This is also known as `abi3`. + +The advantage of building extension modules using the limited Python API is that package vendors only need to build and distribute a single copy (for each OS / architecture), and users can install it on all Python versions from the [minimum version](#minimum-python-version-for-abi3) and up. The downside of this is that PyO3 can't use optimizations which rely on being compiled against a known exact Python version. It's up to you to decide whether this matters for your extension module. It's also possible to design your extension module such that you can distribute `abi3` wheels but allow users compiling from source to benefit from additional optimizations - see the [support for multiple python versions](./building_and_distribution/multiple_python_versions.html) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag. There are three steps involved in making use of `abi3` when building Python packages as wheels: @@ -63,12 +105,12 @@ There are three steps involved in making use of `abi3` when building Python pack pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3"] } ``` -2. Ensure that the built shared objects are correctly marked as `abi3`. This is accomplished by telling your build system that you're using the limited API. [maturin] >= 0.9.0 and [setuptools-rust] >= 0.11.4 support `abi3` wheels. +2. Ensure that the built shared objects are correctly marked as `abi3`. This is accomplished by telling your build system that you're using the limited API. [`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels. See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) for more. 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` +#### 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`. @@ -81,7 +123,7 @@ As an advanced feature, you can build PyO3 wheel without calling Python interpre > 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. -### Missing features +#### Missing features Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for `abi3`. These are: @@ -91,49 +133,6 @@ not work when compiling for `abi3`. These are: - The buffer API is not supported. - Optimizations which rely on knowledge of the exact Python version compiled against. -## Cross Compiling - -Cross compiling PyO3 modules is relatively straightforward and requires a few pieces of software: - -* A toolchain for your target. -* The appropriate options in your Cargo `.config` for the platform you're targeting and the toolchain you are using. -* A Python interpreter that's already been compiled for your target. -* A Python interpreter that is built for your host and available through the `PATH` or setting the [`PYO3_PYTHON`](#python-version) variable. - -See [github.com/japaric/rust-cross](https://github.com/japaric/rust-cross) for a primer on cross compiling Rust in general. - -After you've obtained the above, you can build a cross compiled PyO3 module by setting a few extra environment variables: - -* `PYO3_CROSS`: If present this variable forces PyO3 to configure as a cross-compilation. -* `PYO3_CROSS_LIB_DIR`: This variable must be set to the directory containing the target's libpython DSO and the associated `_sysconfigdata*.py` file for Unix-like targets, or the Python DLL import libraries for the Windows target. -* `PYO3_CROSS_PYTHON_VERSION`: Major and minor version (e.g. 3.9) of the target Python installation. This variable is only needed if PyO3 cannot determine the version to target from `abi3-py3*` features, or if there are multiple versions of Python present in `PYO3_CROSS_LIB_DIR`. - -An example might look like the following (assuming your target's sysroot is at `/home/pyo3/cross/sysroot` and that your target is `armv7`): - -```sh -export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" - -cargo build --target armv7-unknown-linux-gnueabihf -``` - -If there are multiple python versions at the cross lib directory and you cannot set a more precise location to include both the `libpython` DSO and `_sysconfigdata*.py` files, you can set the required version: -```sh -export PYO3_CROSS_PYTHON_VERSION=3.8 -export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" - -cargo build --target armv7-unknown-linux-gnueabihf -``` - -Or another example with the same sys root but building for Windows: -```sh -export PYO3_CROSS_PYTHON_VERSION=3.9 -export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" - -cargo build --target x86_64-pc-windows-gnu -``` - -Any of the `abi3-py3*` features can be enabled instead of setting `PYO3_CROSS_PYTHON_VERSION` in the above examples. - ## Embedding Python in Rust If you want to embed the Python interpreter inside a Rust program, there are two modes in which this can be done: dynamically and statically. We'll cover each of these modes in the following sections. Each of them affect how you must distribute your program. Instead of learning how to do this yourself, you might want to consider using a project like [PyOxidizer] to ship your application and all of its dependencies in a single file. @@ -148,6 +147,8 @@ This mode of embedding works well for Rust tests which need access to the Python For distributing your program to non-technical users, you will have to consider including the Python shared library in your distribution as well as setting up wrapper scripts to set the right environment variables (such as `LD_LIBRARY_PATH` on UNIX, or `PATH` on Windows). +Note that 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). + ### Statically embedding the Python interpreter Embedding the Python interpreter statically means including the contents of a Python static library directly inside your Rust binary. This means that to distribute your program you only need to ship your binary file: it contains the Python interpreter inside the binary! @@ -178,11 +179,56 @@ The known complications are: 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. -## Bazel +## Cross Compiling + +Thanks to Rust's great cross-compilation support, cross-compiling using PyO3 is relatively straightforward. To get started, you'll need a few pieces of software: + +* A toolchain for your target. +* The appropriate options in your Cargo `.config` for the platform you're targeting and the toolchain you are using. +* A Python interpreter that's already been compiled for your target. +* A Python interpreter that is built for your host and available through the `PATH` or setting the [`PYO3_PYTHON`](#python-version) variable. + +After you've obtained the above, you can build a cross-compiled PyO3 module by using Cargo's `--target` flag. PyO3's build script will detect that you are attempting a cross-compile based on your host machine and the desired target. + +When cross-compiling, PyO3's build script cannot execute the target Python interpreter to query the configuration, so there are a few additional environment variables you may need to set: + +* `PYO3_CROSS`: If present this variable forces PyO3 to configure as a cross-compilation. +* `PYO3_CROSS_LIB_DIR`: This variable must be set to the directory containing the target's libpython DSO and the associated `_sysconfigdata*.py` file for Unix-like targets, or the Python DLL import libraries for the Windows target. +* `PYO3_CROSS_PYTHON_VERSION`: Major and minor version (e.g. 3.9) of the target Python installation. This variable is only needed if PyO3 cannot determine the version to target from `abi3-py3*` features, or if there are multiple versions of Python present in `PYO3_CROSS_LIB_DIR`. + +An example might look like the following (assuming your target's sysroot is at `/home/pyo3/cross/sysroot` and that your target is `armv7`): -For an example of how to build python extensions using Bazel, see https://github.com/TheButlah/rules_pyo3 +```sh +export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" + +cargo build --target armv7-unknown-linux-gnueabihf +``` + +If there are multiple python versions at the cross lib directory and you cannot set a more precise location to include both the `libpython` DSO and `_sysconfigdata*.py` files, you can set the required version: +```sh +export PYO3_CROSS_PYTHON_VERSION=3.8 +export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" + +cargo build --target armv7-unknown-linux-gnueabihf +``` + +Or another example with the same sys root but building for Windows: +```sh +export PYO3_CROSS_PYTHON_VERSION=3.9 +export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib" + +cargo build --target x86_64-pc-windows-gnu +``` + +Any of the `abi3-py3*` features can be enabled instead of setting `PYO3_CROSS_PYTHON_VERSION` in the above examples. +The following resources may also be useful for cross-compiling: + - [github.com/japaric/rust-cross](https://github.com/japaric/rust-cross) is a primer on cross compiling Rust. + - [github.com/rust-embedded/cross](https://github.com/rust-embedded/cross) uses Docker to make Rust cross-compilation easier. -[maturin]: https://github.com/PyO3/maturin -[setuptools-rust]: https://github.com/PyO3/setuptools-rust +[`pyo3-build-config`]: https://github.com/PyO3/pyo3/tree/main/pyo3-build-config +[`maturin-starter`]: https://github.com/PyO3/pyo3/tree/main/examples/maturin-starter +[`setuptools-rust-starter`]: https://github.com/PyO3/pyo3/tree/main/examples/setuptools-rust-starter +[`maturin`]: https://github.com/PyO3/maturin +[`setuptools-rust`]: https://github.com/PyO3/setuptools-rust [PyOxidizer]: https://github.com/indygreg/PyOxidizer diff --git a/guide/src/building_and_distribution/pypy.md b/guide/src/building_and_distribution/pypy.md deleted file mode 100644 index 111cf43076c..00000000000 --- a/guide/src/building_and_distribution/pypy.md +++ /dev/null @@ -1,21 +0,0 @@ -# PyPy Support - -Using PyPy is supported via cpyext. - -Support is only provided for building Rust extension for code running under PyPy. This means that PyPy **cannot** be called from rust via cpyext. Note that there some differences in the ffi module between PyPy and CPython. - -This is a limitation of cpyext and support for embedding cpyext is not planned. - -Compilation against PyPy is done by exporting `PYO3_PYTHON` to point to a PyPy binary or by compiling in a PyPy virtualenv. - -For example, `PYO3_PYTHON="/path/to/pypy3" /path/to/pypy3 setup.py install` - - -## Unsupported features - -These are features currently supported by PyO3, but not yet implemented in cpyext. - -- Complex number functions (`_Py_c_sum`, `_Py_c_sum` ..) -- Conversion to rust's i128, u128 types. -- `PySequence_Count` (which is used to count number of element in array) -- `PyDict_MergeFromSeq2` (used in `PyDict::from_sequence`) diff --git a/guide/src/faq.md b/guide/src/faq.md index cbed281a6e0..021e7ff25e0 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -17,7 +17,7 @@ PyO3 provides a struct [`GILOnceCell`] which works equivalently to `OnceCell` bu ## I can't run `cargo test`: I'm having linker issues like "Symbol not found" or "Undefined reference to _PyExc_SystemError"! -Currently, [#341](https://github.com/PyO3/pyo3/issues/341) causes `cargo test` to fail with linking errors when the `extension-module` feature is activated. For now you can work around this by making the `extension-module` feature optional and running the tests with `cargo test --no-default-features`: +Currently, [#340](https://github.com/PyO3/pyo3/issues/340) causes `cargo test` to fail with linking errors when the `extension-module` feature is activated. For now you can work around this by making the `extension-module` feature optional and running the tests with `cargo test --no-default-features`: ```toml [dependencies.pyo3] @@ -138,5 +138,4 @@ print(f"a: {a}\nb: {b}") a: b: ``` -The downside to this approach is that any Rust code working on the `Outer` struct now has to acquire the GIL to do anything with its field. - +The downside to this approach is that any Rust code working on the `Outer` struct now has to acquire the GIL to do anything with its field. diff --git a/pyo3-build-config/src/bin/print-config.rs b/pyo3-build-config/src/bin/print-config.rs deleted file mode 100644 index d9da77495d2..00000000000 --- a/pyo3-build-config/src/bin/print-config.rs +++ /dev/null @@ -1,16 +0,0 @@ -use pyo3_build_config::{find_interpreter, get_config_from_interpreter}; - -fn main() -> Result<(), Box> { - let config = get_config_from_interpreter(&find_interpreter()?)?; - - println!("implementation: {}", config.implementation); - println!("interpreter version: {}", config.version); - println!("interpreter path: {:?}", config.executable); - println!("libdir: {:?}", config.libdir); - println!("shared: {}", config.shared); - println!("base prefix: {:?}", config.base_prefix); - println!("ld_version: {:?}", config.ld_version); - println!("pointer width: {:?}", config.calcsize_pointer); - - Ok(()) -} diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ef0860dbcc1..a8ac9972cab 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -25,13 +25,13 @@ const ABI3_MAX_MINOR: u8 = 9; /// Gets an environment variable owned by cargo. /// /// Environment variables set by cargo are expected to be valid UTF8. -fn cargo_env_var(var: &str) -> Option { +pub fn cargo_env_var(var: &str) -> Option { env::var_os(var).map(|os_string| os_string.to_str().unwrap().into()) } /// Gets an external environment variable, and registers the build script to rerun if /// the variable changes. -fn env_var(var: &str) -> Option { +pub fn env_var(var: &str) -> Option { println!("cargo:rerun-if-env-changed={}", var); env::var_os(var) } diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 38e8a3c2bcc..338e5b42460 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -27,7 +27,7 @@ pub use impl_::{ // Used in PyO3's build.rs #[doc(hidden)] -pub use impl_::make_interpreter_config; +pub use impl_::{cargo_env_var, env_var, make_interpreter_config}; /// Reads the configuration written by PyO3's build.rs /// diff --git a/src/gil.rs b/src/gil.rs index aa91ddc6a54..8d1832e7ad7 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -40,21 +40,22 @@ pub(crate) fn gil_is_acquired() -> bool { /// Prepares the use of Python in a free-threaded context. /// -/// If the Python interpreter is not already initialized, this function -/// will initialize it with disabled signal handling -/// (Python will not raise the `KeyboardInterrupt` exception). -/// Python signal handling depends on the notion of a 'main thread', which must be -/// the thread that initializes the Python interpreter. +/// If the Python interpreter is not already initialized, this function will initialize it with +/// signal handling disabled (Python will not raise the `KeyboardInterrupt` exception). Python +/// 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 both the Python interpreter and Python threading are 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). /// /// # Panics -/// - If the Python interpreter is initialized but Python threading is not, -/// a panic occurs. -/// It is not possible to safely access the Python runtime unless the main -/// thread (the thread which originally initialized Python) also initializes -/// threading. +/// - If the Python interpreter is initialized but Python threading is not, a panic occurs. +/// It is not possible to safely access the Python runtime unless the main thread (the thread +/// which originally initialized Python) also initializes threading. /// /// # Examples /// ```rust diff --git a/src/lib.rs b/src/lib.rs index e0dda9ab246..9f0c66682c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,12 +118,9 @@ //! //! # Minimum supported Rust and Python versions //! -//! PyO3 supports Python 3.6+ and Rust 1.41+. -//! -//! Building with PyPy is also possible (via cpyext) for Python 3.6, -//! targeted PyPy version is 7.3+. Please refer to the -//! [pypy section](https://pyo3.rs/latest/building_and_distribution/pypy.html) -//! in the guide for more information. +//! PyO3 supports the following software versions: +//! - Python 3.6 and up (CPython and PyPy) +//! - Rust 1.41 and up //! //! # Example: Building a native Python module //! @@ -495,10 +492,6 @@ pub mod doc_test { "guide/src/building_and_distribution.md", guide_building_and_distribution_md ); - doctest!( - "guide/src/building_and_distribution/pypy.md", - guide_building_and_distribution_pypy_md - ); doctest!("guide/src/class.md", guide_class_md); doctest!("guide/src/class/protocols.md", guide_class_protocols_md); doctest!("guide/src/conversions.md", guide_conversions_md);