for now just change return type of `intern!`

This commit is contained in:
David Hewitt 2024-01-30 13:28:07 +00:00
parent aa139ad422
commit 2f00eb1423
23 changed files with 117 additions and 155 deletions

View File

@ -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<T>` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound<PyTuple>` 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. 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<T>` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound<PyTuple>` 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<PyString>` 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. 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: Before:
@ -40,7 +42,6 @@ After:
pyo3 = { version = "0.21", features = ["gil-refs"] } pyo3 = { version = "0.21", features = ["gil-refs"] }
``` ```
### `PyTypeInfo` and `PyTryFrom` have been adjusted ### `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`. 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<T>` smart pointe
For example, the following APIs have gained updated variants: 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. - `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) - `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<T>` 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: Because the new `Bound<T>` 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<T>` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count) - Code will need to add the occasional `&` to borrow the new smart pointer as `&Bound<T>` to pass these types around (or use `.clone()` at the very small cost of increasing the Python reference count)

View File

@ -2,7 +2,7 @@ use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criter
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::intern_bound; use pyo3::intern;
fn getattr_direct(b: &mut Bencher<'_>) { fn getattr_direct(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
@ -16,7 +16,7 @@ fn getattr_intern(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let sys = py.import("sys").unwrap(); let sys = py.import("sys").unwrap();
b.iter(|| sys.getattr(intern_bound!(py, "version")).unwrap()); b.iter(|| sys.getattr(intern!(py, "version")).unwrap());
}); });
} }

View File

@ -324,17 +324,17 @@ impl<'a> Container<'a> {
let field_name = ident.to_string(); let field_name = ident.to_string();
let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) { let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) {
FieldGetter::GetAttr(Some(name)) => { FieldGetter::GetAttr(Some(name)) => {
quote!(getattr(_pyo3::intern_bound!(obj.py(), #name))) quote!(getattr(_pyo3::intern!(obj.py(), #name)))
} }
FieldGetter::GetAttr(None) => { 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))) => { 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(Some(key)) => quote!(get_item(#key)),
FieldGetter::GetItem(None) => { 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 { let extractor = match &field.from_py_with {

View File

@ -522,7 +522,7 @@ impl<'a> FnSpec<'a> {
let mut call = quote! {{ let mut call = quote! {{
let future = #future; let future = #future;
_pyo3::impl_::coroutine::new_coroutine( _pyo3::impl_::coroutine::new_coroutine(
_pyo3::intern_bound!(py, stringify!(#python_name)), _pyo3::intern!(py, stringify!(#python_name)),
#qualname_prefix, #qualname_prefix,
#throw_callback, #throw_callback,
async move { _pyo3::impl_::wrap::OkWrap::wrap(future.await) }, async move { _pyo3::impl_::wrap::OkWrap::wrap(future.await) },

View File

@ -52,7 +52,7 @@ use crate::types::{
PyTzInfo, PyTzInfoAccess, PyTzInfo, PyTzInfoAccess,
}; };
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
use crate::{intern_bound, PyDowncastError}; use crate::{intern, PyDowncastError};
use crate::{ use crate::{
FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
}; };
@ -127,10 +127,9 @@ impl FromPyObject<'_> for Duration {
let (days, seconds, microseconds) = { let (days, seconds, microseconds) = {
check_type(ob, &DatetimeTypes::get(ob.py()).timedelta, "PyDelta")?; check_type(ob, &DatetimeTypes::get(ob.py()).timedelta, "PyDelta")?;
( (
ob.getattr(intern_bound!(ob.py(), "days"))?.extract()?, ob.getattr(intern!(ob.py(), "days"))?.extract()?,
ob.getattr(intern_bound!(ob.py(), "seconds"))?.extract()?, ob.getattr(intern!(ob.py(), "seconds"))?.extract()?,
ob.getattr(intern_bound!(ob.py(), "microseconds"))? ob.getattr(intern!(ob.py(), "microseconds"))?.extract()?,
.extract()?,
) )
}; };
Ok( Ok(
@ -251,7 +250,7 @@ impl FromPyObject<'_> for NaiveDateTime {
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
let has_tzinfo = dt.get_tzinfo_bound().is_some(); let has_tzinfo = dt.get_tzinfo_bound().is_some();
#[cfg(Py_LIMITED_API)] #[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 { if has_tzinfo {
return Err(PyTypeError::new_err("expected a datetime without tzinfo")); return Err(PyTypeError::new_err("expected a datetime without tzinfo"));
} }
@ -287,7 +286,7 @@ impl<Tz: TimeZone + for<'a> FromPyObject<'a>> FromPyObject<'_> for DateTime<Tz>
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
let tzinfo = dt.get_tzinfo_bound(); let tzinfo = dt.get_tzinfo_bound();
#[cfg(Py_LIMITED_API)] #[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 { let tz = if let Some(tzinfo) = tzinfo {
tzinfo.extract()? tzinfo.extract()?
@ -483,15 +482,9 @@ fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult<NaiveDate> {
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
fn py_date_to_naive_date(py_date: &PyAny) -> PyResult<NaiveDate> { fn py_date_to_naive_date(py_date: &PyAny) -> PyResult<NaiveDate> {
NaiveDate::from_ymd_opt( NaiveDate::from_ymd_opt(
py_date py_date.getattr(intern!(py_date.py(), "year"))?.extract()?,
.getattr(intern_bound!(py_date.py(), "year"))? py_date.getattr(intern!(py_date.py(), "month"))?.extract()?,
.extract()?, py_date.getattr(intern!(py_date.py(), "day"))?.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")) .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<NaiveTime> {
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> { fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
NaiveTime::from_hms_micro_opt( NaiveTime::from_hms_micro_opt(
py_time.getattr(intern!(py_time.py(), "hour"))?.extract()?,
py_time py_time
.getattr(intern_bound!(py_time.py(), "hour"))? .getattr(intern!(py_time.py(), "minute"))?
.extract()?, .extract()?,
py_time py_time
.getattr(intern_bound!(py_time.py(), "minute"))? .getattr(intern!(py_time.py(), "second"))?
.extract()?, .extract()?,
py_time py_time
.getattr(intern_bound!(py_time.py(), "second"))? .getattr(intern!(py_time.py(), "microsecond"))?
.extract()?,
py_time
.getattr(intern_bound!(py_time.py(), "microsecond"))?
.extract()?, .extract()?,
) )
.ok_or_else(|| PyValueError::new_err("invalid or out-of-range time")) .ok_or_else(|| PyValueError::new_err("invalid or out-of-range time"))

View File

@ -36,9 +36,7 @@
use crate::exceptions::PyValueError; use crate::exceptions::PyValueError;
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::types::PyType; use crate::types::PyType;
use crate::{ use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
};
use chrono_tz::Tz; use chrono_tz::Tz;
use std::str::FromStr; use std::str::FromStr;
@ -62,7 +60,7 @@ impl IntoPy<PyObject> for Tz {
impl FromPyObject<'_> for Tz { impl FromPyObject<'_> for Tz {
fn extract(ob: &PyAny) -> PyResult<Tz> { fn extract(ob: &PyAny) -> PyResult<Tz> {
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())) .map_err(|e| PyValueError::new_err(e.to_string()))
} }
} }

View File

@ -81,9 +81,7 @@ macro_rules! bigint_conversion {
let bytes_obj = PyBytes::new(py, &bytes); let bytes_obj = PyBytes::new(py, &bytes);
let kwargs = if $is_signed > 0 { let kwargs = if $is_signed > 0 {
let kwargs = PyDict::new(py); let kwargs = PyDict::new(py);
kwargs kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
.set_item(crate::intern_bound!(py, "signed"), true)
.unwrap();
Some(kwargs) Some(kwargs)
} else { } else {
None None
@ -210,18 +208,18 @@ fn int_to_u32_vec(long: &PyLong, n_digits: usize, is_signed: bool) -> PyResult<V
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
fn int_to_py_bytes(long: &PyLong, n_bytes: usize, is_signed: bool) -> PyResult<&PyBytes> { fn int_to_py_bytes(long: &PyLong, n_bytes: usize, is_signed: bool) -> PyResult<&PyBytes> {
use crate::intern_bound; use crate::intern;
let py = long.py(); let py = long.py();
let kwargs = if is_signed { let kwargs = if is_signed {
let kwargs = PyDict::new(py); let kwargs = PyDict::new(py);
kwargs.set_item(intern_bound!(py, "signed"), true)?; kwargs.set_item(intern!(py, "signed"), true)?;
Some(kwargs) Some(kwargs)
} else { } else {
None None
}; };
let bytes = long.call_method( let bytes = long.call_method(
intern_bound!(py, "to_bytes"), intern!(py, "to_bytes"),
(n_bytes, intern_bound!(py, "little")), (n_bytes, intern!(py, "little")),
kwargs, kwargs,
)?; )?;
Ok(bytes.downcast()?) Ok(bytes.downcast()?)
@ -243,7 +241,7 @@ fn int_n_bits(long: &PyLong) -> PyResult<usize> {
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
{ {
// slow path // slow path
long.call_method0(crate::intern_bound!(py, "bit_length")) long.call_method0(crate::intern!(py, "bit_length"))
.and_then(PyAny::extract) .and_then(PyAny::extract)
} }
} }

View File

@ -153,7 +153,7 @@ macro_rules! complex_conversion {
let obj = if obj.is_instance_of::<PyComplex>() { let obj = if obj.is_instance_of::<PyComplex>() {
obj obj
} else if let Some(method) = } 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 = method.call0()?;
&complex &complex

View File

@ -52,9 +52,7 @@
use crate::exceptions::PyValueError; use crate::exceptions::PyValueError;
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::types::PyType; use crate::types::PyType;
use crate::{ use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use std::str::FromStr; use std::str::FromStr;
@ -75,8 +73,8 @@ static DECIMAL_CLS: GILOnceCell<Py<PyType>> = GILOnceCell::new();
fn get_decimal_cls(py: Python<'_>) -> PyResult<&PyType> { fn get_decimal_cls(py: Python<'_>) -> PyResult<&PyType> {
DECIMAL_CLS DECIMAL_CLS
.get_or_try_init(py, || { .get_or_try_init(py, || {
py.import(intern_bound!(py, "decimal"))? py.import(intern!(py, "decimal"))?
.getattr(intern_bound!(py, "Decimal"))? .getattr(intern!(py, "Decimal"))?
.extract() .extract()
}) })
.map(|ty| ty.as_ref(py)) .map(|ty| ty.as_ref(py))

View File

@ -6,7 +6,7 @@ use crate::types::PyType;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
use crate::types::{PyDelta, PyDeltaAccess}; use crate::types::{PyDelta, PyDeltaAccess};
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
use crate::{intern_bound, Py}; use crate::{intern, Py};
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject}; use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
use std::time::Duration; use std::time::Duration;
@ -26,10 +26,9 @@ impl FromPyObject<'_> for Duration {
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
let (days, seconds, microseconds): (i32, i32, i32) = { let (days, seconds, microseconds): (i32, i32, i32) = {
( (
obj.getattr(intern_bound!(obj.py(), "days"))?.extract()?, obj.getattr(intern!(obj.py(), "days"))?.extract()?,
obj.getattr(intern_bound!(obj.py(), "seconds"))?.extract()?, obj.getattr(intern!(obj.py(), "seconds"))?.extract()?,
obj.getattr(intern_bound!(obj.py(), "microseconds"))? obj.getattr(intern!(obj.py(), "microseconds"))?.extract()?,
.extract()?,
) )
}; };

View File

@ -3,13 +3,11 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::exceptions::PyValueError; use crate::exceptions::PyValueError;
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::types::PyType; use crate::types::PyType;
use crate::{ use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
intern_bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
};
impl FromPyObject<'_> for IpAddr { impl FromPyObject<'_> for IpAddr {
fn extract(obj: &PyAny) -> PyResult<Self> { fn extract(obj: &PyAny) -> PyResult<Self> {
match obj.getattr(intern_bound!(obj.py(), "packed")) { match obj.getattr(intern!(obj.py(), "packed")) {
Ok(packed) => { Ok(packed) => {
if let Ok(packed) = packed.extract::<[u8; 4]>() { if let Ok(packed) = packed.extract::<[u8; 4]>() {
Ok(IpAddr::V4(Ipv4Addr::from(packed))) Ok(IpAddr::V4(Ipv4Addr::from(packed)))

View File

@ -1,6 +1,6 @@
use crate::sync::GILOnceCell; use crate::sync::GILOnceCell;
use crate::types::PyCFunction; 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 pyo3_macros::pyfunction;
use std::sync::Arc; use std::sync::Arc;
use std::task::Wake; use std::task::Wake;
@ -72,7 +72,7 @@ impl LoopAndFuture {
// so it requires `call_soon_threadsafe` // so it requires `call_soon_threadsafe`
let call_soon_threadsafe = self.event_loop.call_method1( let call_soon_threadsafe = self.event_loop.call_method1(
py, py,
intern_bound!(py, "call_soon_threadsafe"), intern!(py, "call_soon_threadsafe"),
(release_waiter, self.future.as_ref(py)), (release_waiter, self.future.as_ref(py)),
); );
if let Err(err) = call_soon_threadsafe { if let Err(err) = call_soon_threadsafe {
@ -93,12 +93,9 @@ impl LoopAndFuture {
/// See <https://github.com/python/cpython/blob/main/Lib/asyncio/tasks.py#L452C5-L452C5> /// See <https://github.com/python/cpython/blob/main/Lib/asyncio/tasks.py#L452C5-L452C5>
#[pyfunction(crate = "crate")] #[pyfunction(crate = "crate")]
fn release_waiter(future: &PyAny) -> PyResult<()> { 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::<bool>()? { if !done.extract::<bool>()? {
future.call_method1( future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?;
intern_bound!(future.py(), "set_result"),
(future.py().None(),),
)?;
} }
Ok(()) Ok(())
} }

View File

@ -971,18 +971,18 @@ impl<T> Py<T> {
/// ///
/// This is equivalent to the Python expression `self.attr_name`. /// 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 /// can be used to intern `attr_name`, thereby avoiding repeated temporary allocations of
/// Python strings. /// 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] /// #[pyfunction]
/// fn version(sys: Py<PyModule>, py: Python<'_>) -> PyResult<PyObject> { /// fn version(sys: Py<PyModule>, py: Python<'_>) -> PyResult<PyObject> {
/// sys.getattr(py, intern_bound!(py, "version")) /// sys.getattr(py, intern!(py, "version"))
/// } /// }
/// # /// #
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
@ -1001,17 +1001,17 @@ impl<T> Py<T> {
/// ///
/// This is equivalent to the Python expression `self.attr_name = value`. /// 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`. /// 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] /// #[pyfunction]
/// fn set_answer(ob: PyObject, py: Python<'_>) -> PyResult<()> { /// 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| { /// # Python::with_gil(|py| {
@ -1098,7 +1098,7 @@ impl<T> Py<T> {
/// ///
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// 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`. /// macro can be used to intern `name`.
pub fn call_method_bound<N, A>( pub fn call_method_bound<N, A>(
&self, &self,
@ -1121,7 +1121,7 @@ impl<T> Py<T> {
/// ///
/// This is equivalent to the Python expression `self.name(*args)`. /// 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`. /// macro can be used to intern `name`.
pub fn call_method1<N, A>(&self, py: Python<'_>, name: N, args: A) -> PyResult<PyObject> pub fn call_method1<N, A>(&self, py: Python<'_>, name: N, args: A) -> PyResult<PyObject>
where where
@ -1138,7 +1138,7 @@ impl<T> Py<T> {
/// ///
/// This is equivalent to the Python expression `self.name()`. /// 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`. /// macro can be used to intern `name`.
pub fn call_method0<N>(&self, py: Python<'_>, name: N) -> PyResult<PyObject> pub fn call_method0<N>(&self, py: Python<'_>, name: N) -> PyResult<PyObject>
where where
@ -1644,8 +1644,8 @@ a = A()
let module = PyModule::from_code(py, CODE, "", "")?; let module = PyModule::from_code(py, CODE, "", "")?;
let instance: Py<PyAny> = module.getattr("a")?.into(); let instance: Py<PyAny> = module.getattr("a")?.into();
let foo = crate::intern_bound!(py, "foo"); let foo = crate::intern!(py, "foo");
let bar = crate::intern_bound!(py, "bar"); let bar = crate::intern!(py, "bar");
instance.getattr(py, foo).unwrap_err(); instance.getattr(py, foo).unwrap_err();
instance.setattr(py, foo, bar)?; instance.setattr(py, foo, bar)?;

View File

@ -651,7 +651,7 @@ impl<'py> Python<'py> {
// See also: // See also:
// - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10) // - https://github.com/python/cpython/pull/24564 (the same fix in CPython 3.10)
// - https://github.com/PyO3/pyo3/issues/3370 // - 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); let has_builtins = ffi::PyDict_Contains(globals, builtins_s);
if has_builtins == -1 { if has_builtins == -1 {
return Err(PyErr::fetch(self)); return Err(PyErr::fetch(self));

View File

@ -202,29 +202,14 @@ impl GILOnceCell<Py<PyType>> {
} }
} }
/// 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. /// 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. /// 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}; /// # use pyo3::{pyfunction, types::PyDict, wrap_pyfunction, PyResult, Python};
/// ///
/// #[pyfunction] /// #[pyfunction]
@ -241,7 +226,7 @@ macro_rules! intern {
/// let dict = PyDict::new(py); /// let dict = PyDict::new(py);
/// // 👇 A `PyString` is created once and reused /// // 👇 A `PyString` is created once and reused
/// // for the lifetime of the program. /// // for the lifetime of the program.
/// dict.set_item(intern_bound!(py, "foo"), 42)?; /// dict.set_item(intern!(py, "foo"), 42)?;
/// Ok(dict) /// Ok(dict)
/// } /// }
/// # /// #
@ -255,14 +240,14 @@ macro_rules! intern {
/// # }); /// # });
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! intern_bound { macro_rules! intern {
($py: expr, $text: expr) => {{ ($py: expr, $text: expr) => {{
static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text);
INTERNED.get($py) INTERNED.get($py)
}}; }};
} }
/// Implementation detail for `intern_bound!` macro. /// Implementation detail for `intern!` macro.
#[doc(hidden)] #[doc(hidden)]
pub struct Interned(&'static str, GILOnceCell<Py<PyString>>); pub struct Interned(&'static str, GILOnceCell<Py<PyString>>);
@ -291,8 +276,8 @@ mod tests {
fn test_intern() { fn test_intern() {
Python::with_gil(|py| { Python::with_gil(|py| {
let foo1 = "foo"; let foo1 = "foo";
let foo2 = intern_bound!(py, "foo"); let foo2 = intern!(py, "foo");
let foo3 = intern_bound!(py, stringify!(foo)); let foo3 = intern!(py, stringify!(foo));
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item(foo1, 42_usize).unwrap(); dict.set_item(foo1, 42_usize).unwrap();

View File

@ -30,8 +30,8 @@ crate::import_exception!(socket, gaierror);
#[allow(dead_code)] #[allow(dead_code)]
fn intern(py: crate::Python<'_>) { fn intern(py: crate::Python<'_>) {
let _foo = crate::intern_bound!(py, "foo"); let _foo = crate::intern!(py, "foo");
let _bar = crate::intern_bound!(py, stringify!(bar)); let _bar = crate::intern!(py, stringify!(bar));
} }
#[allow(dead_code)] #[allow(dead_code)]

View File

@ -80,17 +80,17 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// 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`. /// 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] /// #[pyfunction]
/// fn has_version(sys: &PyModule) -> PyResult<bool> { /// fn has_version(sys: &PyModule) -> PyResult<bool> {
/// sys.hasattr(intern_bound!(sys.py(), "version")) /// sys.hasattr(intern!(sys.py(), "version"))
/// } /// }
/// # /// #
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
@ -109,17 +109,17 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `self.attr_name`. /// 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`. /// 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] /// #[pyfunction]
/// fn version(sys: &PyModule) -> PyResult<&PyAny> { /// fn version(sys: &PyModule) -> PyResult<&PyAny> {
/// sys.getattr(intern_bound!(sys.py(), "version")) /// sys.getattr(intern!(sys.py(), "version"))
/// } /// }
/// # /// #
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
@ -140,17 +140,17 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `self.attr_name = value`. /// 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`. /// 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] /// #[pyfunction]
/// fn set_answer(ob: &PyAny) -> PyResult<()> { /// 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| { /// # Python::with_gil(|py| {
@ -170,7 +170,7 @@ impl PyAny {
/// ///
/// This is equivalent to the Python statement `del self.attr_name`. /// 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`. /// to intern `attr_name`.
pub fn delattr<N>(&self, attr_name: N) -> PyResult<()> pub fn delattr<N>(&self, attr_name: N) -> PyResult<()>
where where
@ -465,7 +465,7 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # Examples
@ -510,7 +510,7 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `self.name()`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # Examples
@ -550,7 +550,7 @@ impl PyAny {
/// ///
/// This is equivalent to the Python expression `self.name(*args)`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # Examples
@ -950,17 +950,17 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `hasattr(self, attr_name)`. /// 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`. /// 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] /// #[pyfunction]
/// fn has_version(sys: &PyModule) -> PyResult<bool> { /// fn has_version(sys: &PyModule) -> PyResult<bool> {
/// sys.hasattr(intern_bound!(sys.py(), "version")) /// sys.hasattr(intern!(sys.py(), "version"))
/// } /// }
/// # /// #
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
@ -976,17 +976,17 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `self.attr_name`. /// 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`. /// 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] /// #[pyfunction]
/// fn version(sys: &PyModule) -> PyResult<&PyAny> { /// fn version(sys: &PyModule) -> PyResult<&PyAny> {
/// sys.getattr(intern_bound!(sys.py(), "version")) /// sys.getattr(intern!(sys.py(), "version"))
/// } /// }
/// # /// #
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
@ -1002,17 +1002,17 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `self.attr_name = value`. /// 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`. /// 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] /// #[pyfunction]
/// fn set_answer(ob: &PyAny) -> PyResult<()> { /// 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| { /// # Python::with_gil(|py| {
@ -1029,7 +1029,7 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python statement `del self.attr_name`. /// 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`. /// to intern `attr_name`.
fn delattr<N>(&self, attr_name: N) -> PyResult<()> fn delattr<N>(&self, attr_name: N) -> PyResult<()>
where where
@ -1337,7 +1337,7 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # Examples
@ -1382,7 +1382,7 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `self.name()`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # Examples
@ -1417,7 +1417,7 @@ pub trait PyAnyMethods<'py> {
/// ///
/// This is equivalent to the Python expression `self.name(*args)`. /// 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`. /// to intern `name`.
/// ///
/// # Examples /// # 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 /// typically a direct error for the special lookup to fail, as magic methods are optional in
/// many situations in which they might be called. /// 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`. /// to intern `attr_name`.
#[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that. #[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that.
pub(crate) fn lookup_special<N>(&self, attr_name: N) -> PyResult<Option<Bound<'py, PyAny>>> pub(crate) fn lookup_special<N>(&self, attr_name: N) -> PyResult<Option<Bound<'py, PyAny>>>
@ -2290,7 +2290,7 @@ impl<'py> Bound<'py, PyAny> {
} else if let Ok(descr_get) = attr } else if let Ok(descr_get) = attr
.get_type() .get_type()
.as_borrowed() .as_borrowed()
.getattr(crate::intern_bound!(py, "__get__")) .getattr(crate::intern!(py, "__get__"))
{ {
descr_get.call1((attr, self, self_type)).map(Some) descr_get.call1((attr, self, self_type)).map(Some)
} else { } else {
@ -2349,7 +2349,7 @@ class NonHeapNonDescriptorInt:
) )
.unwrap(); .unwrap();
let int = crate::intern_bound!(py, "__int__"); let int = crate::intern!(py, "__int__");
let eval_int = |obj: &PyAny| { let eval_int = |obj: &PyAny| {
obj.as_borrowed() obj.as_borrowed()
.lookup_special(int)? .lookup_special(int)?

View File

@ -116,7 +116,7 @@ impl FromPyObject<'_> for bool {
#[cfg(any(Py_LIMITED_API, PyPy))] #[cfg(any(Py_LIMITED_API, PyPy))]
{ {
let meth = obj let meth = obj
.lookup_special(crate::intern_bound!(obj.py(), "__bool__"))? .lookup_special(crate::intern!(obj.py(), "__bool__"))?
.ok_or_else(|| missing_conversion(obj))?; .ok_or_else(|| missing_conversion(obj))?;
let obj = meth.call0()?.downcast_into::<PyBool>()?; let obj = meth.call0()?.downcast_into::<PyBool>()?;

View File

@ -661,11 +661,11 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> {
} }
fn __all__(py: Python<'_>) -> &Bound<'_, PyString> { fn __all__(py: Python<'_>) -> &Bound<'_, PyString> {
intern_bound!(py, "__all__") intern!(py, "__all__")
} }
fn __name__(py: Python<'_>) -> &Bound<'_, PyString> { fn __name__(py: Python<'_>) -> &Bound<'_, PyString> {
intern_bound!(py, "__name__") intern!(py, "__name__")
} }
#[cfg(test)] #[cfg(test)]

View File

@ -95,13 +95,13 @@ impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> {
fn format(&self) -> PyResult<String> { fn format(&self) -> PyResult<String> {
let py = self.py(); let py = self.py();
let string_io = py let string_io = py
.import(intern_bound!(py, "io"))? .import(intern!(py, "io"))?
.getattr(intern_bound!(py, "StringIO"))? .getattr(intern!(py, "StringIO"))?
.call0()?; .call0()?;
let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) }; let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) };
error_on_minusone(py, result)?; error_on_minusone(py, result)?;
let formatted = string_io let formatted = string_io
.getattr(intern_bound!(py, "getvalue"))? .getattr(intern!(py, "getvalue"))?
.call0()? .call0()?
.downcast::<PyString>()? .downcast::<PyString>()?
.to_str()? .to_str()?

View File

@ -36,9 +36,7 @@ impl PyType {
/// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`. /// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`.
pub fn qualname(&self) -> PyResult<String> { pub fn qualname(&self) -> PyResult<String> {
#[cfg(any(Py_LIMITED_API, PyPy, not(Py_3_11)))] #[cfg(any(Py_LIMITED_API, PyPy, not(Py_3_11)))]
let name = self let name = self.getattr(intern!(self.py(), "__qualname__"))?.extract();
.getattr(intern_bound!(self.py(), "__qualname__"))?
.extract();
#[cfg(not(any(Py_LIMITED_API, PyPy, not(Py_3_11))))] #[cfg(not(any(Py_LIMITED_API, PyPy, not(Py_3_11))))]
let name = { let name = {
@ -73,10 +71,10 @@ impl PyType {
#[cfg(any(Py_LIMITED_API, PyPy))] #[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))] #[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)] #[cfg(Py_3_11)]
let name = { let name = {

View File

@ -2,5 +2,5 @@ use pyo3::Python;
fn main() { fn main() {
let foo = if true { "foo" } else { "bar" }; 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());
} }

View File

@ -1,8 +1,8 @@
error[E0435]: attempt to use a non-constant value in a constant 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()); 5 | Python::with_gil(|py| py.import(pyo3::intern!(py, foo)).unwrap());
| ------------------------^^^- | ------------------^^^-
| | | | | |
| | non-constant value | | non-constant value
| help: consider using `let` instead of `static`: `let INTERNED` | help: consider using `let` instead of `static`: `let INTERNED`