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`