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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/netlify-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Setup mdBook
uses: taiki-e/install-action@v2
with:
tool: mdbook@0.4, mdbook-tabs@0.2, lychee
tool: mdbook@0.5, mdbook-tabs@0.3, lychee

- name: Prepare tag
id: prepare_tag
Expand Down
4 changes: 2 additions & 2 deletions guide/book.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[book]
title = "PyO3 user guide"
description = "PyO3 user guide"
author = "PyO3 Project and Contributors"
authors = ["PyO3 Project and Contributors"]

[preprocessor.pyo3_version]
command = "python3 guide/pyo3_version.py"
command = "python3 pyo3_version.py"

[preprocessor.tabs]

Expand Down
18 changes: 9 additions & 9 deletions guide/pyo3_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- {{#PYO3_CRATE_VERSION}} with a relevant toml snippet (e.g. 'version = "0.13.2"')


Tested against mdbook 0.4.10.
Tested against mdbook 0.5.0.
"""

import json
Expand All @@ -28,26 +28,26 @@
PYO3_CRATE_VERSION = f'version = "{version}"'


def replace_section_content(section):
if not isinstance(section, dict) or "Chapter" not in section:
def replace_item_content(item):
if not isinstance(item, dict) or "Chapter" not in item:
return

# Replace raw and url-encoded forms
section["Chapter"]["content"] = (
section["Chapter"]["content"]
item["Chapter"]["content"] = (
item["Chapter"]["content"]
.replace("{{#PYO3_VERSION_TAG}}", PYO3_VERSION_TAG)
.replace("{{#PYO3_DOCS_URL}}", PYO3_DOCS_URL)
.replace("{{#PYO3_DOCS_VERSION}}", PYO3_DOCS_VERSION)
.replace("{{#PYO3_CRATE_VERSION}}", PYO3_CRATE_VERSION)
)

for sub_item in section["Chapter"]["sub_items"]:
replace_section_content(sub_item)
for sub_item in item["Chapter"]["sub_items"]:
replace_item_content(sub_item)


for line in sys.stdin:
if line:
[context, book] = json.loads(line)
for section in book["sections"]:
replace_section_content(section)
for item in book["items"]:
replace_item_content(item)
json.dump(book, fp=sys.stdout)
3 changes: 2 additions & 1 deletion guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ See [the relevant section of this guide](./building-and-distribution/multiple-py
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.

> Note: If you set more that one of these `abi3` version feature flags the lowest version always wins. For example, with both `abi3-py37` and `abi3-py38` set, PyO3 would build a wheel which supports Python 3.7 and up.
> [!NOTE]
> If you set more that one of these `abi3` version feature flags the lowest version always wins. For example, with both `abi3-py37` and `abi3-py38` set, PyO3 would build a wheel which supports Python 3.7 and up.

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

Expand Down
8 changes: 5 additions & 3 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ mod my_module {
}
```

## Bound<T> and interior mutability
## `Bound<T>` and interior mutability { #bound-and-interior-mutability }

It is often useful to turn a `#[pyclass]` type `T` into a Python object and access it from Rust code.
The [`Py<T>`] and [`Bound<'py, T>`] smart pointers are the ways to represent a Python object in PyO3's API.
Expand Down Expand Up @@ -807,10 +807,12 @@ Python::attach(|py| {
});
```

> Note: if the method has a `Result` return type and returns an `Err`, PyO3 will panic during
> [!NOTE]
> If the method has a `Result` return type and returns an `Err`, PyO3 will panic during
class creation.

> Note: `#[classattr]` does not work with [`#[pyo3(warn(...))]`](./function.md#warn) attribute.
> [!NOTE]
> `#[classattr]` does not work with [`#[pyo3(warn(...))]`](./function.md#warn) attribute.

If the class attribute is defined with `const` code only, one can also annotate associated constants:

Expand Down
3 changes: 2 additions & 1 deletion guide/src/class/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ This option also requires `eq`: According to the [Python docs](https://docs.pyth
struct Number(i32);
```

> **Note**: When implementing `__hash__` and comparisons, it is important that the following property holds:
> [!NOTE]
> When implementing `__hash__` and comparisons, it is important that the following property holds:
>
> ```text
> k1 == k2 -> hash(k1) == hash(k2)
Expand Down
12 changes: 9 additions & 3 deletions guide/src/class/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ The given signatures should be interpreted as follows:
The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively.

_Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._

<details>
<summary>Return type</summary>

The return type will normally be `bool` or `PyResult<bool>`, however any Python object can be returned.
</details>

Expand All @@ -100,6 +102,7 @@ The given signatures should be interpreted as follows:
_Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._
<details>
<summary>Return type</summary>

The return type will normally be `PyResult<bool>`, but any Python object can be returned.

If you want to leave some operations unimplemented, you can return `py.NotImplemented()`
Expand Down Expand Up @@ -135,7 +138,8 @@ The given signatures should be interpreted as follows:
- `__getattribute__(<self>, object) -> object`

<details>
<summary>Differences between `__getattr__` and `__getattribute__`</summary>
<summary>Differences between <code>__getattr__</code> and <code>__getattribute__</code></summary>

As in Python, `__getattr__` is only called if the attribute is not found
by normal attribute lookup. `__getattribute__`, on the other hand, is
called for *every* attribute access. If it wants to access existing
Expand Down Expand Up @@ -440,7 +444,8 @@ Immutable references do not have to be cleared, as every cycle must contain at l
- `__traverse__(<self>, pyo3::class::gc::PyVisit<'_>) -> Result<(), pyo3::class::gc::PyTraverseError>`
- `__clear__(<self>) -> ()`

> Note: `__traverse__` does not work with [`#[pyo3(warn(...))]`](../function.md#warn).
> [!NOTE]
> `__traverse__` does not work with [`#[pyo3(warn(...))]`](../function.md#warn).

Example:

Expand Down Expand Up @@ -471,7 +476,8 @@ impl ClassWithGCSupport {
Usually, an implementation of `__traverse__` should do nothing but calls to `visit.call`.
Most importantly, safe access to the interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` 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://github.com/pypy/pypy/issues/3848).
> [!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).

[`PySequence`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PySequence.html
<!-- rumdl-disable-next-line MD053 - false positive -->
Expand Down
3 changes: 2 additions & 1 deletion guide/src/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Some ways to achieve this are:
If the wrong DLL is linked it is possible that this happened because another program added itself and its own Python DLLs to `PATH`.
Rearrange your `PATH` variables to give the correct DLL priority.

> **Note**: Changes to `PATH` (or any other environment variable) are not visible to existing shells. Restart it for changes to take effect.
> [!NOTE]
> Changes to `PATH` (or any other environment variable) are not visible to existing shells. Restart it for changes to take effect.

For advanced troubleshooting, [Dependency Walker](https://www.dependencywalker.com/) can be used to diagnose linking errors.
1 change: 1 addition & 0 deletions guide/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ It also provides the `py_run!` macro.
These macros require a number of dependencies which may not be needed by users who just need PyO3 for Python FFI.
Disabling this feature enables faster builds for those users, as these dependencies will not be built if this feature is disabled.

> [!NOTE]
> This feature is enabled by default. To disable it, set `default-features = false` for the `pyo3` entry in your Cargo.toml.

### `multiple-pymethods`
Expand Down
3 changes: 2 additions & 1 deletion guide/src/function/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ num=-1 (was previously=44), py_args=(), name=World, py_kwargs=None
num=44
```

> Note: to use keywords like `struct` as a function argument, use "raw identifier" syntax `r#struct` in both the signature and the function definition:
> [!NOTE]
> To use keywords like `struct` as a function argument, use "raw identifier" syntax `r#struct` in both the signature and the function definition:
>
> ```rust,no_run
> # #![allow(dead_code)]
Expand Down
1 change: 1 addition & 0 deletions guide/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
To get started using PyO3 you will need three things: a Rust toolchain, a Python environment, and a way to build.
We'll cover each of these below.

> [!TIP]
> If you'd like to chat to the PyO3 maintainers and other PyO3 users, consider joining the [PyO3 Discord server](https://discord.gg/33kcChzH7f). We're keen to hear about your experience getting started, so we can make PyO3 as accessible as possible for everyone!

## Rust
Expand Down
14 changes: 8 additions & 6 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,9 @@ This change makes error conversions more precise and matches the semantics of ou

<details>
<summary><small>Click to expand</small></summary>
The `AsPyPointer` trait is mostly a leftover from the now removed gil-refs API. The last remaining uses were the GC API, namely `PyVisit::call`, and identity comparison (`PyAnyMethods::is` and `Py::is`).

The `AsPyPointer` trait is mostly a leftover from the now removed gil-refs API.
The last remaining uses were the GC API, namely `PyVisit::call`, and identity comparison (`PyAnyMethods::is` and `Py::is`).

`PyVisit::call` has been updated to take `T: Into<Option<&Py<T>>>`, which allows for arguments of type `&Py<T>`, `&Option<Py<T>>` and `Option<&Py<T>>`.
It is unlikely any changes are needed here to migrate.
Expand Down Expand Up @@ -713,6 +715,7 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {

<details>
<summary><small>Click to expand</small></summary>

If you rely on `impl<T> Clone for Py<T>` to fulfil trait requirements imposed by existing Rust code written without PyO3-based code in mind, the newly introduced feature `py-clone` must be enabled.

However, take care to note that the behaviour is different from previous versions.
Expand Down Expand Up @@ -861,6 +864,7 @@ To make the transition for the PyO3 ecosystem away from the GIL Refs API as smoo
Instead, variants using `Bound<T>` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound<PyTuple>` is the replacement form of `PyTuple::new`.
The GIL Ref APIs have been deprecated, but to make migration easier it is possible to disable these deprecation warnings by enabling the `gil-refs` feature.

> [!TIP]
> The one single exception where an existing API was changed in-place is the `pyo3::intern!` macro. Almost all uses of this macro did not need to update code to account it changing to return `&Bound<PyString>` immediately, and adding an `intern_bound!` replacement was perceived as adding more work for users.

It is recommended that users do this as a first step of updating to PyO3 0.21 so that the deprecation warnings do not get in the way of resolving the rest of the migration steps.
Expand Down Expand Up @@ -1126,10 +1130,9 @@ let obj: &Py<PyList> = bound.as_unbound();
let obj: Py<PyList> = bound.unbind();
```

<div class="warning">

⚠️ Warning: dangling pointer trap 💣

> [!WARNING]
> Dangling pointer trap 💣
>
> Because of the ownership changes, code which uses `.as_ptr()` to convert `&PyAny` and other GIL Refs to a `*mut pyo3_ffi::PyObject` should take care to avoid creating dangling pointers now that `Bound<PyAny>` carries ownership.
>
> For example, the following pattern with `Option<&PyAny>` can easily create a dangling pointer when migrating to the `Bound<PyAny>` smart pointer:
Expand All @@ -1145,7 +1148,6 @@ let obj: Py<PyList> = bound.unbind();
> let opt: Option<Bound<PyAny>> = ...;
> let p: *mut ffi::PyObject = opt.as_ref().map_or(std::ptr::null_mut(), Bound::as_ptr);
> ```
<div>

#### Migrating `FromPyObject` implementations

Expand Down
Loading