docs: use `lychee` to check link URLs (#3941)

* guide: install `mdbook-linkcheck`

* use `shutil` to copy license files

* move from `mdbook-linkcheck` to `lychee`

* clean guide & doc build products before build

* fix more broken links

* review: mejrs
This commit is contained in:
David Hewitt 2024-03-08 14:10:47 +00:00 committed by GitHub
parent 770d9b7f01
commit 14d1d2a9ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 135 additions and 65 deletions

View File

@ -26,9 +26,9 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
uses: taiki-e/install-action@v2
with:
mdbook-version: "0.4.19"
tool: mdbook,lychee
- name: Prepare tag
id: prepare_tag
@ -36,11 +36,11 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "::set-output name=tag_name::${TAG_NAME}"
# This builds the book in target/guide.
# This builds the book in target/guide/.
- name: Build the guide
run: |
python -m pip install --upgrade pip && pip install nox
nox -s build-guide
nox -s check-guide
env:
PYO3_VERSION_TAG: ${{ steps.prepare_tag.outputs.tag_name }}

2
.gitignore vendored
View File

@ -14,6 +14,8 @@ dist/
.eggs/
venv*
guide/book/
guide/src/LICENSE-APACHE
guide/src/LICENSE-MIT
*.so
*.out
*.egg-info

View File

@ -71,9 +71,17 @@ if [ "${INSTALLED_MDBOOK_VERSION}" != "mdbook v${MDBOOK_VERSION}" ]; then
cargo install mdbook@${MDBOOK_VERSION} --force
fi
# Install latest mdbook-linkcheck. Netlify will cache the cargo bin dir, so this will
# only build mdbook-linkcheck if needed.
MDBOOK_LINKCHECK_VERSION=$(cargo search mdbook-linkcheck --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"')
INSTALLED_MDBOOK_LINKCHECK_VERSION=$(mdbook-linkcheck --version || echo "none")
if [ "${INSTALLED_MDBOOK_LINKCHECK_VERSION}" != "mdbook v${MDBOOK_LINKCHECK_VERSION}" ]; then
cargo install mdbook-linkcheck@${MDBOOK_LINKCHECK_VERSION} --force
fi
pip install nox
nox -s build-guide
mv target/guide netlify_build/main/
mv target/guide/ netlify_build/main/
## Build public docs

View File

@ -1687,7 +1687,7 @@ Yanked
[0.9.2]: https://github.com/pyo3/pyo3/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/pyo3/pyo3/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/pyo3/pyo3/compare/v0.8.5...v0.9.0
[0.8.4]: https://github.com/pyo3/pyo3/compare/v0.8.4...v0.8.5
[0.8.5]: https://github.com/pyo3/pyo3/compare/v0.8.4...v0.8.5
[0.8.4]: https://github.com/pyo3/pyo3/compare/v0.8.3...v0.8.4
[0.8.3]: https://github.com/pyo3/pyo3/compare/v0.8.2...v0.8.3
[0.8.2]: https://github.com/pyo3/pyo3/compare/v0.8.1...v0.8.2
@ -1696,7 +1696,8 @@ Yanked
[0.7.0]: https://github.com/pyo3/pyo3/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/pyo3/pyo3/compare/v0.5.3...v0.6.0
[0.5.3]: https://github.com/pyo3/pyo3/compare/v0.5.2...v0.5.3
[0.5.2]: https://github.com/pyo3/pyo3/compare/v0.5.0...v0.5.2
[0.5.2]: https://github.com/pyo3/pyo3/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/pyo3/pyo3/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/pyo3/pyo3/compare/v0.4.1...v0.5.0
[0.4.1]: https://github.com/pyo3/pyo3/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/pyo3/pyo3/compare/v0.3.2...v0.4.0

View File

@ -70,6 +70,12 @@ First, install [`mdbook`][mdbook] and [`nox`][nox]. Then, run
nox -s build-guide -- --open
```
To check all links in the guide are valid, also install [`lychee`][lychee] and use the `check-guide` session instead:
```shell
nox -s check-guide
```
### Help design the next PyO3
Issues which don't yet have a clear solution use the [needs-design](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Aneeds-design) label.
@ -171,7 +177,7 @@ First, there are Rust-based benchmarks located in the `pyo3-benches` subdirector
nox -s bench
Second, there is a Python-based benchmark contained in the `pytests` subdirectory. You can read more about it [here](pytests).
Second, there is a Python-based benchmark contained in the `pytests` subdirectory. You can read more about it [here](https://github.com/PyO3/pyo3/tree/main/pytests).
## Code coverage
@ -211,4 +217,5 @@ In the meanwhile, some of our maintainers have personal GitHub sponsorship pages
- [messense](https://github.com/sponsors/messense)
[mdbook]: https://rust-lang.github.io/mdBook/cli/index.html
[lychee]: https://github.com/lycheeverse/lychee
[nox]: https://github.com/theacodes/nox

View File

@ -9,4 +9,4 @@ command = "python3 guide/pyo3_version.py"
[output.html]
git-repository-url = "https://github.com/PyO3/pyo3/tree/main/guide"
edit-url-template = "https://github.com/PyO3/pyo3/edit/main/guide/{path}"
playground.runnable = false
playground.runnable = false

View File

@ -33,7 +33,7 @@ struct MyClass {}
struct MyClass {}
```
[params-1]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
[params-1]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html
[params-2]: https://en.wikipedia.org/wiki/Free_list
[params-3]: https://doc.rust-lang.org/std/marker/trait.Send.html
[params-4]: https://doc.rust-lang.org/std/rc/struct.Rc.html

View File

@ -38,7 +38,7 @@ However, there is an exception for method receiver, so async methods can accept
Even if it is not possible to pass a `py: Python<'_>` parameter to `async fn`, the GIL is still held during the execution of the future it's also the case for regular `fn` without `Python<'_>`/`&PyAny` parameter, yet the GIL is held.
It is still possible to get a `Python` marker using [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil); because `with_gil` is reentrant and optimized, the cost will be negligible.
It is still possible to get a `Python` marker using [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.with_gil); because `with_gil` is reentrant and optimized, the cost will be negligible.
## Release the GIL across `.await`
@ -70,7 +70,7 @@ where
## Cancellation
Cancellation on the Python side can be caught using [`CancelHandle`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html) type, by annotating a function parameter with `#[pyo3(cancel_handle)].
Cancellation on the Python side can be caught using [`CancelHandle`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html) type, by annotating a function parameter with `#[pyo3(cancel_handle)]`.
```rust
# #![allow(dead_code)]

View File

@ -4,7 +4,7 @@ This chapter of the guide goes into detail on how to build and distribute projec
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).
There is an additional sub-chapter dedicated to [supporting multiple Python versions](./building_and_distribution/multiple_python_versions.md).
## Configuring the Python version
@ -177,7 +177,7 @@ The downside of not linking to `libpython` is that binaries, tests, and examples
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.
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.md) 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:
@ -198,7 +198,7 @@ See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://
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`.
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.html#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime.
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.
@ -232,7 +232,7 @@ not work when compiling for `abi3`. These are:
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.
PyO3 automatically switches between the two linking modes depending on whether the Python distribution you have configured PyO3 to use ([see above](#python-version)) contains a shared library or a static library. The static library is most often seen in Python distributions compiled from source without the `--enable-shared` configuration option. For example, this is the default for `pyenv` on macOS.
PyO3 automatically switches between the two linking modes depending on whether the Python distribution you have configured PyO3 to use ([see above](#configuring-the-python-version)) contains a shared library or a static library. The static library is most often seen in Python distributions compiled from source without the `--enable-shared` configuration option. For example, this is the default for `pyenv` on macOS.
### Dynamically embedding the Python interpreter
@ -242,7 +242,7 @@ 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).
Note that PyPy cannot be embedded in Rust (or any other software). Support for this is tracked on the [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
### Statically embedding the Python interpreter
@ -285,7 +285,7 @@ Thanks to Rust's great cross-compilation support, cross-compiling using PyO3 is
* 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 (optional when building "abi3" extension modules).
* A Python interpreter that is built for your host and available through the `PATH` or setting the [`PYO3_PYTHON`](#python-version) variable (optional when building "abi3" extension modules).
* A Python interpreter that is built for your host and available through the `PATH` or setting the [`PYO3_PYTHON`](#configuring-the-python-version) variable (optional when building "abi3" extension modules).
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.

View File

@ -85,7 +85,7 @@ This `#[cfg]` marks code which is running on PyPy.
## Checking the Python version at runtime
When building with PyO3's `abi3` feature, your extension module will be compiled against a specific [minimum version](../building_and_distribution.html#minimum-python-version-for-abi3) of Python, but may be running on newer Python versions.
When building with PyO3's `abi3` feature, your extension module will be compiled against a specific [minimum version](../building_and_distribution.md#minimum-python-version-for-abi3) of Python, but may be running on newer Python versions.
For example with PyO3's `abi3-py38` feature, your extension will be compiled as if it were for Python 3.8. If you were using `pyo3-build-config`, `#[cfg(Py_3_8)]` would be present. Your user could freely install and run your abi3 extension on Python 3.9.
@ -104,5 +104,5 @@ Python::with_gil(|py| {
});
```
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version
[`Python::version_info()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version_info
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.version
[`Python::version_info()`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.version_info

View File

@ -16,7 +16,7 @@ This chapter will discuss the functionality and configuration these attributes o
- [`#[classmethod]`](#class-methods)
- [`#[classattr]`](#class-attributes)
- [`#[args]`](#method-arguments)
- [Magic methods and slots](class/protocols.html)
- [Magic methods and slots](class/protocols.md)
- [Classes as function arguments](#classes-as-function-arguments)
## Defining a new class
@ -82,7 +82,7 @@ When you need to share ownership of data between Python and Rust, instead of usi
A Rust `struct Foo<T>` with a generic parameter `T` generates new compiled implementations each time it is used with a different concrete type for `T`. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping `Foo` in Python, where there needs to be a single compiled implementation of `Foo` which is integrated with the Python interpreter.
Currently, the best alternative is to write a macro which expands to a new #[pyclass] for each instantiation you want:
Currently, the best alternative is to write a macro which expands to a new `#[pyclass]` for each instantiation you want:
```rust
# #![allow(dead_code)]
@ -898,9 +898,7 @@ py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44,
py_args=(), py_kwargs=None, name=World, num=-1, num_before=44
```
## Making class method signatures available to Python
The [`text_signature = "..."`](./function.md#text_signature) option for `#[pyfunction]` also works for `#[pymethods]`:
The [`#[pyo3(text_signature = "...")`](./function/signature.md#overriding-the-generated-signature) option for `#[pyfunction]` also works for `#[pymethods]`.
```rust
# #![allow(dead_code)]
@ -1002,7 +1000,7 @@ impl MyClass {
Note that `text_signature` on `#[new]` is not compatible with compilation in
`abi3` mode until Python 3.10 or greater.
## #[pyclass] enums
## `#[pyclass]` enums
Enum support in PyO3 comes in two flavors, depending on what kind of variants the enum has: simple and complex.

View File

@ -440,6 +440,6 @@ fn wrap(obj: &Bound<'_, PyAny>) -> Result<i32, PyErr> {
```
[`PyErr::take`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/struct.PyErr.html#method.take
[`Python`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html
[`Python`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html
[`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
[`pyo3::ffi::PyLong_AsUnsignedLongMask`]: {{#PYO3_DOCS_URL}}/pyo3/ffi/fn.PyLong_AsUnsignedLongMask.html

View File

@ -450,7 +450,7 @@ Usually, an implementation of `__traverse__` should do nothing but calls to `vis
Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`,
i.e. `Python::with_gil` will panic.
> Note: these methods are part of the C API, PyPy does not necessarily honor them. If you are building for PyPy you should measure memory consumption to make sure you do not have runaway memory growth. See [this issue on the PyPy bug tracker](https://foss.heptapod.net/pypy/pypy/-/issues/3899).
> Note: these methods are part of the C API, PyPy does not necessarily honor them. If you are building for PyPy you should measure memory consumption to make sure you do not have runaway memory growth. See [this issue on the PyPy bug tracker](https://github.com/pypy/pypy/issues/3848).
[`IterNextOutput`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/enum.IterNextOutput.html
[`PySequence`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PySequence.html

View File

@ -43,7 +43,7 @@ The table below contains the Python type and the corresponding function argument
| `typing.Sequence[T]` | `Vec<T>` | `&PySequence` |
| `typing.Mapping[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^3], `indexmap::IndexMap<K, V>`[^4] | `&PyMapping` |
| `typing.Iterator[Any]` | - | `&PyIterator` |
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](traits.html#deriving-a-hrefhttpsdocsrspyo3latestpyo3conversiontraitfrompyobjecthtmlfrompyobjecta-for-enums) | - |
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](traits.md#deriving-frompyobject-for-enums) | - |
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:

View File

@ -197,7 +197,7 @@ to do something special with the object that it returns.
Normally in Python, that something special is the `await` keyword, but in order to await this
coroutine in Rust, we first need to convert it into Rust's version of a `coroutine`: a `Future`.
That's where `pyo3-asyncio` comes in.
[`pyo3_asyncio::into_future`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.into_future.html)
[`pyo3_asyncio::async_std::into_future`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.into_future.html)
performs this conversion for us.
The following example uses `into_future` to call the `py_sleep` function shown above and then await the
@ -349,7 +349,7 @@ implementations _prefer_ control over the main thread, this can still make some
Because Python needs to control the main thread, we can't use the convenient proc macros from Rust
runtimes to handle the `main` function or `#[test]` functions. Instead, the initialization for PyO3 has to be done from the `main` function and the main
thread must block on [`pyo3_asyncio::run_forever`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.run_forever.html) or [`pyo3_asyncio::async_std::run_until_complete`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.run_until_complete.html).
thread must block on [`pyo3_asyncio::async_std::run_until_complete`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.run_until_complete.html).
Because we have to block on one of those functions, we can't use [`#[async_std::main]`](https://docs.rs/async-std/latest/async_std/attr.main.html) or [`#[tokio::main]`](https://docs.rs/tokio/1.1.0/tokio/attr.main.html)
since it's not a good idea to make long blocking calls during an async function.

View File

@ -128,5 +128,5 @@ defines exceptions for several standard library modules.
[`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html
[`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html
[`PyErr::from_value`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_value
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance_of
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.is_instance_of

View File

@ -12,7 +12,7 @@ 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.
See the [building and distribution](building_and_distribution.md#linking) section for further detail.
See the [building and distribution](building_and_distribution.md#the-extension-module-feature) section for further detail.
### `abi3`
@ -45,7 +45,7 @@ section for further detail.
### `auto-initialize`
This feature changes [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.with_gil) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed.
This feature changes [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.with_gil) to automatically initialize a Python interpreter (by calling [`prepare_freethreaded_python`]({{#PYO3_DOCS_URL}}/pyo3/fn.prepare_freethreaded_python.html)) if needed.
If you do not enable this feature, you should call `pyo3::prepare_freethreaded_python()` before attempting to call any other Python APIs.
@ -114,7 +114,7 @@ Adds a dependency on [anyhow](https://docs.rs/anyhow). Enables a conversion from
### `chrono`
Adds a dependency on [chrono](https://docs.rs/chrono). Enables a conversion from [chrono](https://docs.rs/chrono)'s types to python:
- [Duration](https://docs.rs/chrono/latest/chrono/struct.Duration.html) -> [`PyDelta`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDelta.html)
- [TimeDelta](https://docs.rs/chrono/latest/chrono/struct.TimeDelta.html) -> [`PyDelta`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDelta.html)
- [FixedOffset](https://docs.rs/chrono/latest/chrono/offset/struct.FixedOffset.html) -> [`PyDelta`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDelta.html)
- [Utc](https://docs.rs/chrono/latest/chrono/offset/struct.Utc.html) -> [`PyTzInfo`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyTzInfo.html)
- [NaiveDate](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDate.html) -> [`PyDate`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDate.html)
@ -129,7 +129,7 @@ It requires at least Python 3.9.
### `either`
Adds a dependency on [either](https://docs.rs/either). Enables a conversions into [either](https://docs.rs/either)s [`Either`](https://docs.rs/either/latest/either/struct.Report.html) type.
Adds a dependency on [either](https://docs.rs/either). Enables a conversions into [either](https://docs.rs/either)s [`Either`](https://docs.rs/either/latest/either/enum.Either.html) type.
### `eyre`
@ -145,7 +145,7 @@ Adds a dependency on [indexmap](https://docs.rs/indexmap) and enables conversion
### `num-bigint`
Adds a dependency on [num-bigint](https://docs.rs/num-bigint) and enables conversions into its [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUInt.html) types.
Adds a dependency on [num-bigint](https://docs.rs/num-bigint) and enables conversions into its [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) types.
### `num-complex`

View File

@ -38,7 +38,7 @@ There are also additional sections on the following topics:
The `#[pyo3]` attribute can be used to modify properties of the generated Python function. It can take any combination of the following options:
- <a name="name"></a> `#[pyo3(name = "...")]`
- <a id="name"></a> `#[pyo3(name = "...")]`
Overrides the name exposed to Python.
@ -67,15 +67,15 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
# });
```
- <a name="signature"></a> `#[pyo3(signature = (...))]`
- <a id="signature"></a> `#[pyo3(signature = (...))]`
Defines the function signature in Python. See [Function Signatures](./function/signature.md).
- <a name="text_signature"></a> `#[pyo3(text_signature = "...")]`
- <a id="text_signature"></a> `#[pyo3(text_signature = "...")]`
Overrides the PyO3-generated function signature visible in Python tooling (such as via [`inspect.signature`]). See the [corresponding topic in the Function Signatures subchapter](./function/signature.md#making-the-function-signature-available-to-python).
- <a name="pass_module" ></a> `#[pyo3(pass_module)]`
- <a id="pass_module" ></a> `#[pyo3(pass_module)]`
Set this option to make PyO3 pass the containing module as the first argument to the function. It is then possible to use the module in the function body. The first argument **must** be of type `&Bound<'_, PyModule>`, `Bound<'_, PyModule>`, or `Py<PyModule>`.
@ -101,7 +101,7 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
The `#[pyo3]` attribute can be used on individual arguments to modify properties of them in the generated function. It can take any combination of the following options:
- <a name="from_py_with"></a> `#[pyo3(from_py_with = "...")]`
- <a id="from_py_with"></a> `#[pyo3(from_py_with = "...")]`
Set this on an option to specify a custom function to convert the function argument from Python to the desired Rust type, instead of using the default `FromPyObject` extraction. The function signature must be `fn(&Bound<'_, PyAny>) -> PyResult<T>` where `T` is the Rust type of the argument.

View File

@ -23,7 +23,7 @@ In summary:
As indicated in the previous section, when a `PyResult` containing an `Err` crosses from Rust to Python, PyO3 will raise the exception contained within.
Accordingly, to raise an exception from a `#[pyfunction]`, change the return type `T` to `PyResult<T>`. When the function returns an `Err` it will raise a Python exception. (Other `Result<T, E>` types can be used as long as the error `E` has a `From` conversion for `PyErr`, see [implementing a conversion](#implementing-an-error-conversion) below.)
Accordingly, to raise an exception from a `#[pyfunction]`, change the return type `T` to `PyResult<T>`. When the function returns an `Err` it will raise a Python exception. (Other `Result<T, E>` types can be used as long as the error `E` has a `From` conversion for `PyErr`, see [custom Rust error types](#custom-rust-error-types) below.)
This also works for functions in `#[pymethods]`.

View File

@ -115,7 +115,7 @@ at the end of each loop iteration, before the `with_gil()` closure ends.
When doing this, you must be very careful to ensure that once the `GILPool` is
dropped you do not retain access to any owned references created after the
`GILPool` was created. Read the
[documentation for `Python::new_pool()`]({{#PYO3_DOCS_URL}}/pyo3/prelude/struct.Python.html#method.new_pool)
[documentation for `Python::new_pool()`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.new_pool)
for more information on safety.
This memory management can also be applicable when writing extension modules.

View File

@ -44,7 +44,7 @@ pyo3 = { version = "0.21", features = ["gil-refs"] }
### `PyTypeInfo` and `PyTryFrom` have been adjusted
The `PyTryFrom` trait has aged poorly, its [`try_from`] method now conflicts with `try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`.
The `PyTryFrom` trait has aged poorly, its `try_from` method now conflicts with `TryFrom::try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`.
To tighten up the PyO3 traits as part of the deprecation of the GIL Refs API the `PyTypeInfo` trait has had a simpler companion `PyTypeCheck`. The methods [`PyAny::downcast`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast) and [`PyAny::downcast_exact`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.downcast_exact) no longer use `PyTryFrom` as a bound, instead using `PyTypeCheck` and `PyTypeInfo` respectively.
@ -1299,7 +1299,7 @@ impl MyClass {
```
Basically you can return `Self` or `Result<Self>` directly.
For more, see [the constructor section](class.html#constructor) of this guide.
For more, see [the constructor section](class.md#constructor) of this guide.
### PyCell
PyO3 0.9 introduces [`PyCell`], which is a [`RefCell`]-like object wrapper

View File

@ -45,7 +45,7 @@ file. Otherwise, you will get an import error in Python with the following messa
`ImportError: dynamic module does not define module export function (PyInit_name_of_your_module)`
To import the module, either:
- copy the shared library as described in [Manual builds](building_and_distribution.html#manual-builds), or
- copy the shared library as described in [Manual builds](building_and_distribution.md#manual-builds), or
- use a tool, e.g. `maturin develop` with [maturin](https://github.com/PyO3/maturin) or
`python setup.py develop` with [setuptools-rust](https://github.com/PyO3/setuptools-rust).

View File

@ -117,4 +117,4 @@ test_word_count_python_sequential 27.3985 (15.82) 45.452
You can see that the Python threaded version is not much slower than the Rust sequential version, which means compared to an execution on a single CPU core the speed has doubled.
[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.allow_threads
[`Python::allow_threads`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.allow_threads

View File

@ -10,15 +10,15 @@ Any Python-native object reference (such as `&PyAny`, `&PyList`, or `&PyCell<MyC
PyO3 offers two APIs to make function calls:
* [`call`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call) - call any callable Python object.
* [`call_method`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method) - call a method on the Python object.
* [`call`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call) - call any callable Python object.
* [`call_method`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call_method) - call a method on the Python object.
Both of these APIs take `args` and `kwargs` arguments (for positional and keyword arguments respectively). There are variants for less complex calls:
* [`call1`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call1) and [`call_method1`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method1) to call only with positional `args`.
* [`call0`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call0) and [`call_method0`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method0) to call with no arguments.
* [`call1`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call1) and [`call_method1`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call_method1) to call only with positional `args`.
* [`call0`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call0) and [`call_method0`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call_method0) to call with no arguments.
For convenience the [`Py<T>`](types.html#pyt-and-pyobject) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the GIL is held.
For convenience the [`Py<T>`](types.md#pyt-and-pyobject) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the GIL is held.
The example below calls a Python function behind a `PyObject` (aka `Py<PyAny>`) reference:
@ -113,9 +113,9 @@ fn main() -> PyResult<()> {
<div class="warning">
During PyO3's [migration from "GIL Refs" to the `Bound<T>` smart pointer](./migration.md#migrating-from-the-gil-refs-api-to-boundt), [`Py<T>::call`]({{#PYO3_DOCS_URL}}/pyo3/struct.py#method.call) is temporarily named `call_bound` (and `call_method` is temporarily `call_method_bound`).
During PyO3's [migration from "GIL Refs" to the `Bound<T>` smart pointer](./migration.md#migrating-from-the-gil-refs-api-to-boundt), [`Py<T>::call`]({{#PYO3_DOCS_URL}}/pyo3/struct.Py.html#method.call) is temporarily named `call_bound` (and `call_method` is temporarily `call_method_bound`).
(This temporary naming is only the case for the `Py<T>` smart pointer. The methods on the `&PyAny` GIL Ref such as `call` have not been given replacements, and the methods on the `Bound<PyAny>` smart pointer such as [`Bound<PyAny>::call`]({#PYO3_DOCS_URL}}/pyo3/prelude/trait.pyanymethods#tymethod.call) already use follow the newest API conventions.)
(This temporary naming is only the case for the `Py<T>` smart pointer. The methods on the `&PyAny` GIL Ref such as `call` have not been given replacements, and the methods on the `Bound<PyAny>` smart pointer such as [`Bound<PyAny>::call`]({{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.call) already use follow the newest API conventions.)
</div>
@ -428,7 +428,7 @@ fn main() -> PyResult<()> {
```
[`Python::run`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.run
[`Python::run`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.run
[`py_run!`]: {{#PYO3_DOCS_URL}}/pyo3/macro.py_run.html
## Need to use a context manager from Rust?

View File

@ -1,6 +1,6 @@
# Using in Python a Rust function with trait bounds
PyO3 allows for easy conversion from Rust to Python for certain functions and classes (see the [conversion table](conversions/tables.html).
PyO3 allows for easy conversion from Rust to Python for certain functions and classes (see the [conversion table](conversions/tables.md).
However, it is not always straightforward to convert Rust code that requires a given trait implementation as an argument.
This tutorial explains how to convert a Rust function that takes a trait as argument for use in Python with classes implementing the same methods as the trait.

View File

@ -2,6 +2,7 @@ from contextlib import contextmanager
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
@ -25,6 +26,10 @@ nox.options.sessions = ["test", "clippy", "rustfmt", "ruff", "docs"]
PYO3_DIR = Path(__file__).parent
PYO3_TARGET = Path(os.environ.get("CARGO_TARGET_DIR", PYO3_DIR / "target")).absolute()
PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src"
PYO3_GUIDE_TARGET = PYO3_TARGET / "guide"
PYO3_DOCS_TARGET = PYO3_TARGET / "doc"
PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12")
PYPY_VERSIONS = ("3.7", "3.8", "3.9", "3.10")
@ -356,6 +361,7 @@ def docs(session: nox.Session) -> None:
rustdoc_flags.append(session.env.get("RUSTDOCFLAGS", ""))
session.env["RUSTDOCFLAGS"] = " ".join(rustdoc_flags)
shutil.rmtree(PYO3_DOCS_TARGET, ignore_errors=True)
_run_cargo(
session,
*toolchain_flags,
@ -371,7 +377,49 @@ def docs(session: nox.Session) -> None:
@nox.session(name="build-guide", venv_backend="none")
def build_guide(session: nox.Session):
_run(session, "mdbook", "build", "-d", "../target/guide", "guide", *session.posargs)
shutil.rmtree(PYO3_GUIDE_TARGET, ignore_errors=True)
_run(session, "mdbook", "build", "-d", PYO3_GUIDE_TARGET, "guide", *session.posargs)
for license in ("LICENSE-APACHE", "LICENSE-MIT"):
target_file = PYO3_GUIDE_TARGET / license
target_file.unlink(missing_ok=True)
shutil.copy(PYO3_DIR / license, target_file)
@nox.session(name="check-guide", venv_backend="none")
def check_guide(session: nox.Session):
# reuse other sessions, but with default args
posargs = [*session.posargs]
del session.posargs[:]
build_guide(session)
docs(session)
session.posargs.extend(posargs)
remaps = {
f"file://{PYO3_GUIDE_SRC}/([^/]*/)*?%7B%7B#PYO3_DOCS_URL\}}\}}": f"file://{PYO3_DOCS_TARGET}",
"%7B%7B#PYO3_DOCS_VERSION\}\}": "latest",
}
remap_args = []
for key, value in remaps.items():
remap_args.extend(("--remap", f"{key} {value}"))
# check all links in the guide
_run(
session,
"lychee",
"--include-fragments",
PYO3_GUIDE_SRC,
*remap_args,
*session.posargs,
)
# check external links in the docs
# (intra-doc links are checked by rustdoc)
_run(
session,
"lychee",
PYO3_DOCS_TARGET,
f"--remap=https://pyo3.rs/main/ file://{PYO3_GUIDE_TARGET}/",
f"--exclude=file://{PYO3_DOCS_TARGET}",
*session.posargs,
)
@nox.session(name="format-guide", venv_backend="none")
@ -451,10 +499,11 @@ _IGNORE_CHANGELOG_PR_CATEGORIES = (
@nox.session(name="check-changelog")
def check_changelog(session: nox.Session):
event_path = os.environ.get("GITHUB_EVENT_PATH")
if event_path is None:
if not _is_github_actions():
session.error("Can only check changelog on github actions")
event_path = os.environ["GITHUB_EVENT_PATH"]
with open(event_path) as event_file:
event = json.load(event_file)
@ -762,11 +811,12 @@ def _get_coverage_env() -> Dict[str, str]:
def _run(session: nox.Session, *args: str, **kwargs: Any) -> None:
"""Wrapper for _run(session, which creates nice groups on GitHub Actions."""
if "GITHUB_ACTIONS" in os.environ:
is_github_actions = _is_github_actions()
if is_github_actions:
# Insert ::group:: at the start of nox's command line output
print("::group::", end="", flush=True, file=sys.stderr)
session.run(*args, **kwargs)
if "GITHUB_ACTIONS" in os.environ:
if is_github_actions:
print("::endgroup::", file=sys.stderr)
@ -869,5 +919,9 @@ def _config_file() -> Iterator[_ConfigFile]:
yield _ConfigFile(config)
def _is_github_actions() -> bool:
return "GITHUB_ACTIONS" in os.environ
_BENCHES = "--manifest-path=pyo3-benches/Cargo.toml"
_FFI_CHECK = "--manifest-path=pyo3-ffi-check/Cargo.toml"

View File

@ -67,7 +67,7 @@ fn gil_is_acquired() -> bool {
///
/// 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).
/// [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
///
/// # Examples
/// ```rust