Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature gate APIs using into_gil_ref (Part 1) #4160

Merged
merged 1 commit into from
May 9, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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