Skip to content

Commit

Permalink
introduce no-pool feature
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 26, 2023
1 parent 7cea93b commit 6719e98
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 18 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ auto-initialize = []
# Optimizes PyObject to Vec conversion and so on.
nightly = []

# Removes the "GIL Pool" internal datastructure from PyO3.
no-pool = []

# Activates all additional features
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
full = [
Expand All @@ -116,6 +119,7 @@ full = [
"anyhow",
"experimental-inspect",
"rust_decimal",
"no-pool",
]

[workspace]
Expand Down
8 changes: 8 additions & 0 deletions guide/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ See [the `#[pyclass]` implementation details](class.md#implementation-details) f

The `nightly` feature needs the nightly Rust compiler. This allows PyO3 to use the `auto_traits` and `negative_impls` features to fix the `Python::allow_threads` function.

### `no-pool`

This feature completes the migration of PyO3's API from the "GIL Refs" API to the `Bound<T>` smart pointer API.

There are two key effects to this feature:
- The internal "GIL Pool" datastructure is completely removed.
- Many GIL Ref APIs which have temporary `_bound` variants have their return values adjusted to their future version. For example, with this feature enabled `PyTuple::new` will return `Bound<'py, PyTuple>`.

### `resolve-config`

The `resolve-config` feature of the `pyo3-build-config` crate controls whether that crate's
Expand Down
2 changes: 1 addition & 1 deletion pytests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
publish = false

[dependencies]
pyo3 = { path = "../", features = ["extension-module"] }
pyo3 = { path = "../", features = ["extension-module", "no-pool"] }

[build-dependencies]
pyo3-build-config = { path = "../pyo3-build-config" }
Expand Down
12 changes: 6 additions & 6 deletions pytests/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate>
}

#[pyfunction]
fn get_date_tuple<'p>(py: Python<'p>, d: &PyDate) -> &'p PyTuple {
fn get_date_tuple<'p>(py: Python<'p>, d: &PyDate) -> Bound<'p, PyTuple> {
PyTuple::new(py, [d.get_year(), d.get_month() as i32, d.get_day() as i32])
}

Expand Down Expand Up @@ -48,7 +48,7 @@ fn time_with_fold<'p>(
}

#[pyfunction]
fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple {
fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> {
PyTuple::new(
py,
[
Expand All @@ -61,7 +61,7 @@ fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple {
}

#[pyfunction]
fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple {
fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> {
PyTuple::new(
py,
[
Expand All @@ -80,7 +80,7 @@ fn make_delta(py: Python<'_>, days: i32, seconds: i32, microseconds: i32) -> PyR
}

#[pyfunction]
fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> &'p PyTuple {
fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> Bound<'p, PyTuple> {
PyTuple::new(
py,
[
Expand Down Expand Up @@ -118,7 +118,7 @@ fn make_datetime<'p>(
}

#[pyfunction]
fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple {
fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> {
PyTuple::new(
py,
[
Expand All @@ -134,7 +134,7 @@ fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple {
}

#[pyfunction]
fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple {
fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> {
PyTuple::new(
py,
[
Expand Down
27 changes: 26 additions & 1 deletion src/types/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyT
impl PyTuple {
/// Deprecated form of `PyTuple::new_bound`.
#[track_caller]
#[cfg(not(feature = "no-pool"))]
#[deprecated(
since = "0.21.0",
note = "`PyTuple::new` will be replaced by `PyTuple::new_bound` in a future PyO3 version"
Expand All @@ -75,6 +76,21 @@ impl PyTuple {
Self::new_bound(py, elements).into_gil_ref()
}

/// Future form of `PyTuple::new_bound`.
#[track_caller]
#[inline]
#[cfg(feature = "no-pool")]
pub fn new<T, U>(
py: Python<'_>,
elements: impl IntoIterator<Item = T, IntoIter = U>,
) -> Bound<'_, PyTuple>
where
T: ToPyObject,
U: ExactSizeIterator<Item = T>,
{
Self::new_bound(py, elements)
}

/// Constructs a new tuple with the given elements.
///
/// If you want to create a [`PyTuple`] with elements of different or unknown types, or from an
Expand Down Expand Up @@ -119,10 +135,18 @@ impl PyTuple {
since = "0.21.0",
note = "`PyTuple::empty` will be replaced by `PyTuple::empty_bound` in a future PyO3 version"
)]
#[cfg(not(feature = "no-pool"))]
pub fn empty(py: Python<'_>) -> &PyTuple {
Self::empty_bound(py).into_gil_ref()
}

/// Future form of `PyTuple::empty_bound`.
#[inline]
#[cfg(feature = "no-pool")]
pub fn empty(py: Python<'_>) -> Bound<'_, PyTuple> {
Self::empty_bound(py)
}

/// Constructs an empty tuple (on the Python side, a singleton object).
pub fn empty_bound(py: Python<'_>) -> Bound<'_, PyTuple> {
unsafe {
Expand Down Expand Up @@ -798,6 +822,8 @@ tuple_conversion!(

#[cfg(test)]
mod tests {
#[cfg(feature = "no-pool")]
use crate::types::{any::PyAnyMethods, tuple::PyTupleMethods};
use crate::types::{PyAny, PyList, PyTuple};
use crate::{Python, ToPyObject};
use std::collections::HashSet;
Expand All @@ -807,7 +833,6 @@ mod tests {
Python::with_gil(|py| {
let ob = PyTuple::new(py, [1, 2, 3]);
assert_eq!(3, ob.len());
let ob: &PyAny = ob.into();
assert_eq!((1, 2, 3), ob.extract().unwrap());

let mut map = HashSet::new();
Expand Down
16 changes: 8 additions & 8 deletions tests/test_frompyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ pub struct Tuple(String, usize);
#[test]
fn test_tuple_struct() {
Python::with_gil(|py| {
let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]);
let tup = Tuple::extract(tup.as_ref());
let tup = PyTuple::new_bound(py, &[1.into_py(py), "test".into_py(py)]);
let tup = Tuple::extract(tup.into_gil_ref());
assert!(tup.is_err());
let tup = PyTuple::new(py, &["test".into_py(py), 1.into_py(py)]);
let tup = Tuple::extract(tup.as_ref()).expect("Failed to extract Tuple from PyTuple");
let tup = PyTuple::new_bound(py, &["test".into_py(py), 1.into_py(py)]);
let tup = Tuple::extract(tup.into_gil_ref()).expect("Failed to extract Tuple from PyTuple");
assert_eq!(tup.0, "test");
assert_eq!(tup.1, 1);
});
Expand Down Expand Up @@ -324,8 +324,8 @@ pub struct PyBool {
#[test]
fn test_enum() {
Python::with_gil(|py| {
let tup = PyTuple::new(py, &[1.into_py(py), "test".into_py(py)]);
let f = Foo::extract(tup.as_ref()).expect("Failed to extract Foo from tuple");
let tup = PyTuple::new_bound(py, &[1.into_py(py), "test".into_py(py)]);
let f = Foo::extract(tup.into_gil_ref()).expect("Failed to extract Foo from tuple");
match f {
Foo::TupleVar(test, test2) => {
assert_eq!(test, 1);
Expand Down Expand Up @@ -401,8 +401,8 @@ TypeError: failed to extract enum Foo ('TupleVar | StructVar | TransparentTuple
- variant StructWithGetItemArg (StructWithGetItemArg): KeyError: 'foo'"
);

let tup = PyTuple::empty(py);
let err = Foo::extract(tup.as_ref()).unwrap_err();
let tup = PyTuple::empty_bound(py);
let err = Foo::extract(tup.into_gil_ref()).unwrap_err();
assert_eq!(
err.to_string(),
"\
Expand Down
4 changes: 2 additions & 2 deletions tests/test_various.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ impl PickleSupport {
pub fn __reduce__<'py>(
slf: &'py PyCell<Self>,
py: Python<'py>,
) -> PyResult<(PyObject, &'py PyTuple, PyObject)> {
) -> PyResult<(PyObject, Bound<'py, PyTuple>, PyObject)> {
let cls = slf.to_object(py).getattr(py, "__class__")?;
let dict = slf.to_object(py).getattr(py, "__dict__")?;
Ok((cls, PyTuple::empty(py), dict))
Ok((cls, PyTuple::empty_bound(py), dict))
}
}

Expand Down

0 comments on commit 6719e98

Please sign in to comment.