Skip to content

Commit

Permalink
feature gate APIs using into_gil_ref (Part 1) (#4160)
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu authored May 9, 2024
1 parent d803f7f commit 635cb80
Show file tree
Hide file tree
Showing 22 changed files with 336 additions and 253 deletions.
20 changes: 10 additions & 10 deletions src/conversions/std/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,11 @@ mod tests {
Python::with_gil(|py| {
let array: [f32; 4] = [0.0, -16.0, 16.0, 42.0];
let pyobject = array.to_object(py);
let pylist: &PyList = pyobject.extract(py).unwrap();
assert_eq!(pylist[0].extract::<f32>().unwrap(), 0.0);
assert_eq!(pylist[1].extract::<f32>().unwrap(), -16.0);
assert_eq!(pylist[2].extract::<f32>().unwrap(), 16.0);
assert_eq!(pylist[3].extract::<f32>().unwrap(), 42.0);
let pylist = pyobject.downcast_bound::<PyList>(py).unwrap();
assert_eq!(pylist.get_item(0).unwrap().extract::<f32>().unwrap(), 0.0);
assert_eq!(pylist.get_item(1).unwrap().extract::<f32>().unwrap(), -16.0);
assert_eq!(pylist.get_item(2).unwrap().extract::<f32>().unwrap(), 16.0);
assert_eq!(pylist.get_item(3).unwrap().extract::<f32>().unwrap(), 42.0);
});
}

Expand All @@ -213,11 +213,11 @@ mod tests {
Python::with_gil(|py| {
let array: [f32; 4] = [0.0, -16.0, 16.0, 42.0];
let pyobject = array.into_py(py);
let pylist: &PyList = pyobject.extract(py).unwrap();
assert_eq!(pylist[0].extract::<f32>().unwrap(), 0.0);
assert_eq!(pylist[1].extract::<f32>().unwrap(), -16.0);
assert_eq!(pylist[2].extract::<f32>().unwrap(), 16.0);
assert_eq!(pylist[3].extract::<f32>().unwrap(), 42.0);
let pylist = pyobject.downcast_bound::<PyList>(py).unwrap();
assert_eq!(pylist.get_item(0).unwrap().extract::<f32>().unwrap(), 0.0);
assert_eq!(pylist.get_item(1).unwrap().extract::<f32>().unwrap(), -16.0);
assert_eq!(pylist.get_item(2).unwrap().extract::<f32>().unwrap(), 16.0);
assert_eq!(pylist.get_item(3).unwrap().extract::<f32>().unwrap(), 42.0);
});
}

Expand Down
1 change: 1 addition & 0 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ impl<'py, T> Borrowed<'py, 'py, T>
where
T: HasPyGilRef,
{
#[cfg(feature = "gil-refs")]
pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
// Safety: self is a borrow over `'py`.
#[allow(deprecated)]
Expand Down
5 changes: 5 additions & 0 deletions src/internal_tricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub(crate) fn get_ssize_index(index: usize) -> Py_ssize_t {
}

/// Implementations used for slice indexing PySequence, PyTuple, and PyList
#[cfg(feature = "gil-refs")]
macro_rules! index_impls {
(
$ty:ty,
Expand Down Expand Up @@ -154,6 +155,7 @@ macro_rules! index_impls {
#[inline(never)]
#[cold]
#[track_caller]
#[cfg(feature = "gil-refs")]
pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
panic!(
"index {} out of range for {} of length {}",
Expand All @@ -164,6 +166,7 @@ pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
#[inline(never)]
#[cold]
#[track_caller]
#[cfg(feature = "gil-refs")]
pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
panic!(
"range start index {} out of range for {} of length {}",
Expand All @@ -174,6 +177,7 @@ pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize
#[inline(never)]
#[cold]
#[track_caller]
#[cfg(feature = "gil-refs")]
pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
panic!(
"range end index {} out of range for {} of length {}",
Expand All @@ -184,6 +188,7 @@ pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize)
#[inline(never)]
#[cold]
#[track_caller]
#[cfg(feature = "gil-refs")]
pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
panic!("slice index starts at {} but ends at {}", index, end);
}
Expand Down
11 changes: 6 additions & 5 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ macro_rules! py_run_impl {

/// Wraps a Rust function annotated with [`#[pyfunction]`](macro@crate::pyfunction).
///
/// This can be used with [`PyModule::add_function`](crate::types::PyModule::add_function) to add free
/// functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
/// This can be used with [`PyModule::add_function`](crate::types::PyModuleMethods::add_function) to
/// add free functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
/// information.
///
/// During the migration from the GIL Ref API to the Bound API, the return type of this macro will
Expand Down Expand Up @@ -157,8 +157,9 @@ macro_rules! wrap_pyfunction {

/// Wraps a Rust function annotated with [`#[pyfunction]`](macro@crate::pyfunction).
///
/// This can be used with [`PyModule::add_function`](crate::types::PyModule::add_function) to add free
/// functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more information.
/// This can be used with [`PyModule::add_function`](crate::types::PyModuleMethods::add_function) to
/// add free functions to a [`PyModule`](crate::types::PyModule) - see its documentation for more
/// information.
#[macro_export]
macro_rules! wrap_pyfunction_bound {
($function:path) => {
Expand All @@ -183,7 +184,7 @@ macro_rules! wrap_pyfunction_bound {
/// Python module.
///
/// Use this together with [`#[pymodule]`](crate::pymodule) and
/// [`PyModule::add_wrapped`](crate::types::PyModule::add_wrapped).
/// [`PyModule::add_wrapped`](crate::types::PyModuleMethods::add_wrapped).
#[macro_export]
macro_rules! wrap_pymodule {
($module:path) => {
Expand Down
10 changes: 7 additions & 3 deletions src/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,18 @@ mod inner {
}

impl<'py> CatchWarnings<'py> {
pub fn enter<R>(py: Python<'py>, f: impl FnOnce(&PyList) -> PyResult<R>) -> PyResult<R> {
pub fn enter<R>(
py: Python<'py>,
f: impl FnOnce(&Bound<'py, PyList>) -> PyResult<R>,
) -> PyResult<R> {
let warnings = py.import_bound("warnings")?;
let kwargs = [("record", true)].into_py_dict_bound(py);
let catch_warnings = warnings
.getattr("catch_warnings")?
.call((), Some(&kwargs))?;
let list = catch_warnings.call_method0("__enter__")?.extract()?;
let list = catch_warnings.call_method0("__enter__")?.downcast_into()?;
let _guard = Self { catch_warnings };
f(list)
f(&list)
}
}

Expand All @@ -139,6 +142,7 @@ mod inner {
macro_rules! assert_warnings {
($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{
$crate::tests::common::CatchWarnings::enter($py, |w| {
use $crate::types::{PyListMethods, PyStringMethods};
$body;
let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object_bound($py), $message)),+];
assert_eq!(w.len(), expected_warnings.len());
Expand Down
1 change: 1 addition & 0 deletions src/tests/hygiene/pymodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn foo(_py: crate::Python<'_>, _m: &crate::types::PyModule) -> crate::PyResult<(
::std::result::Result::Ok(())
}

#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
#[crate::pymodule]
#[pyo3(crate = "crate")]
Expand Down
1 change: 1 addition & 0 deletions src/types/bytearray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ impl<'a> Borrowed<'a, '_, PyByteArray> {
}
}

#[cfg(feature = "gil-refs")]
impl<'py> TryFrom<&'py PyAny> for &'py PyByteArray {
type Error = crate::PyErr;

Expand Down
3 changes: 3 additions & 0 deletions src/types/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mod not_limited_impls {
use super::*;
use std::ops::{Add, Div, Mul, Neg, Sub};

#[cfg(feature = "gil-refs")]
impl PyComplex {
/// Returns `|self|`.
pub fn abs(&self) -> c_double {
Expand Down Expand Up @@ -94,6 +95,7 @@ mod not_limited_impls {
}
}

#[cfg(feature = "gil-refs")]
impl<'py> $trait for &'py PyComplex {
type Output = &'py PyComplex;
fn $fn(self, other: &'py PyComplex) -> &'py PyComplex {
Expand Down Expand Up @@ -136,6 +138,7 @@ mod not_limited_impls {
bin_ops!(Mul, mul, *, ffi::_Py_c_prod);
bin_ops!(Div, div, /, ffi::_Py_c_quot);

#[cfg(feature = "gil-refs")]
impl<'py> Neg for &'py PyComplex {
type Output = &'py PyComplex;
fn neg(self) -> &'py PyComplex {
Expand Down
55 changes: 31 additions & 24 deletions src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::instance::{Borrowed, Bound};
use crate::py_result_ext::PyResultExt;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyList};
use crate::{ffi, PyNativeType, Python, ToPyObject};
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{ffi, Python, ToPyObject};

/// Represents a Python `dict`.
#[repr(transparent)]
Expand Down Expand Up @@ -56,34 +58,11 @@ pyobject_native_type_core!(
);

impl PyDict {
/// Deprecated form of [`new_bound`][PyDict::new_bound].
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDict::new` will be replaced by `PyDict::new_bound` in a future PyO3 version"
)]
#[inline]
pub fn new(py: Python<'_>) -> &PyDict {
Self::new_bound(py).into_gil_ref()
}

/// Creates a new empty dictionary.
pub fn new_bound(py: Python<'_>) -> Bound<'_, PyDict> {
unsafe { ffi::PyDict_New().assume_owned(py).downcast_into_unchecked() }
}

/// Deprecated form of [`from_sequence_bound`][PyDict::from_sequence_bound].
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyDict::from_sequence` will be replaced by `PyDict::from_sequence_bound` in a future PyO3 version"
)]
#[inline]
#[cfg(not(any(PyPy, GraalPy)))]
pub fn from_sequence(seq: &PyAny) -> PyResult<&PyDict> {
Self::from_sequence_bound(&seq.as_borrowed()).map(Bound::into_gil_ref)
}

/// Creates a new dictionary from the sequence given.
///
/// The sequence must consist of `(PyObject, PyObject)`. This is
Expand All @@ -100,6 +79,30 @@ impl PyDict {
})?;
Ok(dict)
}
}

#[cfg(feature = "gil-refs")]
impl PyDict {
/// Deprecated form of [`new_bound`][PyDict::new_bound].
#[deprecated(
since = "0.21.0",
note = "`PyDict::new` will be replaced by `PyDict::new_bound` in a future PyO3 version"
)]
#[inline]
pub fn new(py: Python<'_>) -> &PyDict {
Self::new_bound(py).into_gil_ref()
}

/// Deprecated form of [`from_sequence_bound`][PyDict::from_sequence_bound].
#[deprecated(
since = "0.21.0",
note = "`PyDict::from_sequence` will be replaced by `PyDict::from_sequence_bound` in a future PyO3 version"
)]
#[inline]
#[cfg(not(any(PyPy, GraalPy)))]
pub fn from_sequence(seq: &PyAny) -> PyResult<&PyDict> {
Self::from_sequence_bound(&seq.as_borrowed()).map(Bound::into_gil_ref)
}

/// Returns a new dictionary that contains the same key-value pairs as self.
///
Expand Down Expand Up @@ -550,8 +553,10 @@ fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
}

/// PyO3 implementation of an iterator for a Python `dict` object.
#[cfg(feature = "gil-refs")]
pub struct PyDictIterator<'py>(BoundDictIterator<'py>);

#[cfg(feature = "gil-refs")]
impl<'py> Iterator for PyDictIterator<'py> {
type Item = (&'py PyAny, &'py PyAny);

Expand All @@ -567,12 +572,14 @@ impl<'py> Iterator for PyDictIterator<'py> {
}
}

#[cfg(feature = "gil-refs")]
impl<'py> ExactSizeIterator for PyDictIterator<'py> {
fn len(&self) -> usize {
self.0.len()
}
}

#[cfg(feature = "gil-refs")]
impl<'a> IntoIterator for &'a PyDict {
type Item = (&'a PyAny, &'a PyAny);
type IntoIter = PyDictIterator<'a>;
Expand Down
53 changes: 30 additions & 23 deletions src/types/frozenset.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::types::PyIterator;
#[cfg(feature = "gil-refs")]
use crate::PyNativeType;
use crate::{
err::{self, PyErr, PyResult},
ffi,
ffi_ptr_ext::FfiPtrExt,
py_result_ext::PyResultExt,
types::any::PyAnyMethods,
Bound, PyAny, PyNativeType, PyObject, Python, ToPyObject,
Bound, PyAny, PyObject, Python, ToPyObject,
};
use std::ptr;

Expand Down Expand Up @@ -73,10 +75,32 @@ pyobject_native_type_core!(
#checkfunction=ffi::PyFrozenSet_Check
);

impl PyFrozenSet {
/// Creates a new frozenset.
///
/// May panic when running out of memory.
#[inline]
pub fn new_bound<'a, 'p, T: ToPyObject + 'a>(
py: Python<'p>,
elements: impl IntoIterator<Item = &'a T>,
) -> PyResult<Bound<'p, PyFrozenSet>> {
new_from_iter(py, elements)
}

/// Creates a new empty frozen set
pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
unsafe {
ffi::PyFrozenSet_New(ptr::null_mut())
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}
}

#[cfg(feature = "gil-refs")]
impl PyFrozenSet {
/// Deprecated form of [`PyFrozenSet::new_bound`].
#[inline]
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyFrozenSet::new` will be replaced by `PyFrozenSet::new_bound` in a future PyO3 version"
Expand All @@ -88,19 +112,7 @@ impl PyFrozenSet {
Self::new_bound(py, elements).map(Bound::into_gil_ref)
}

/// Creates a new frozenset.
///
/// May panic when running out of memory.
#[inline]
pub fn new_bound<'a, 'p, T: ToPyObject + 'a>(
py: Python<'p>,
elements: impl IntoIterator<Item = &'a T>,
) -> PyResult<Bound<'p, PyFrozenSet>> {
new_from_iter(py, elements)
}

/// Deprecated form of [`PyFrozenSet::empty_bound`].
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyFrozenSet::empty` will be replaced by `PyFrozenSet::empty_bound` in a future PyO3 version"
Expand All @@ -109,15 +121,6 @@ impl PyFrozenSet {
Self::empty_bound(py).map(Bound::into_gil_ref)
}

/// Creates a new empty frozen set
pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
unsafe {
ffi::PyFrozenSet_New(ptr::null_mut())
.assume_owned_or_err(py)
.downcast_into_unchecked()
}
}

/// Return the number of items in the set.
/// This is equivalent to len(p) on a set.
#[inline]
Expand Down Expand Up @@ -201,8 +204,10 @@ impl<'py> PyFrozenSetMethods<'py> for Bound<'py, PyFrozenSet> {
}

/// PyO3 implementation of an iterator for a Python `frozenset` object.
#[cfg(feature = "gil-refs")]
pub struct PyFrozenSetIterator<'py>(BoundFrozenSetIterator<'py>);

#[cfg(feature = "gil-refs")]
impl<'py> Iterator for PyFrozenSetIterator<'py> {
type Item = &'py super::PyAny;

Expand All @@ -217,13 +222,15 @@ impl<'py> Iterator for PyFrozenSetIterator<'py> {
}
}

#[cfg(feature = "gil-refs")]
impl ExactSizeIterator for PyFrozenSetIterator<'_> {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}

#[cfg(feature = "gil-refs")]
impl<'py> IntoIterator for &'py PyFrozenSet {
type Item = &'py PyAny;
type IntoIter = PyFrozenSetIterator<'py>;
Expand Down
Loading

0 comments on commit 635cb80

Please sign in to comment.