Skip to content

Commit c2dfa77

Browse files
committed
Merge branch 'main' into enum-feature-variants
2 parents 2de25e0 + d14bfd0 commit c2dfa77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+844
-533
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ jobs:
513513
with:
514514
components: rust-src
515515
# TODO: replace with setup-python when there is support
516-
- uses: deadsnakes/action@v3.1.0
516+
- uses: deadsnakes/action@v3.2.0
517517
with:
518518
python-version: '3.13-dev'
519519
nogil: true

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ inventory = { version = "0.3.0", optional = true }
3434
# crate integrations that can be added using the eponymous features
3535
anyhow = { version = "1.0.1", optional = true }
3636
chrono = { version = "0.4.25", default-features = false, optional = true }
37-
chrono-tz = { version = ">= 0.6, < 0.10", default-features = false, optional = true }
37+
chrono-tz = { version = ">= 0.6, < 0.11", default-features = false, optional = true }
3838
either = { version = "1.9", optional = true }
3939
eyre = { version = ">= 0.4, < 0.7", optional = true }
4040
hashbrown = { version = ">= 0.9, < 0.15", optional = true }
@@ -52,7 +52,7 @@ portable-atomic = "1.0"
5252
[dev-dependencies]
5353
assert_approx_eq = "1.1.0"
5454
chrono = "0.4.25"
55-
chrono-tz = ">= 0.6, < 0.10"
55+
chrono-tz = ">= 0.6, < 0.11"
5656
# Required for "and $N others" normalization
5757
trybuild = ">=1.0.70"
5858
proptest = { version = "1.0", default-features = false, features = ["std"] }

Contributing.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ nox -s docs -- open
5353
#### Doctests
5454

5555
We use lots of code blocks in our docs. Run `cargo test --doc` when making changes to check that
56-
the doctests still work, or `cargo test` to run all the tests including doctests. See
56+
the doctests still work, or `cargo test` to run all the Rust tests including doctests. See
5757
https://doc.rust-lang.org/rustdoc/documentation-tests.html for a guide on doctests.
5858

5959
#### Building the guide
@@ -86,16 +86,15 @@ Everybody is welcome to submit comments on open PRs. Please help ensure new PyO3
8686

8787
Here are a few things to note when you are writing PRs.
8888

89-
### Continuous Integration
89+
### Testing and Continuous Integration
9090

9191
The PyO3 repo uses GitHub Actions. PRs are blocked from merging if CI is not successful. Formatting, linting and tests are checked for all Rust and Python code. In addition, all warnings in Rust code are disallowed (using `RUSTFLAGS="-D warnings"`).
9292

9393
Tests run with all supported Python versions with the latest stable Rust compiler, as well as for Python 3.9 with the minimum supported Rust version.
9494

9595
If you are adding a new feature, you should add it to the `full` feature in our *Cargo.toml** so that it is tested in CI.
9696

97-
You can run these tests yourself with
98-
`nox`. Use `nox -l` to list the full set of subcommands you can run.
97+
You can run these checks yourself with `nox`. Use `nox -l` to list the full set of subcommands you can run.
9998

10099
#### Linting Python code
101100
`nox -s ruff`
@@ -110,7 +109,7 @@ You can run these tests yourself with
110109
`nox -s clippy-all`
111110

112111
#### Tests
113-
`cargo test --features full`
112+
`nox -s test` or `cargo test` for Rust tests only, `nox -f pytests/noxfile.py -s test` for Python tests only
114113

115114
#### Check all conditional compilation
116115
`nox -s check-feature-powerset`

LICENSE-APACHE

Lines changed: 178 additions & 189 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ about this topic.
219219
- [river](https://github.com/online-ml/river) _Online machine learning in python, the computationally heavy statistics algorithms are implemented in Rust._
220220
- [robyn](https://github.com/sparckles/Robyn) A Super Fast Async Python Web Framework with a Rust runtime.
221221
- [rust-python-coverage](https://github.com/cjermain/rust-python-coverage) _Example PyO3 project with automated test coverage for Rust and Python._
222+
- [sail](https://github.com/lakehq/sail) _Unifying stream, batch, and AI workloads with Apache Spark compatibility._
222223
- [tiktoken](https://github.com/openai/tiktoken) _A fast BPE tokeniser for use with OpenAI's models._
223224
- [tokenizers](https://github.com/huggingface/tokenizers/tree/main/bindings/python) _Python bindings to the Hugging Face tokenizers (NLP) written in Rust._
224225
- [tzfpy](http://github.com/ringsaturn/tzfpy) _A fast package to convert longitude/latitude to timezone name._

examples/decorator/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl PyCounter {
5151
println!("{} has been called {} time(s).", name, new_count);
5252

5353
// After doing something, we finally forward the call to the wrapped function
54-
let ret = self.wraps.call_bound(py, args, kwargs)?;
54+
let ret = self.wraps.call(py, args, kwargs)?;
5555

5656
// We could do something with the return value of
5757
// the function before returning it

examples/plugin/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1313
//do useful work
1414
Python::with_gil(|py| {
1515
//add the current directory to import path of Python (do not use this in production!)
16-
let syspath: &PyList = py.import("sys")?.getattr("path")?.extract()?;
16+
let syspath: Bound<PyList> = py.import("sys")?.getattr("path")?.extract()?;
1717
syspath.insert(0, &path)?;
1818
println!("Import path is: {:?}", syspath);
1919

guide/src/building-and-distribution.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ If you're packaging your library for redistribution, you should indicated the Py
9292

9393
To use PyO3 with bazel one needs to manually configure PyO3, PyO3-ffi and PyO3-macros. In particular, one needs to make sure that it is compiled with the right python flags for the version you intend to use.
9494
For example see:
95-
1. https://github.com/OliverFM/pytorch_with_gazelle -- for a minimal example of a repo that can use PyO3, PyTorch and Gazelle to generate python Build files.
96-
2. https://github.com/TheButlah/rules_pyo3 -- which has more extensive support, but is outdated.
95+
96+
1. [github.com/abrisco/rules_pyo3](https://github.com/abrisco/rules_pyo3) -- General rules for building extension modules.
97+
2. [github.com/OliverFM/pytorch_with_gazelle](https://github.com/OliverFM/pytorch_with_gazelle) -- for a minimal example of a repo that can use PyO3, PyTorch and Gazelle to generate python Build files.
98+
3. [github.com/TheButlah/rules_pyo3](https://github.com/TheButlah/rules_pyo3) -- is somewhat dated.
9799

98100
#### Platform tags
99101

guide/src/class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ or [`PyRefMut`] instead of `&mut self`.
330330
Then you can access a parent class by `self_.as_super()` as `&PyRef<Self::BaseClass>`,
331331
or by `self_.into_super()` as `PyRef<Self::BaseClass>` (and similar for the `PyRefMut`
332332
case). For convenience, `self_.as_ref()` can also be used to get `&Self::BaseClass`
333-
directly; however, this approach does not let you access base clases higher in the
333+
directly; however, this approach does not let you access base classes higher in the
334334
inheritance hierarchy, for which you would need to chain multiple `as_super` or
335335
`into_super` calls.
336336

guide/src/migration.md

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ This is purely additional and should just extend the possible return types.
139139

140140
PyO3 0.23 introduces a new unified `IntoPyObject` trait to convert Rust types into Python objects.
141141
Notable features of this new trait:
142-
- conversions can now return an error
142+
- conversions can now return an error
143143
- compared to `IntoPy<T>` the generic `T` moved into an associated type, so
144144
- there is now only one way to convert a given type
145145
- the output type is stronger typed and may return any Python type instead of just `PyAny`
@@ -150,7 +150,7 @@ All PyO3 provided types as well as `#[pyclass]`es already implement `IntoPyObjec
150150
need to adapt an implementation of `IntoPyObject` to stay compatible with the Python APIs.
151151

152152

153-
Before:
153+
Before:
154154
```rust
155155
# use pyo3::prelude::*;
156156
# #[allow(dead_code)]
@@ -198,6 +198,80 @@ impl<'a, 'py> IntoPyObject<'py> for &'a MyPyObjectWrapper {
198198
```
199199
</details>
200200

201+
### Free-threaded Python Support
202+
<details open>
203+
<summary><small>Click to expand</small></summary>
204+
205+
PyO3 0.23 introduces preliminary support for the new free-threaded build of
206+
CPython 3.13. PyO3 features that implicitly assumed the existence of the GIL
207+
are not exposed in the free-threaded build, since they are no longer safe.
208+
209+
If you make use of these features then you will need to account for the
210+
unavailability of this API in the free-threaded build. One way to handle it is
211+
via conditional compilation -- extensions built for the free-threaded build will
212+
have the `Py_GIL_DISABLED` attribute defined.
213+
214+
### `GILProtected`
215+
216+
`GILProtected` allows mutable access to static data by leveraging the GIL to
217+
lock concurrent access from other threads. In free-threaded python there is no
218+
GIL, so you will need to replace this type with some other form of locking. In
219+
many cases, `std::sync::Atomic` or `std::sync::Mutex` will be sufficient. If the
220+
locks do not guard the execution of arbitrary Python code or use of the CPython
221+
C API then conditional compilation is likely unnecessary since `GILProtected`
222+
was not needed in the first place.
223+
224+
Before:
225+
226+
```rust
227+
# fn main() {
228+
# #[cfg(not(Py_GIL_DISABLED))] {
229+
# use pyo3::prelude::*;
230+
use pyo3::sync::GILProtected;
231+
use pyo3::types::{PyDict, PyNone};
232+
use std::cell::RefCell;
233+
234+
static OBJECTS: GILProtected<RefCell<Vec<Py<PyDict>>>> =
235+
GILProtected::new(RefCell::new(Vec::new()));
236+
237+
Python::with_gil(|py| {
238+
// stand-in for something that executes arbitrary python code
239+
let d = PyDict::new(py);
240+
d.set_item(PyNone::get(py), PyNone::get(py)).unwrap();
241+
OBJECTS.get(py).borrow_mut().push(d.unbind());
242+
});
243+
# }}
244+
```
245+
246+
After:
247+
248+
```rust
249+
# use pyo3::prelude::*;
250+
# fn main() {
251+
use pyo3::types::{PyDict, PyNone};
252+
use std::sync::Mutex;
253+
254+
static OBJECTS: Mutex<Vec<Py<PyDict>>> = Mutex::new(Vec::new());
255+
256+
Python::with_gil(|py| {
257+
// stand-in for something that executes arbitrary python code
258+
let d = PyDict::new(py);
259+
d.set_item(PyNone::get(py), PyNone::get(py)).unwrap();
260+
// we're not executing python code while holding the lock, so GILProtected
261+
// was never needed
262+
OBJECTS.lock().unwrap().push(d.unbind());
263+
});
264+
# }
265+
```
266+
267+
If you are executing arbitrary Python code while holding the lock, then you will
268+
need to use conditional compilation to use `GILProtected` on GIL-enabled python
269+
builds and mutexes otherwise. Python 3.13 introduces `PyMutex`, which releases
270+
the GIL while the lock is held, so that is another option if you only need to
271+
support newer Python versions.
272+
273+
</details>
274+
201275
## from 0.21.* to 0.22
202276

203277
### Deprecation of `gil-refs` feature continues
@@ -248,6 +322,8 @@ If you rely on `impl<T> Clone for Py<T>` to fulfil trait requirements imposed by
248322

249323
However, take care to note that the behaviour is different from previous versions. If `Clone` was called without the GIL being held, we tried to delay the application of these reference count increments until PyO3-based code would re-acquire it. This turned out to be impossible to implement in a sound manner and hence was removed. Now, if `Clone` is called without the GIL being held, we panic instead for which calling code might not be prepared.
250324

325+
It is advised to migrate off the `py-clone` feature. The simplest way to remove dependency on `impl<T> Clone for Py<T>` is to wrap `Py<T>` as `Arc<Py<T>>` and use cloning of the arc.
326+
251327
Related to this, we also added a `pyo3_disable_reference_pool` conditional compilation flag which removes the infrastructure necessary to apply delayed reference count decrements implied by `impl<T> Drop for Py<T>`. They do not appear to be a soundness hazard as they should lead to memory leaks in the worst case. However, the global synchronization adds significant overhead to cross the Python-Rust boundary. Enabling this feature will remove these costs and make the `Drop` implementation abort the process if called without the GIL being held instead.
252328
</details>
253329

guide/src/python-from-rust/calling-existing-code.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() -> PyResult<()> {
2727
## Want to run just an expression? Then use `eval_bound`.
2828

2929
[`Python::eval_bound`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.eval_bound) is
30-
a method to execute a [Python expression](https://docs.python.org/3.7/reference/expressions.html)
30+
a method to execute a [Python expression](https://docs.python.org/3/reference/expressions.html)
3131
and return the evaluated value as a `Bound<'py, PyAny>` object.
3232

3333
```rust
@@ -51,7 +51,7 @@ Python::with_gil(|py| {
5151
## Want to run statements? Then use `run_bound`.
5252

5353
[`Python::run_bound`] is a method to execute one or more
54-
[Python statements](https://docs.python.org/3.7/reference/simple_stmts.html).
54+
[Python statements](https://docs.python.org/3/reference/simple_stmts.html).
5555
This method returns nothing (like any Python statement), but you can get
5656
access to manipulated objects via the `locals` dict.
5757

guide/src/python-from-rust/function-calls.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,18 @@ fn main() -> PyResult<()> {
9191

9292
// call object with PyDict
9393
let kwargs = [(key1, val1)].into_py_dict(py);
94-
fun.call_bound(py, (), Some(&kwargs))?;
94+
fun.call(py, (), Some(&kwargs))?;
9595

9696
// pass arguments as Vec
9797
let kwargs = vec![(key1, val1), (key2, val2)];
98-
fun.call_bound(py, (), Some(&kwargs.into_py_dict(py)))?;
98+
fun.call(py, (), Some(&kwargs.into_py_dict(py)))?;
9999

100100
// pass arguments as HashMap
101101
let mut kwargs = HashMap::<&str, i32>::new();
102102
kwargs.insert(key1, 1);
103-
fun.call_bound(py, (), Some(&kwargs.into_py_dict(py)))?;
103+
fun.call(py, (), Some(&kwargs.into_py_dict(py)))?;
104104

105105
Ok(())
106106
})
107107
}
108-
```
109-
110-
<div class="warning">
111-
112-
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` is temporarily named [`Py<T>::call_bound`]({{#PYO3_DOCS_URL}}/pyo3/struct.Py.html#method.call_bound) (and `call_method` is temporarily `call_method_bound`).
113-
114-
(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.)
115-
116-
</div>
108+
```

newsfragments/4453.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make subclassing a class that doesn't allow that a compile-time error instead of runtime

newsfragments/4504.changed.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* The `GILProtected` struct is not available on the free-threaded build of
2+
Python 3.13.

newsfragments/4511.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add Python-ref cloning `clone_ref` for `GILOnceCell<Py<T>>`

newsfragments/4520.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `Borrowed::as_ptr`.

newsfragments/4521.removed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove private FFI definitions `_Py_c_sum`, `_Py_c_diff`, `_Py_c_neg`, `_Py_c_prod`, `_Py_c_quot`, `_Py_c_pow`, `_Py_c_abs`.

newsfragments/4529.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Added FFI bindings for `PyImport_AddModuleRef`.

newsfragments/4534.changed.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* Updated the FFI bindings for functions and struct fields that have been
2+
deprecated or removed. You may see new deprecation warnings if you are using
3+
functions or fields exposed by the C API that are deprecated.

newsfragments/4534.removed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Removed the bindings for the private function `_PyErr_ChainExceptions`.

pyo3-build-config/src/impl_.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,8 +1059,9 @@ impl FromStr for BuildFlag {
10591059
}
10601060
}
10611061

1062-
/// A list of python interpreter compile-time preprocessor defines that
1063-
/// we will pick up and pass to rustc via `--cfg=py_sys_config={varname}`;
1062+
/// A list of python interpreter compile-time preprocessor defines.
1063+
///
1064+
/// PyO3 will pick these up and pass to rustc via `--cfg=py_sys_config={varname}`;
10641065
/// this allows using them conditional cfg attributes in the .rs files, so
10651066
///
10661067
/// ```rust

pyo3-ffi-check/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ fn main() {
4848

4949
macro_rules! check_field {
5050
($struct_name:ident, $field:ident, $bindgen_field:ident) => {{
51-
#[allow(clippy::used_underscore_binding)]
51+
// some struct fields are deprecated but still present in the ABI
52+
#[allow(clippy::used_underscore_binding, deprecated)]
5253
let pyo3_ffi_offset = memoffset::offset_of!(pyo3_ffi::$struct_name, $field);
5354
#[allow(clippy::used_underscore_binding)]
5455
let bindgen_offset = memoffset::offset_of!(bindings::$struct_name, $bindgen_field);

pyo3-ffi/src/ceval.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern "C" {
2424
closure: *mut PyObject,
2525
) -> *mut PyObject;
2626

27+
#[cfg(not(Py_3_13))]
2728
#[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))]
2829
#[cfg_attr(PyPy, link_name = "PyPyEval_CallObjectWithKeywords")]
2930
pub fn PyEval_CallObjectWithKeywords(
@@ -33,6 +34,7 @@ extern "C" {
3334
) -> *mut PyObject;
3435
}
3536

37+
#[cfg(not(Py_3_13))]
3638
#[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))]
3739
#[inline]
3840
pub unsafe fn PyEval_CallObject(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject {
@@ -41,9 +43,11 @@ pub unsafe fn PyEval_CallObject(func: *mut PyObject, arg: *mut PyObject) -> *mut
4143
}
4244

4345
extern "C" {
46+
#[cfg(not(Py_3_13))]
4447
#[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))]
4548
#[cfg_attr(PyPy, link_name = "PyPyEval_CallFunction")]
4649
pub fn PyEval_CallFunction(obj: *mut PyObject, format: *const c_char, ...) -> *mut PyObject;
50+
#[cfg(not(Py_3_13))]
4751
#[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))]
4852
#[cfg_attr(PyPy, link_name = "PyPyEval_CallMethod")]
4953
pub fn PyEval_CallMethod(
@@ -95,9 +99,22 @@ extern "C" {
9599
}
96100

97101
extern "C" {
102+
#[cfg(not(Py_3_13))]
98103
#[cfg_attr(PyPy, link_name = "PyPyEval_ThreadsInitialized")]
104+
#[cfg_attr(
105+
Py_3_9,
106+
deprecated(
107+
note = "Deprecated in Python 3.9, this function always returns true in Python 3.7 or newer."
108+
)
109+
)]
99110
pub fn PyEval_ThreadsInitialized() -> c_int;
100111
#[cfg_attr(PyPy, link_name = "PyPyEval_InitThreads")]
112+
#[cfg_attr(
113+
Py_3_9,
114+
deprecated(
115+
note = "Deprecated in Python 3.9, this function does nothing in Python 3.7 or newer."
116+
)
117+
)]
101118
pub fn PyEval_InitThreads();
102119
pub fn PyEval_AcquireLock();
103120
pub fn PyEval_ReleaseLock();

pyo3-ffi/src/compat/py_3_13.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,16 @@ compat_function!(
3737
item
3838
}
3939
);
40+
41+
compat_function!(
42+
originally_defined_for(Py_3_13);
43+
44+
#[inline]
45+
pub unsafe fn PyImport_AddModuleRef(
46+
name: *const std::os::raw::c_char,
47+
) -> *mut crate::PyObject {
48+
use crate::{compat::Py_XNewRef, PyImport_AddModule};
49+
50+
Py_XNewRef(PyImport_AddModule(name))
51+
}
52+
);

0 commit comments

Comments
 (0)