From fed8bcadafe7bfc50c092b0ed84d55b403f46c88 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 30 Jan 2024 09:13:24 +0000 Subject: [PATCH 1/3] add remaining bound string constructors --- src/types/string.rs | 48 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/types/string.rs b/src/types/string.rs index fd20aab8c45..b1457950bb8 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -2,6 +2,7 @@ use crate::exceptions::PyUnicodeDecodeError; use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Borrowed; +use crate::py_result_ext::PyResultExt; use crate::types::any::PyAnyMethods; use crate::types::bytes::PyBytesMethods; use crate::types::PyBytes; @@ -159,6 +160,18 @@ impl PyString { } } + /// Deprecated form of [`PyString::intern_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyString::intern` will be replaced by `PyString::intern_bound` in a future PyO3 version" + ) + )] + pub fn intern<'py>(py: Python<'py>, s: &str) -> &'py Self { + Self::intern_bound(py, s).into_gil_ref() + } + /// Intern the given string /// /// This will return a reference to the same Python string object if called repeatedly with the same string. @@ -167,7 +180,7 @@ impl PyString { /// temporary Python string object and is thereby slower than [`PyString::new`]. /// /// Panics if out of memory. - pub fn intern<'p>(py: Python<'p>, s: &str) -> &'p PyString { + pub fn intern_bound<'py>(py: Python<'py>, s: &str) -> Bound<'py, PyString> { let ptr = s.as_ptr() as *const c_char; let len = s.len() as ffi::Py_ssize_t; unsafe { @@ -175,21 +188,38 @@ impl PyString { if !ob.is_null() { ffi::PyUnicode_InternInPlace(&mut ob); } - py.from_owned_ptr(ob) + ob.assume_owned(py).downcast_into_unchecked() } } + /// Deprecated form of [`PyString::from_object_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyString::from_object` will be replaced by `PyString::from_object_bound` in a future PyO3 version" + ) + )] + pub fn from_object<'py>(src: &'py PyAny, encoding: &str, errors: &str) -> PyResult<&'py Self> { + Self::from_object_bound(&src.as_borrowed(), encoding, errors).map(Bound::into_gil_ref) + } + /// Attempts to create a Python string from a Python [bytes-like object]. /// /// [bytes-like object]: (https://docs.python.org/3/glossary.html#term-bytes-like-object). - pub fn from_object<'p>(src: &'p PyAny, encoding: &str, errors: &str) -> PyResult<&'p PyString> { + pub fn from_object_bound<'py>( + src: &Bound<'py, PyAny>, + encoding: &str, + errors: &str, + ) -> PyResult> { unsafe { - src.py() - .from_owned_ptr_or_err(ffi::PyUnicode_FromEncodedObject( - src.as_ptr(), - encoding.as_ptr() as *const c_char, - errors.as_ptr() as *const c_char, - )) + ffi::PyUnicode_FromEncodedObject( + src.as_ptr(), + encoding.as_ptr() as *const c_char, + errors.as_ptr() as *const c_char, + ) + .assume_owned_or_err(src.py()) + .downcast_into_unchecked() } } From aa139ad422a629903d61139e72c78aed53de8cd3 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 30 Jan 2024 09:13:42 +0000 Subject: [PATCH 2/3] add `intern_bound!` macro --- guide/src/migration.md | 2 +- pyo3-benches/benches/bench_intern.rs | 4 +- pyo3-macros-backend/src/frompyobject.rs | 8 +-- pyo3-macros-backend/src/method.rs | 2 +- src/conversions/chrono.rs | 35 ++++++++----- src/conversions/chrono_tz.rs | 6 ++- src/conversions/num_bigint.rs | 14 ++--- src/conversions/num_complex.rs | 2 +- src/conversions/rust_decimal.rs | 8 +-- src/conversions/std/duration.rs | 9 ++-- src/conversions/std/ipaddr.rs | 6 ++- src/coroutine/waker.rs | 11 ++-- src/impl_/coroutine.rs | 10 +++- src/instance.rs | 30 +++++------ src/marker.rs | 2 +- src/sync.rs | 37 +++++++++---- src/tests/hygiene/misc.rs | 4 +- src/types/any.rs | 70 ++++++++++++------------- src/types/boolobject.rs | 2 +- src/types/mod.rs | 2 +- src/types/module.rs | 8 +-- src/types/traceback.rs | 6 +-- src/types/typeobject.rs | 8 +-- tests/ui/invalid_intern_arg.rs | 2 +- tests/ui/invalid_intern_arg.stderr | 10 ++-- 25 files changed, 171 insertions(+), 127 deletions(-) diff --git a/guide/src/migration.md b/guide/src/migration.md index 9fb5adba387..b3d577548dc 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -242,6 +242,7 @@ To minimise breakage of code using the GIL-Refs API, the `Bound` smart pointe For example, the following APIs have gained updated variants: - `PyList::new`, `PyTyple::new` and similar constructors have replacements `PyList::new_bound`, `PyTuple::new_bound` etc. - `FromPyObject::extract` has a new `FromPyObject::extract_bound` (see the section below) +- `pyo3::intern!` macro has a new replacement `pyo3::intern_bound!` Because the new `Bound` API brings ownership out of the PyO3 framework and into user code, there are a few places where user code is expected to need to adjust while switching to the new API: - Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count) @@ -274,7 +275,6 @@ impl<'py> FromPyObject<'py> for MyType { } ``` - The expectation is that in 0.22 `extract_bound` will have the default implementation removed and in 0.23 `extract` will be removed. ## from 0.19.* to 0.20 diff --git a/pyo3-benches/benches/bench_intern.rs b/pyo3-benches/benches/bench_intern.rs index d8dd1b8fd30..11e6395220c 100644 --- a/pyo3-benches/benches/bench_intern.rs +++ b/pyo3-benches/benches/bench_intern.rs @@ -2,7 +2,7 @@ use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criter use pyo3::prelude::*; -use pyo3::intern; +use pyo3::intern_bound; fn getattr_direct(b: &mut Bencher<'_>) { Python::with_gil(|py| { @@ -16,7 +16,7 @@ fn getattr_intern(b: &mut Bencher<'_>) { Python::with_gil(|py| { let sys = py.import("sys").unwrap(); - b.iter(|| sys.getattr(intern!(py, "version")).unwrap()); + b.iter(|| sys.getattr(intern_bound!(py, "version")).unwrap()); }); } diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 2b527dc8a29..7a825f7d492 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -324,17 +324,17 @@ impl<'a> Container<'a> { let field_name = ident.to_string(); let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) { FieldGetter::GetAttr(Some(name)) => { - quote!(getattr(_pyo3::intern!(obj.py(), #name))) + quote!(getattr(_pyo3::intern_bound!(obj.py(), #name))) } FieldGetter::GetAttr(None) => { - quote!(getattr(_pyo3::intern!(obj.py(), #field_name))) + quote!(getattr(_pyo3::intern_bound!(obj.py(), #field_name))) } FieldGetter::GetItem(Some(syn::Lit::Str(key))) => { - quote!(get_item(_pyo3::intern!(obj.py(), #key))) + quote!(get_item(_pyo3::intern_bound!(obj.py(), #key))) } FieldGetter::GetItem(Some(key)) => quote!(get_item(#key)), FieldGetter::GetItem(None) => { - quote!(get_item(_pyo3::intern!(obj.py(), #field_name))) + quote!(get_item(_pyo3::intern_bound!(obj.py(), #field_name))) } }; let extractor = match &field.from_py_with { diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 7050be23d5c..e0d61672982 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -522,7 +522,7 @@ impl<'a> FnSpec<'a> { let mut call = quote! {{ let future = #future; _pyo3::impl_::coroutine::new_coroutine( - _pyo3::intern!(py, stringify!(#python_name)), + _pyo3::intern_bound!(py, stringify!(#python_name)), #qualname_prefix, #throw_callback, async move { _pyo3::impl_::wrap::OkWrap::wrap(future.await) }, diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 0e95375b5bd..7b74c596472 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -52,7 +52,7 @@ use crate::types::{ PyTzInfo, PyTzInfoAccess, }; #[cfg(Py_LIMITED_API)] -use crate::{intern, PyDowncastError}; +use crate::{intern_bound, PyDowncastError}; use crate::{ FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject, }; @@ -127,9 +127,10 @@ impl FromPyObject<'_> for Duration { let (days, seconds, microseconds) = { check_type(ob, &DatetimeTypes::get(ob.py()).timedelta, "PyDelta")?; ( - ob.getattr(intern!(ob.py(), "days"))?.extract()?, - ob.getattr(intern!(ob.py(), "seconds"))?.extract()?, - ob.getattr(intern!(ob.py(), "microseconds"))?.extract()?, + ob.getattr(intern_bound!(ob.py(), "days"))?.extract()?, + ob.getattr(intern_bound!(ob.py(), "seconds"))?.extract()?, + ob.getattr(intern_bound!(ob.py(), "microseconds"))? + .extract()?, ) }; Ok( @@ -250,7 +251,7 @@ impl FromPyObject<'_> for NaiveDateTime { #[cfg(not(Py_LIMITED_API))] let has_tzinfo = dt.get_tzinfo_bound().is_some(); #[cfg(Py_LIMITED_API)] - let has_tzinfo = !dt.getattr(intern!(dt.py(), "tzinfo"))?.is_none(); + let has_tzinfo = !dt.getattr(intern_bound!(dt.py(), "tzinfo"))?.is_none(); if has_tzinfo { return Err(PyTypeError::new_err("expected a datetime without tzinfo")); } @@ -286,7 +287,7 @@ impl FromPyObject<'a>> FromPyObject<'_> for DateTime #[cfg(not(Py_LIMITED_API))] let tzinfo = dt.get_tzinfo_bound(); #[cfg(Py_LIMITED_API)] - let tzinfo: Option<&PyAny> = dt.getattr(intern!(dt.py(), "tzinfo"))?.extract()?; + let tzinfo: Option<&PyAny> = dt.getattr(intern_bound!(dt.py(), "tzinfo"))?.extract()?; let tz = if let Some(tzinfo) = tzinfo { tzinfo.extract()? @@ -482,9 +483,15 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult { #[cfg(Py_LIMITED_API)] fn py_date_to_naive_date(py_date: &PyAny) -> PyResult { NaiveDate::from_ymd_opt( - py_date.getattr(intern!(py_date.py(), "year"))?.extract()?, - py_date.getattr(intern!(py_date.py(), "month"))?.extract()?, - py_date.getattr(intern!(py_date.py(), "day"))?.extract()?, + py_date + .getattr(intern_bound!(py_date.py(), "year"))? + .extract()?, + py_date + .getattr(intern_bound!(py_date.py(), "month"))? + .extract()?, + py_date + .getattr(intern_bound!(py_date.py(), "day"))? + .extract()?, ) .ok_or_else(|| PyValueError::new_err("invalid or out-of-range date")) } @@ -503,15 +510,17 @@ fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult { #[cfg(Py_LIMITED_API)] fn py_time_to_naive_time(py_time: &PyAny) -> PyResult { NaiveTime::from_hms_micro_opt( - py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?, py_time - .getattr(intern!(py_time.py(), "minute"))? + .getattr(intern_bound!(py_time.py(), "hour"))? + .extract()?, + py_time + .getattr(intern_bound!(py_time.py(), "minute"))? .extract()?, py_time - .getattr(intern!(py_time.py(), "second"))? + .getattr(intern_bound!(py_time.py(), "second"))? .extract()?, py_time - .getattr(intern!(py_time.py(), "microsecond"))? + .getattr(intern_bound!(py_time.py(), "microsecond"))? .extract()?, ) .ok_or_else(|| PyValueError::new_err("invalid or out-of-range time")) diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index 8740d0bdd98..980ac03caff 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -36,7 +36,9 @@ use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; +use crate::{ + intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, +}; use chrono_tz::Tz; use std::str::FromStr; @@ -60,7 +62,7 @@ impl IntoPy for Tz { impl FromPyObject<'_> for Tz { fn extract(ob: &PyAny) -> PyResult { - Tz::from_str(ob.getattr(intern!(ob.py(), "key"))?.extract()?) + Tz::from_str(ob.getattr(intern_bound!(ob.py(), "key"))?.extract()?) .map_err(|e| PyValueError::new_err(e.to_string())) } } diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index 5cc2157d446..b7d2f11c036 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -81,7 +81,9 @@ macro_rules! bigint_conversion { let bytes_obj = PyBytes::new(py, &bytes); let kwargs = if $is_signed > 0 { let kwargs = PyDict::new(py); - kwargs.set_item(crate::intern!(py, "signed"), true).unwrap(); + kwargs + .set_item(crate::intern_bound!(py, "signed"), true) + .unwrap(); Some(kwargs) } else { None @@ -208,18 +210,18 @@ fn int_to_u32_vec(long: &PyLong, n_digits: usize, is_signed: bool) -> PyResult PyResult<&PyBytes> { - use crate::intern; + use crate::intern_bound; let py = long.py(); let kwargs = if is_signed { let kwargs = PyDict::new(py); - kwargs.set_item(intern!(py, "signed"), true)?; + kwargs.set_item(intern_bound!(py, "signed"), true)?; Some(kwargs) } else { None }; let bytes = long.call_method( - intern!(py, "to_bytes"), - (n_bytes, intern!(py, "little")), + intern_bound!(py, "to_bytes"), + (n_bytes, intern_bound!(py, "little")), kwargs, )?; Ok(bytes.downcast()?) @@ -241,7 +243,7 @@ fn int_n_bits(long: &PyLong) -> PyResult { #[cfg(Py_LIMITED_API)] { // slow path - long.call_method0(crate::intern!(py, "bit_length")) + long.call_method0(crate::intern_bound!(py, "bit_length")) .and_then(PyAny::extract) } } diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index ba741323611..ad8ea27397d 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -153,7 +153,7 @@ macro_rules! complex_conversion { let obj = if obj.is_instance_of::() { obj } else if let Some(method) = - obj.lookup_special(crate::intern!(obj.py(), "__complex__"))? + obj.lookup_special(crate::intern_bound!(obj.py(), "__complex__"))? { complex = method.call0()?; &complex diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index 173e57851c9..b42488e9f11 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -52,7 +52,9 @@ use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; +use crate::{ + intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, +}; use rust_decimal::Decimal; use std::str::FromStr; @@ -73,8 +75,8 @@ static DECIMAL_CLS: GILOnceCell> = GILOnceCell::new(); fn get_decimal_cls(py: Python<'_>) -> PyResult<&PyType> { DECIMAL_CLS .get_or_try_init(py, || { - py.import(intern!(py, "decimal"))? - .getattr(intern!(py, "Decimal"))? + py.import(intern_bound!(py, "decimal"))? + .getattr(intern_bound!(py, "Decimal"))? .extract() }) .map(|ty| ty.as_ref(py)) diff --git a/src/conversions/std/duration.rs b/src/conversions/std/duration.rs index e4540bd0aaa..24f183a65ef 100755 --- a/src/conversions/std/duration.rs +++ b/src/conversions/std/duration.rs @@ -6,7 +6,7 @@ use crate::types::PyType; #[cfg(not(Py_LIMITED_API))] use crate::types::{PyDelta, PyDeltaAccess}; #[cfg(Py_LIMITED_API)] -use crate::{intern, Py}; +use crate::{intern_bound, Py}; use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject}; use std::time::Duration; @@ -26,9 +26,10 @@ impl FromPyObject<'_> for Duration { #[cfg(Py_LIMITED_API)] let (days, seconds, microseconds): (i32, i32, i32) = { ( - obj.getattr(intern!(obj.py(), "days"))?.extract()?, - obj.getattr(intern!(obj.py(), "seconds"))?.extract()?, - obj.getattr(intern!(obj.py(), "microseconds"))?.extract()?, + obj.getattr(intern_bound!(obj.py(), "days"))?.extract()?, + obj.getattr(intern_bound!(obj.py(), "seconds"))?.extract()?, + obj.getattr(intern_bound!(obj.py(), "microseconds"))? + .extract()?, ) }; diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 713de0afd1a..d3cbda2c216 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -3,11 +3,13 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; +use crate::{ + intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, +}; impl FromPyObject<'_> for IpAddr { fn extract(obj: &PyAny) -> PyResult { - match obj.getattr(intern!(obj.py(), "packed")) { + match obj.getattr(intern_bound!(obj.py(), "packed")) { Ok(packed) => { if let Ok(packed) = packed.extract::<[u8; 4]>() { Ok(IpAddr::V4(Ipv4Addr::from(packed))) diff --git a/src/coroutine/waker.rs b/src/coroutine/waker.rs index 8a1166ce3fb..d4a63d35669 100644 --- a/src/coroutine/waker.rs +++ b/src/coroutine/waker.rs @@ -1,6 +1,6 @@ use crate::sync::GILOnceCell; use crate::types::PyCFunction; -use crate::{intern, wrap_pyfunction, Py, PyAny, PyObject, PyResult, Python}; +use crate::{intern_bound, wrap_pyfunction, Py, PyAny, PyObject, PyResult, Python}; use pyo3_macros::pyfunction; use std::sync::Arc; use std::task::Wake; @@ -72,7 +72,7 @@ impl LoopAndFuture { // so it requires `call_soon_threadsafe` let call_soon_threadsafe = self.event_loop.call_method1( py, - intern!(py, "call_soon_threadsafe"), + intern_bound!(py, "call_soon_threadsafe"), (release_waiter, self.future.as_ref(py)), ); if let Err(err) = call_soon_threadsafe { @@ -93,9 +93,12 @@ impl LoopAndFuture { /// See #[pyfunction(crate = "crate")] fn release_waiter(future: &PyAny) -> PyResult<()> { - let done = future.call_method0(intern!(future.py(), "done"))?; + let done = future.call_method0(intern_bound!(future.py(), "done"))?; if !done.extract::()? { - future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?; + future.call_method1( + intern_bound!(future.py(), "set_result"), + (future.py().None(),), + )?; } Ok(()) } diff --git a/src/impl_/coroutine.rs b/src/impl_/coroutine.rs index 32f3e94ad8a..c9ca4873c6c 100644 --- a/src/impl_/coroutine.rs +++ b/src/impl_/coroutine.rs @@ -6,13 +6,14 @@ use std::{ use crate::{ coroutine::{cancel::ThrowCallback, Coroutine}, + instance::Bound, pyclass::boolean_struct::False, types::PyString, IntoPy, Py, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult, Python, }; pub fn new_coroutine( - name: &PyString, + name: &Bound<'_, PyString>, qualname_prefix: Option<&'static str>, throw_callback: Option, future: F, @@ -22,7 +23,12 @@ where T: IntoPy, E: Into, { - Coroutine::new(Some(name.into()), qualname_prefix, throw_callback, future) + Coroutine::new( + Some(name.clone().into()), + qualname_prefix, + throw_callback, + future, + ) } fn get_ptr(obj: &Py) -> *mut T { diff --git a/src/instance.rs b/src/instance.rs index c1ab45926b3..ff51cb30293 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -66,7 +66,7 @@ impl<'py> Bound<'py, PyAny> { /// /// # Safety /// - /// `ptr`` must be a valid pointer to a Python object. + /// `ptr` must be a valid pointer to a Python object. pub(crate) unsafe fn from_owned_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self { Self(py, ManuallyDrop::new(Py::from_owned_ptr(py, ptr))) } @@ -75,7 +75,7 @@ impl<'py> Bound<'py, PyAny> { /// /// # Safety /// - /// `ptr`` must be a valid pointer to a Python object, or NULL. + /// `ptr` must be a valid pointer to a Python object, or NULL. pub(crate) unsafe fn from_owned_ptr_or_opt( py: Python<'py>, ptr: *mut ffi::PyObject, @@ -971,18 +971,18 @@ impl Py { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// If calling this method becomes performance-critical, the [`intern!`](crate::intern) macro + /// If calling this method becomes performance-critical, the [`intern_bound!`](crate::intern_bound) macro /// can be used to intern `attr_name`, thereby avoiding repeated temporary allocations of /// Python strings. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPy, Py, Python, PyObject, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, IntoPy, Py, Python, PyObject, PyResult}; /// # /// #[pyfunction] /// fn version(sys: Py, py: Python<'_>) -> PyResult { - /// sys.getattr(py, intern!(py, "version")) + /// sys.getattr(py, intern_bound!(py, "version")) /// } /// # /// # Python::with_gil(|py| { @@ -1001,17 +1001,17 @@ impl Py { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) /// macro can be used to intern `attr_name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPy, PyObject, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, IntoPy, PyObject, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: PyObject, py: Python<'_>) -> PyResult<()> { - /// ob.setattr(py, intern!(py, "answer"), 42) + /// ob.setattr(py, intern_bound!(py, "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -1098,7 +1098,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) /// macro can be used to intern `name`. pub fn call_method_bound( &self, @@ -1121,7 +1121,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) /// macro can be used to intern `name`. pub fn call_method1(&self, py: Python<'_>, name: N, args: A) -> PyResult where @@ -1138,7 +1138,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) /// macro can be used to intern `name`. pub fn call_method0(&self, py: Python<'_>, name: N) -> PyResult where @@ -1644,8 +1644,8 @@ a = A() let module = PyModule::from_code(py, CODE, "", "")?; let instance: Py = module.getattr("a")?.into(); - let foo = crate::intern!(py, "foo"); - let bar = crate::intern!(py, "bar"); + let foo = crate::intern_bound!(py, "foo"); + let bar = crate::intern_bound!(py, "bar"); instance.getattr(py, foo).unwrap_err(); instance.setattr(py, foo, bar)?; diff --git a/src/marker.rs b/src/marker.rs index 836d0109eeb..6a298db6a9a 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -651,7 +651,7 @@ impl<'py> Python<'py> { // See also: // - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10) // - https://github.com/PyO3/pyo3/issues/3370 - let builtins_s = crate::intern!(self, "__builtins__").as_ptr(); + let builtins_s = crate::intern_bound!(self, "__builtins__").as_ptr(); let has_builtins = ffi::PyDict_Contains(globals, builtins_s); if has_builtins == -1 { return Err(PyErr::fetch(self)); diff --git a/src/sync.rs b/src/sync.rs index e747269ae70..36356c4fe5b 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,5 +1,5 @@ //! Synchronization mechanisms based on the Python GIL. -use crate::{types::PyString, types::PyType, Py, PyResult, PyVisit, Python}; +use crate::{instance::Bound, types::PyString, types::PyType, Py, PyResult, PyVisit, Python}; use std::cell::UnsafeCell; /// Value with concurrent access protected by the GIL. @@ -202,14 +202,29 @@ impl GILOnceCell> { } } +/// Deprecated form of [intern_bound!][crate::intern_bound]. +#[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`intern_bound!` will be replaced by `intern_bound!` in a future PyO3 version" + ) +)] +#[macro_export] +macro_rules! intern { + ($py: expr, $text: expr) => { + $crate::intern_bound!($py, $text).as_gil_ref() + }; +} + /// Interns `text` as a Python string and stores a reference to it in static storage. /// /// A reference to the same Python string is returned on each invocation. /// -/// # Example: Using `intern!` to avoid needlessly recreating the same Python string +/// # Example: Using `intern_bound!` to avoid needlessly recreating the same Python string /// /// ``` -/// use pyo3::intern; +/// use pyo3::intern_bound; /// # use pyo3::{pyfunction, types::PyDict, wrap_pyfunction, PyResult, Python}; /// /// #[pyfunction] @@ -226,7 +241,7 @@ impl GILOnceCell> { /// let dict = PyDict::new(py); /// // 👇 A `PyString` is created once and reused /// // for the lifetime of the program. -/// dict.set_item(intern!(py, "foo"), 42)?; +/// dict.set_item(intern_bound!(py, "foo"), 42)?; /// Ok(dict) /// } /// # @@ -240,14 +255,14 @@ impl GILOnceCell> { /// # }); /// ``` #[macro_export] -macro_rules! intern { +macro_rules! intern_bound { ($py: expr, $text: expr) => {{ static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); INTERNED.get($py) }}; } -/// Implementation detail for `intern!` macro. +/// Implementation detail for `intern_bound!` macro. #[doc(hidden)] pub struct Interned(&'static str, GILOnceCell>); @@ -259,10 +274,10 @@ impl Interned { /// Gets or creates the interned `str` value. #[inline] - pub fn get<'py>(&'py self, py: Python<'py>) -> &'py PyString { + pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> { self.1 - .get_or_init(py, || PyString::intern(py, self.0).into()) - .as_ref(py) + .get_or_init(py, || PyString::intern_bound(py, self.0).into()) + .bind(py) } } @@ -276,8 +291,8 @@ mod tests { fn test_intern() { Python::with_gil(|py| { let foo1 = "foo"; - let foo2 = intern!(py, "foo"); - let foo3 = intern!(py, stringify!(foo)); + let foo2 = intern_bound!(py, "foo"); + let foo3 = intern_bound!(py, stringify!(foo)); let dict = PyDict::new(py); dict.set_item(foo1, 42_usize).unwrap(); diff --git a/src/tests/hygiene/misc.rs b/src/tests/hygiene/misc.rs index 66db7f3a28a..3008b5a3b9e 100644 --- a/src/tests/hygiene/misc.rs +++ b/src/tests/hygiene/misc.rs @@ -30,8 +30,8 @@ crate::import_exception!(socket, gaierror); #[allow(dead_code)] fn intern(py: crate::Python<'_>) { - let _foo = crate::intern!(py, "foo"); - let _bar = crate::intern!(py, stringify!(bar)); + let _foo = crate::intern_bound!(py, "foo"); + let _bar = crate::intern_bound!(py, stringify!(bar)); } #[allow(dead_code)] diff --git a/src/types/any.rs b/src/types/any.rs index 14102d80a8d..aec978f7fb7 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -80,17 +80,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, Python, PyResult}; /// # /// #[pyfunction] /// fn has_version(sys: &PyModule) -> PyResult { - /// sys.hasattr(intern!(sys.py(), "version")) + /// sys.hasattr(intern_bound!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -109,17 +109,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn version(sys: &PyModule) -> PyResult<&PyAny> { - /// sys.getattr(intern!(sys.py(), "version")) + /// sys.getattr(intern_bound!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -140,17 +140,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: &PyAny) -> PyResult<()> { - /// ob.setattr(intern!(ob.py(), "answer"), 42) + /// ob.setattr(intern_bound!(ob.py(), "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -170,7 +170,7 @@ impl PyAny { /// /// This is equivalent to the Python statement `del self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. pub fn delattr(&self, attr_name: N) -> PyResult<()> where @@ -465,7 +465,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -510,7 +510,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -550,7 +550,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -950,17 +950,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, Python, PyResult}; /// # /// #[pyfunction] /// fn has_version(sys: &PyModule) -> PyResult { - /// sys.hasattr(intern!(sys.py(), "version")) + /// sys.hasattr(intern_bound!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -976,17 +976,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn version(sys: &PyModule) -> PyResult<&PyAny> { - /// sys.getattr(intern!(sys.py(), "version")) + /// sys.getattr(intern_bound!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -1002,17 +1002,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// - /// # Example: `intern!`ing the attribute name + /// # Example: `intern_bound!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: &PyAny) -> PyResult<()> { - /// ob.setattr(intern!(ob.py(), "answer"), 42) + /// ob.setattr(intern_bound!(ob.py(), "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -1029,7 +1029,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python statement `del self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. fn delattr(&self, attr_name: N) -> PyResult<()> where @@ -1337,7 +1337,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -1382,7 +1382,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -1417,7 +1417,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `name`. /// /// # Examples @@ -2257,7 +2257,7 @@ impl<'py> Bound<'py, PyAny> { /// typically a direct error for the special lookup to fail, as magic methods are optional in /// many situations in which they might be called. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used /// to intern `attr_name`. #[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that. pub(crate) fn lookup_special(&self, attr_name: N) -> PyResult>> @@ -2290,7 +2290,7 @@ impl<'py> Bound<'py, PyAny> { } else if let Ok(descr_get) = attr .get_type() .as_borrowed() - .getattr(crate::intern!(py, "__get__")) + .getattr(crate::intern_bound!(py, "__get__")) { descr_get.call1((attr, self, self_type)).map(Some) } else { @@ -2349,7 +2349,7 @@ class NonHeapNonDescriptorInt: ) .unwrap(); - let int = crate::intern!(py, "__int__"); + let int = crate::intern_bound!(py, "__int__"); let eval_int = |obj: &PyAny| { obj.as_borrowed() .lookup_special(int)? diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index 0decc0b4c80..b3384f8830b 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -116,7 +116,7 @@ impl FromPyObject<'_> for bool { #[cfg(any(Py_LIMITED_API, PyPy))] { let meth = obj - .lookup_special(crate::intern!(obj.py(), "__bool__"))? + .lookup_special(crate::intern_bound!(obj.py(), "__bool__"))? .ok_or_else(|| missing_conversion(obj))?; let obj = meth.call0()?.downcast_into::()?; diff --git a/src/types/mod.rs b/src/types/mod.rs index e77526a2520..5f88d57ce4d 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -290,7 +290,7 @@ pub(crate) mod float; mod frame; pub(crate) mod frozenset; mod function; -mod iterator; +pub(crate) mod iterator; pub(crate) mod list; pub(crate) mod mapping; mod memoryview; diff --git a/src/types/module.rs b/src/types/module.rs index 416090a187b..a44fe383f5e 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -660,12 +660,12 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { } } -fn __all__(py: Python<'_>) -> &PyString { - intern!(py, "__all__") +fn __all__(py: Python<'_>) -> &Bound<'_, PyString> { + intern_bound!(py, "__all__") } -fn __name__(py: Python<'_>) -> &PyString { - intern!(py, "__name__") +fn __name__(py: Python<'_>) -> &Bound<'_, PyString> { + intern_bound!(py, "__name__") } #[cfg(test)] diff --git a/src/types/traceback.rs b/src/types/traceback.rs index 84ecda747eb..3dd9e150e31 100644 --- a/src/types/traceback.rs +++ b/src/types/traceback.rs @@ -95,13 +95,13 @@ impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> { fn format(&self) -> PyResult { let py = self.py(); let string_io = py - .import(intern!(py, "io"))? - .getattr(intern!(py, "StringIO"))? + .import(intern_bound!(py, "io"))? + .getattr(intern_bound!(py, "StringIO"))? .call0()?; let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) }; error_on_minusone(py, result)?; let formatted = string_io - .getattr(intern!(py, "getvalue"))? + .getattr(intern_bound!(py, "getvalue"))? .call0()? .downcast::()? .to_str()? diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index a1a053f93a4..b37f8ebe564 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -36,7 +36,9 @@ impl PyType { /// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`. pub fn qualname(&self) -> PyResult { #[cfg(any(Py_LIMITED_API, PyPy, not(Py_3_11)))] - let name = self.getattr(intern!(self.py(), "__qualname__"))?.extract(); + let name = self + .getattr(intern_bound!(self.py(), "__qualname__"))? + .extract(); #[cfg(not(any(Py_LIMITED_API, PyPy, not(Py_3_11))))] let name = { @@ -71,10 +73,10 @@ impl PyType { #[cfg(any(Py_LIMITED_API, PyPy))] { - let module = self.getattr(intern!(self.py(), "__module__"))?; + let module = self.getattr(intern_bound!(self.py(), "__module__"))?; #[cfg(not(Py_3_11))] - let name = self.getattr(intern!(self.py(), "__name__"))?; + let name = self.getattr(intern_bound!(self.py(), "__name__"))?; #[cfg(Py_3_11)] let name = { diff --git a/tests/ui/invalid_intern_arg.rs b/tests/ui/invalid_intern_arg.rs index fa9e1e59f0c..7bc29ddc65b 100644 --- a/tests/ui/invalid_intern_arg.rs +++ b/tests/ui/invalid_intern_arg.rs @@ -2,5 +2,5 @@ use pyo3::Python; fn main() { let foo = if true { "foo" } else { "bar" }; - Python::with_gil(|py| py.import(pyo3::intern!(py, foo)).unwrap()); + Python::with_gil(|py| py.import(pyo3::intern_bound!(py, foo)).unwrap()); } diff --git a/tests/ui/invalid_intern_arg.stderr b/tests/ui/invalid_intern_arg.stderr index bb84d00e15b..5d03cadf6ed 100644 --- a/tests/ui/invalid_intern_arg.stderr +++ b/tests/ui/invalid_intern_arg.stderr @@ -1,8 +1,8 @@ error[E0435]: attempt to use a non-constant value in a constant - --> tests/ui/invalid_intern_arg.rs:5:55 + --> tests/ui/invalid_intern_arg.rs:5:61 | -5 | Python::with_gil(|py| py.import(pyo3::intern!(py, foo)).unwrap()); - | ------------------^^^- - | | | - | | non-constant value +5 | Python::with_gil(|py| py.import(pyo3::intern_bound!(py, foo)).unwrap()); + | ------------------------^^^- + | | | + | | non-constant value | help: consider using `let` instead of `static`: `let INTERNED` From 2f00eb1423505698746a00024469f9a64bc2feb2 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Tue, 30 Jan 2024 13:28:07 +0000 Subject: [PATCH 3/3] for now just change return type of `intern!` --- guide/src/migration.md | 4 +- pyo3-benches/benches/bench_intern.rs | 4 +- pyo3-macros-backend/src/frompyobject.rs | 8 +-- pyo3-macros-backend/src/method.rs | 2 +- src/conversions/chrono.rs | 35 +++++-------- src/conversions/chrono_tz.rs | 6 +-- src/conversions/num_bigint.rs | 14 +++-- src/conversions/num_complex.rs | 2 +- src/conversions/rust_decimal.rs | 8 ++- src/conversions/std/duration.rs | 9 ++-- src/conversions/std/ipaddr.rs | 6 +-- src/coroutine/waker.rs | 11 ++-- src/instance.rs | 26 ++++----- src/marker.rs | 2 +- src/sync.rs | 29 +++------- src/tests/hygiene/misc.rs | 4 +- src/types/any.rs | 70 ++++++++++++------------- src/types/boolobject.rs | 2 +- src/types/module.rs | 4 +- src/types/traceback.rs | 6 +-- src/types/typeobject.rs | 8 ++- tests/ui/invalid_intern_arg.rs | 2 +- tests/ui/invalid_intern_arg.stderr | 10 ++-- 23 files changed, 117 insertions(+), 155 deletions(-) diff --git a/guide/src/migration.md b/guide/src/migration.md index b3d577548dc..2eb4d3d6117 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -22,6 +22,8 @@ The following sections are laid out in this order. To make the transition for the PyO3 ecosystem away from the GIL Refs API as smooth as possible, in PyO3 0.21 no APIs consuming or producing GIL Refs have been altered. Instead, variants using `Bound` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound` 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. +> 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` 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. Before: @@ -40,7 +42,6 @@ After: pyo3 = { version = "0.21", features = ["gil-refs"] } ``` - ### `PyTypeInfo` and `PyTryFrom` have been adjusted The `PyTryFrom` trait has aged poorly, its [`try_from`] method now conflicts with `try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`. @@ -242,7 +243,6 @@ To minimise breakage of code using the GIL-Refs API, the `Bound` smart pointe For example, the following APIs have gained updated variants: - `PyList::new`, `PyTyple::new` and similar constructors have replacements `PyList::new_bound`, `PyTuple::new_bound` etc. - `FromPyObject::extract` has a new `FromPyObject::extract_bound` (see the section below) -- `pyo3::intern!` macro has a new replacement `pyo3::intern_bound!` Because the new `Bound` API brings ownership out of the PyO3 framework and into user code, there are a few places where user code is expected to need to adjust while switching to the new API: - Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count) diff --git a/pyo3-benches/benches/bench_intern.rs b/pyo3-benches/benches/bench_intern.rs index 11e6395220c..d8dd1b8fd30 100644 --- a/pyo3-benches/benches/bench_intern.rs +++ b/pyo3-benches/benches/bench_intern.rs @@ -2,7 +2,7 @@ use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criter use pyo3::prelude::*; -use pyo3::intern_bound; +use pyo3::intern; fn getattr_direct(b: &mut Bencher<'_>) { Python::with_gil(|py| { @@ -16,7 +16,7 @@ fn getattr_intern(b: &mut Bencher<'_>) { Python::with_gil(|py| { let sys = py.import("sys").unwrap(); - b.iter(|| sys.getattr(intern_bound!(py, "version")).unwrap()); + b.iter(|| sys.getattr(intern!(py, "version")).unwrap()); }); } diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 7a825f7d492..2b527dc8a29 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -324,17 +324,17 @@ impl<'a> Container<'a> { let field_name = ident.to_string(); let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) { FieldGetter::GetAttr(Some(name)) => { - quote!(getattr(_pyo3::intern_bound!(obj.py(), #name))) + quote!(getattr(_pyo3::intern!(obj.py(), #name))) } FieldGetter::GetAttr(None) => { - quote!(getattr(_pyo3::intern_bound!(obj.py(), #field_name))) + quote!(getattr(_pyo3::intern!(obj.py(), #field_name))) } FieldGetter::GetItem(Some(syn::Lit::Str(key))) => { - quote!(get_item(_pyo3::intern_bound!(obj.py(), #key))) + quote!(get_item(_pyo3::intern!(obj.py(), #key))) } FieldGetter::GetItem(Some(key)) => quote!(get_item(#key)), FieldGetter::GetItem(None) => { - quote!(get_item(_pyo3::intern_bound!(obj.py(), #field_name))) + quote!(get_item(_pyo3::intern!(obj.py(), #field_name))) } }; let extractor = match &field.from_py_with { diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index e0d61672982..7050be23d5c 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -522,7 +522,7 @@ impl<'a> FnSpec<'a> { let mut call = quote! {{ let future = #future; _pyo3::impl_::coroutine::new_coroutine( - _pyo3::intern_bound!(py, stringify!(#python_name)), + _pyo3::intern!(py, stringify!(#python_name)), #qualname_prefix, #throw_callback, async move { _pyo3::impl_::wrap::OkWrap::wrap(future.await) }, diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 7b74c596472..0e95375b5bd 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -52,7 +52,7 @@ use crate::types::{ PyTzInfo, PyTzInfoAccess, }; #[cfg(Py_LIMITED_API)] -use crate::{intern_bound, PyDowncastError}; +use crate::{intern, PyDowncastError}; use crate::{ FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject, }; @@ -127,10 +127,9 @@ impl FromPyObject<'_> for Duration { let (days, seconds, microseconds) = { check_type(ob, &DatetimeTypes::get(ob.py()).timedelta, "PyDelta")?; ( - ob.getattr(intern_bound!(ob.py(), "days"))?.extract()?, - ob.getattr(intern_bound!(ob.py(), "seconds"))?.extract()?, - ob.getattr(intern_bound!(ob.py(), "microseconds"))? - .extract()?, + ob.getattr(intern!(ob.py(), "days"))?.extract()?, + ob.getattr(intern!(ob.py(), "seconds"))?.extract()?, + ob.getattr(intern!(ob.py(), "microseconds"))?.extract()?, ) }; Ok( @@ -251,7 +250,7 @@ impl FromPyObject<'_> for NaiveDateTime { #[cfg(not(Py_LIMITED_API))] let has_tzinfo = dt.get_tzinfo_bound().is_some(); #[cfg(Py_LIMITED_API)] - let has_tzinfo = !dt.getattr(intern_bound!(dt.py(), "tzinfo"))?.is_none(); + let has_tzinfo = !dt.getattr(intern!(dt.py(), "tzinfo"))?.is_none(); if has_tzinfo { return Err(PyTypeError::new_err("expected a datetime without tzinfo")); } @@ -287,7 +286,7 @@ impl FromPyObject<'a>> FromPyObject<'_> for DateTime #[cfg(not(Py_LIMITED_API))] let tzinfo = dt.get_tzinfo_bound(); #[cfg(Py_LIMITED_API)] - let tzinfo: Option<&PyAny> = dt.getattr(intern_bound!(dt.py(), "tzinfo"))?.extract()?; + let tzinfo: Option<&PyAny> = dt.getattr(intern!(dt.py(), "tzinfo"))?.extract()?; let tz = if let Some(tzinfo) = tzinfo { tzinfo.extract()? @@ -483,15 +482,9 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult { #[cfg(Py_LIMITED_API)] fn py_date_to_naive_date(py_date: &PyAny) -> PyResult { NaiveDate::from_ymd_opt( - py_date - .getattr(intern_bound!(py_date.py(), "year"))? - .extract()?, - py_date - .getattr(intern_bound!(py_date.py(), "month"))? - .extract()?, - py_date - .getattr(intern_bound!(py_date.py(), "day"))? - .extract()?, + py_date.getattr(intern!(py_date.py(), "year"))?.extract()?, + py_date.getattr(intern!(py_date.py(), "month"))?.extract()?, + py_date.getattr(intern!(py_date.py(), "day"))?.extract()?, ) .ok_or_else(|| PyValueError::new_err("invalid or out-of-range date")) } @@ -510,17 +503,15 @@ fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult { #[cfg(Py_LIMITED_API)] fn py_time_to_naive_time(py_time: &PyAny) -> PyResult { NaiveTime::from_hms_micro_opt( + py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?, py_time - .getattr(intern_bound!(py_time.py(), "hour"))? - .extract()?, - py_time - .getattr(intern_bound!(py_time.py(), "minute"))? + .getattr(intern!(py_time.py(), "minute"))? .extract()?, py_time - .getattr(intern_bound!(py_time.py(), "second"))? + .getattr(intern!(py_time.py(), "second"))? .extract()?, py_time - .getattr(intern_bound!(py_time.py(), "microsecond"))? + .getattr(intern!(py_time.py(), "microsecond"))? .extract()?, ) .ok_or_else(|| PyValueError::new_err("invalid or out-of-range time")) diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index 980ac03caff..8740d0bdd98 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -36,9 +36,7 @@ use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{ - intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, -}; +use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; use chrono_tz::Tz; use std::str::FromStr; @@ -62,7 +60,7 @@ impl IntoPy for Tz { impl FromPyObject<'_> for Tz { fn extract(ob: &PyAny) -> PyResult { - Tz::from_str(ob.getattr(intern_bound!(ob.py(), "key"))?.extract()?) + Tz::from_str(ob.getattr(intern!(ob.py(), "key"))?.extract()?) .map_err(|e| PyValueError::new_err(e.to_string())) } } diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index b7d2f11c036..5cc2157d446 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -81,9 +81,7 @@ macro_rules! bigint_conversion { let bytes_obj = PyBytes::new(py, &bytes); let kwargs = if $is_signed > 0 { let kwargs = PyDict::new(py); - kwargs - .set_item(crate::intern_bound!(py, "signed"), true) - .unwrap(); + kwargs.set_item(crate::intern!(py, "signed"), true).unwrap(); Some(kwargs) } else { None @@ -210,18 +208,18 @@ fn int_to_u32_vec(long: &PyLong, n_digits: usize, is_signed: bool) -> PyResult PyResult<&PyBytes> { - use crate::intern_bound; + use crate::intern; let py = long.py(); let kwargs = if is_signed { let kwargs = PyDict::new(py); - kwargs.set_item(intern_bound!(py, "signed"), true)?; + kwargs.set_item(intern!(py, "signed"), true)?; Some(kwargs) } else { None }; let bytes = long.call_method( - intern_bound!(py, "to_bytes"), - (n_bytes, intern_bound!(py, "little")), + intern!(py, "to_bytes"), + (n_bytes, intern!(py, "little")), kwargs, )?; Ok(bytes.downcast()?) @@ -243,7 +241,7 @@ fn int_n_bits(long: &PyLong) -> PyResult { #[cfg(Py_LIMITED_API)] { // slow path - long.call_method0(crate::intern_bound!(py, "bit_length")) + long.call_method0(crate::intern!(py, "bit_length")) .and_then(PyAny::extract) } } diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index ad8ea27397d..ba741323611 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -153,7 +153,7 @@ macro_rules! complex_conversion { let obj = if obj.is_instance_of::() { obj } else if let Some(method) = - obj.lookup_special(crate::intern_bound!(obj.py(), "__complex__"))? + obj.lookup_special(crate::intern!(obj.py(), "__complex__"))? { complex = method.call0()?; &complex diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index b42488e9f11..173e57851c9 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -52,9 +52,7 @@ use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{ - intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, -}; +use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; use rust_decimal::Decimal; use std::str::FromStr; @@ -75,8 +73,8 @@ static DECIMAL_CLS: GILOnceCell> = GILOnceCell::new(); fn get_decimal_cls(py: Python<'_>) -> PyResult<&PyType> { DECIMAL_CLS .get_or_try_init(py, || { - py.import(intern_bound!(py, "decimal"))? - .getattr(intern_bound!(py, "Decimal"))? + py.import(intern!(py, "decimal"))? + .getattr(intern!(py, "Decimal"))? .extract() }) .map(|ty| ty.as_ref(py)) diff --git a/src/conversions/std/duration.rs b/src/conversions/std/duration.rs index 24f183a65ef..e4540bd0aaa 100755 --- a/src/conversions/std/duration.rs +++ b/src/conversions/std/duration.rs @@ -6,7 +6,7 @@ use crate::types::PyType; #[cfg(not(Py_LIMITED_API))] use crate::types::{PyDelta, PyDeltaAccess}; #[cfg(Py_LIMITED_API)] -use crate::{intern_bound, Py}; +use crate::{intern, Py}; use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject}; use std::time::Duration; @@ -26,10 +26,9 @@ impl FromPyObject<'_> for Duration { #[cfg(Py_LIMITED_API)] let (days, seconds, microseconds): (i32, i32, i32) = { ( - obj.getattr(intern_bound!(obj.py(), "days"))?.extract()?, - obj.getattr(intern_bound!(obj.py(), "seconds"))?.extract()?, - obj.getattr(intern_bound!(obj.py(), "microseconds"))? - .extract()?, + obj.getattr(intern!(obj.py(), "days"))?.extract()?, + obj.getattr(intern!(obj.py(), "seconds"))?.extract()?, + obj.getattr(intern!(obj.py(), "microseconds"))?.extract()?, ) }; diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index d3cbda2c216..713de0afd1a 100755 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -3,13 +3,11 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use crate::exceptions::PyValueError; use crate::sync::GILOnceCell; use crate::types::PyType; -use crate::{ - intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, -}; +use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject}; impl FromPyObject<'_> for IpAddr { fn extract(obj: &PyAny) -> PyResult { - match obj.getattr(intern_bound!(obj.py(), "packed")) { + match obj.getattr(intern!(obj.py(), "packed")) { Ok(packed) => { if let Ok(packed) = packed.extract::<[u8; 4]>() { Ok(IpAddr::V4(Ipv4Addr::from(packed))) diff --git a/src/coroutine/waker.rs b/src/coroutine/waker.rs index d4a63d35669..8a1166ce3fb 100644 --- a/src/coroutine/waker.rs +++ b/src/coroutine/waker.rs @@ -1,6 +1,6 @@ use crate::sync::GILOnceCell; use crate::types::PyCFunction; -use crate::{intern_bound, wrap_pyfunction, Py, PyAny, PyObject, PyResult, Python}; +use crate::{intern, wrap_pyfunction, Py, PyAny, PyObject, PyResult, Python}; use pyo3_macros::pyfunction; use std::sync::Arc; use std::task::Wake; @@ -72,7 +72,7 @@ impl LoopAndFuture { // so it requires `call_soon_threadsafe` let call_soon_threadsafe = self.event_loop.call_method1( py, - intern_bound!(py, "call_soon_threadsafe"), + intern!(py, "call_soon_threadsafe"), (release_waiter, self.future.as_ref(py)), ); if let Err(err) = call_soon_threadsafe { @@ -93,12 +93,9 @@ impl LoopAndFuture { /// See #[pyfunction(crate = "crate")] fn release_waiter(future: &PyAny) -> PyResult<()> { - let done = future.call_method0(intern_bound!(future.py(), "done"))?; + let done = future.call_method0(intern!(future.py(), "done"))?; if !done.extract::()? { - future.call_method1( - intern_bound!(future.py(), "set_result"), - (future.py().None(),), - )?; + future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?; } Ok(()) } diff --git a/src/instance.rs b/src/instance.rs index ff51cb30293..4dffe67f9b2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -971,18 +971,18 @@ impl Py { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// If calling this method becomes performance-critical, the [`intern_bound!`](crate::intern_bound) macro + /// If calling this method becomes performance-critical, the [`intern!`](crate::intern) macro /// can be used to intern `attr_name`, thereby avoiding repeated temporary allocations of /// Python strings. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, IntoPy, Py, Python, PyObject, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPy, Py, Python, PyObject, PyResult}; /// # /// #[pyfunction] /// fn version(sys: Py, py: Python<'_>) -> PyResult { - /// sys.getattr(py, intern_bound!(py, "version")) + /// sys.getattr(py, intern!(py, "version")) /// } /// # /// # Python::with_gil(|py| { @@ -1001,17 +1001,17 @@ impl Py { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) + /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) /// macro can be used to intern `attr_name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, IntoPy, PyObject, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPy, PyObject, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: PyObject, py: Python<'_>) -> PyResult<()> { - /// ob.setattr(py, intern_bound!(py, "answer"), 42) + /// ob.setattr(py, intern!(py, "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -1098,7 +1098,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) + /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) /// macro can be used to intern `name`. pub fn call_method_bound( &self, @@ -1121,7 +1121,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) + /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) /// macro can be used to intern `name`. pub fn call_method1(&self, py: Python<'_>, name: N, args: A) -> PyResult where @@ -1138,7 +1138,7 @@ impl Py { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`](crate::intern_bound) + /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) /// macro can be used to intern `name`. pub fn call_method0(&self, py: Python<'_>, name: N) -> PyResult where @@ -1644,8 +1644,8 @@ a = A() let module = PyModule::from_code(py, CODE, "", "")?; let instance: Py = module.getattr("a")?.into(); - let foo = crate::intern_bound!(py, "foo"); - let bar = crate::intern_bound!(py, "bar"); + let foo = crate::intern!(py, "foo"); + let bar = crate::intern!(py, "bar"); instance.getattr(py, foo).unwrap_err(); instance.setattr(py, foo, bar)?; diff --git a/src/marker.rs b/src/marker.rs index 6a298db6a9a..836d0109eeb 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -651,7 +651,7 @@ impl<'py> Python<'py> { // See also: // - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10) // - https://github.com/PyO3/pyo3/issues/3370 - let builtins_s = crate::intern_bound!(self, "__builtins__").as_ptr(); + let builtins_s = crate::intern!(self, "__builtins__").as_ptr(); let has_builtins = ffi::PyDict_Contains(globals, builtins_s); if has_builtins == -1 { return Err(PyErr::fetch(self)); diff --git a/src/sync.rs b/src/sync.rs index 36356c4fe5b..a88a0f4b485 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -202,29 +202,14 @@ impl GILOnceCell> { } } -/// Deprecated form of [intern_bound!][crate::intern_bound]. -#[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`intern_bound!` will be replaced by `intern_bound!` in a future PyO3 version" - ) -)] -#[macro_export] -macro_rules! intern { - ($py: expr, $text: expr) => { - $crate::intern_bound!($py, $text).as_gil_ref() - }; -} - /// Interns `text` as a Python string and stores a reference to it in static storage. /// /// A reference to the same Python string is returned on each invocation. /// -/// # Example: Using `intern_bound!` to avoid needlessly recreating the same Python string +/// # Example: Using `intern!` to avoid needlessly recreating the same Python string /// /// ``` -/// use pyo3::intern_bound; +/// use pyo3::intern; /// # use pyo3::{pyfunction, types::PyDict, wrap_pyfunction, PyResult, Python}; /// /// #[pyfunction] @@ -241,7 +226,7 @@ macro_rules! intern { /// let dict = PyDict::new(py); /// // 👇 A `PyString` is created once and reused /// // for the lifetime of the program. -/// dict.set_item(intern_bound!(py, "foo"), 42)?; +/// dict.set_item(intern!(py, "foo"), 42)?; /// Ok(dict) /// } /// # @@ -255,14 +240,14 @@ macro_rules! intern { /// # }); /// ``` #[macro_export] -macro_rules! intern_bound { +macro_rules! intern { ($py: expr, $text: expr) => {{ static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); INTERNED.get($py) }}; } -/// Implementation detail for `intern_bound!` macro. +/// Implementation detail for `intern!` macro. #[doc(hidden)] pub struct Interned(&'static str, GILOnceCell>); @@ -291,8 +276,8 @@ mod tests { fn test_intern() { Python::with_gil(|py| { let foo1 = "foo"; - let foo2 = intern_bound!(py, "foo"); - let foo3 = intern_bound!(py, stringify!(foo)); + let foo2 = intern!(py, "foo"); + let foo3 = intern!(py, stringify!(foo)); let dict = PyDict::new(py); dict.set_item(foo1, 42_usize).unwrap(); diff --git a/src/tests/hygiene/misc.rs b/src/tests/hygiene/misc.rs index 3008b5a3b9e..66db7f3a28a 100644 --- a/src/tests/hygiene/misc.rs +++ b/src/tests/hygiene/misc.rs @@ -30,8 +30,8 @@ crate::import_exception!(socket, gaierror); #[allow(dead_code)] fn intern(py: crate::Python<'_>) { - let _foo = crate::intern_bound!(py, "foo"); - let _bar = crate::intern_bound!(py, stringify!(bar)); + let _foo = crate::intern!(py, "foo"); + let _bar = crate::intern!(py, stringify!(bar)); } #[allow(dead_code)] diff --git a/src/types/any.rs b/src/types/any.rs index aec978f7fb7..14102d80a8d 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -80,17 +80,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, Python, PyResult}; /// # /// #[pyfunction] /// fn has_version(sys: &PyModule) -> PyResult { - /// sys.hasattr(intern_bound!(sys.py(), "version")) + /// sys.hasattr(intern!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -109,17 +109,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn version(sys: &PyModule) -> PyResult<&PyAny> { - /// sys.getattr(intern_bound!(sys.py(), "version")) + /// sys.getattr(intern!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -140,17 +140,17 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: &PyAny) -> PyResult<()> { - /// ob.setattr(intern_bound!(ob.py(), "answer"), 42) + /// ob.setattr(intern!(ob.py(), "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -170,7 +170,7 @@ impl PyAny { /// /// This is equivalent to the Python statement `del self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. pub fn delattr(&self, attr_name: N) -> PyResult<()> where @@ -465,7 +465,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -510,7 +510,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -550,7 +550,7 @@ impl PyAny { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -950,17 +950,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, Python, PyResult}; /// # /// #[pyfunction] /// fn has_version(sys: &PyModule) -> PyResult { - /// sys.hasattr(intern_bound!(sys.py(), "version")) + /// sys.hasattr(intern!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -976,17 +976,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn version(sys: &PyModule) -> PyResult<&PyAny> { - /// sys.getattr(intern_bound!(sys.py(), "version")) + /// sys.getattr(intern!(sys.py(), "version")) /// } /// # /// # Python::with_gil(|py| { @@ -1002,17 +1002,17 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.attr_name = value`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// - /// # Example: `intern_bound!`ing the attribute name + /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern_bound, pyfunction, types::PyModule, PyAny, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, PyAny, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: &PyAny) -> PyResult<()> { - /// ob.setattr(intern_bound!(ob.py(), "answer"), 42) + /// ob.setattr(intern!(ob.py(), "answer"), 42) /// } /// # /// # Python::with_gil(|py| { @@ -1029,7 +1029,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python statement `del self.attr_name`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. fn delattr(&self, attr_name: N) -> PyResult<()> where @@ -1337,7 +1337,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -1382,7 +1382,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name()`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -1417,7 +1417,7 @@ pub trait PyAnyMethods<'py> { /// /// This is equivalent to the Python expression `self.name(*args)`. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `name`. /// /// # Examples @@ -2257,7 +2257,7 @@ impl<'py> Bound<'py, PyAny> { /// typically a direct error for the special lookup to fail, as magic methods are optional in /// many situations in which they might be called. /// - /// To avoid repeated temporary allocations of Python strings, the [`intern_bound!`] macro can be used + /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. #[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that. pub(crate) fn lookup_special(&self, attr_name: N) -> PyResult>> @@ -2290,7 +2290,7 @@ impl<'py> Bound<'py, PyAny> { } else if let Ok(descr_get) = attr .get_type() .as_borrowed() - .getattr(crate::intern_bound!(py, "__get__")) + .getattr(crate::intern!(py, "__get__")) { descr_get.call1((attr, self, self_type)).map(Some) } else { @@ -2349,7 +2349,7 @@ class NonHeapNonDescriptorInt: ) .unwrap(); - let int = crate::intern_bound!(py, "__int__"); + let int = crate::intern!(py, "__int__"); let eval_int = |obj: &PyAny| { obj.as_borrowed() .lookup_special(int)? diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index b3384f8830b..0decc0b4c80 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -116,7 +116,7 @@ impl FromPyObject<'_> for bool { #[cfg(any(Py_LIMITED_API, PyPy))] { let meth = obj - .lookup_special(crate::intern_bound!(obj.py(), "__bool__"))? + .lookup_special(crate::intern!(obj.py(), "__bool__"))? .ok_or_else(|| missing_conversion(obj))?; let obj = meth.call0()?.downcast_into::()?; diff --git a/src/types/module.rs b/src/types/module.rs index a44fe383f5e..7edf164266f 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -661,11 +661,11 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { } fn __all__(py: Python<'_>) -> &Bound<'_, PyString> { - intern_bound!(py, "__all__") + intern!(py, "__all__") } fn __name__(py: Python<'_>) -> &Bound<'_, PyString> { - intern_bound!(py, "__name__") + intern!(py, "__name__") } #[cfg(test)] diff --git a/src/types/traceback.rs b/src/types/traceback.rs index 3dd9e150e31..84ecda747eb 100644 --- a/src/types/traceback.rs +++ b/src/types/traceback.rs @@ -95,13 +95,13 @@ impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> { fn format(&self) -> PyResult { let py = self.py(); let string_io = py - .import(intern_bound!(py, "io"))? - .getattr(intern_bound!(py, "StringIO"))? + .import(intern!(py, "io"))? + .getattr(intern!(py, "StringIO"))? .call0()?; let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) }; error_on_minusone(py, result)?; let formatted = string_io - .getattr(intern_bound!(py, "getvalue"))? + .getattr(intern!(py, "getvalue"))? .call0()? .downcast::()? .to_str()? diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index b37f8ebe564..a1a053f93a4 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -36,9 +36,7 @@ impl PyType { /// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`. pub fn qualname(&self) -> PyResult { #[cfg(any(Py_LIMITED_API, PyPy, not(Py_3_11)))] - let name = self - .getattr(intern_bound!(self.py(), "__qualname__"))? - .extract(); + let name = self.getattr(intern!(self.py(), "__qualname__"))?.extract(); #[cfg(not(any(Py_LIMITED_API, PyPy, not(Py_3_11))))] let name = { @@ -73,10 +71,10 @@ impl PyType { #[cfg(any(Py_LIMITED_API, PyPy))] { - let module = self.getattr(intern_bound!(self.py(), "__module__"))?; + let module = self.getattr(intern!(self.py(), "__module__"))?; #[cfg(not(Py_3_11))] - let name = self.getattr(intern_bound!(self.py(), "__name__"))?; + let name = self.getattr(intern!(self.py(), "__name__"))?; #[cfg(Py_3_11)] let name = { diff --git a/tests/ui/invalid_intern_arg.rs b/tests/ui/invalid_intern_arg.rs index 7bc29ddc65b..fa9e1e59f0c 100644 --- a/tests/ui/invalid_intern_arg.rs +++ b/tests/ui/invalid_intern_arg.rs @@ -2,5 +2,5 @@ use pyo3::Python; fn main() { let foo = if true { "foo" } else { "bar" }; - Python::with_gil(|py| py.import(pyo3::intern_bound!(py, foo)).unwrap()); + Python::with_gil(|py| py.import(pyo3::intern!(py, foo)).unwrap()); } diff --git a/tests/ui/invalid_intern_arg.stderr b/tests/ui/invalid_intern_arg.stderr index 5d03cadf6ed..bb84d00e15b 100644 --- a/tests/ui/invalid_intern_arg.stderr +++ b/tests/ui/invalid_intern_arg.stderr @@ -1,8 +1,8 @@ error[E0435]: attempt to use a non-constant value in a constant - --> tests/ui/invalid_intern_arg.rs:5:61 + --> tests/ui/invalid_intern_arg.rs:5:55 | -5 | Python::with_gil(|py| py.import(pyo3::intern_bound!(py, foo)).unwrap()); - | ------------------------^^^- - | | | - | | non-constant value +5 | Python::with_gil(|py| py.import(pyo3::intern!(py, foo)).unwrap()); + | ------------------^^^- + | | | + | | non-constant value | help: consider using `let` instead of `static`: `let INTERNED`