add `intern_bound!` macro

This commit is contained in:
David Hewitt 2024-01-30 09:13:42 +00:00
parent fed8bcadaf
commit aa139ad422
25 changed files with 171 additions and 127 deletions

View File

@ -242,6 +242,7 @@ To minimise breakage of code using the GIL-Refs API, the `Bound<T>` 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<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)
@ -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

View File

@ -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());
});
}

View File

@ -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 {

View File

@ -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) },

View File

@ -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<Tz: TimeZone + for<'a> FromPyObject<'a>> FromPyObject<'_> for DateTime<Tz>
#[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<NaiveDate> {
#[cfg(Py_LIMITED_API)]
fn py_date_to_naive_date(py_date: &PyAny) -> PyResult<NaiveDate> {
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<NaiveTime> {
#[cfg(Py_LIMITED_API)]
fn py_time_to_naive_time(py_time: &PyAny) -> PyResult<NaiveTime> {
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!(py_time.py(), "second"))?
.getattr(intern_bound!(py_time.py(), "minute"))?
.extract()?,
py_time
.getattr(intern!(py_time.py(), "microsecond"))?
.getattr(intern_bound!(py_time.py(), "second"))?
.extract()?,
py_time
.getattr(intern_bound!(py_time.py(), "microsecond"))?
.extract()?,
)
.ok_or_else(|| PyValueError::new_err("invalid or out-of-range time"))

View File

@ -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<PyObject> for Tz {
impl FromPyObject<'_> for Tz {
fn extract(ob: &PyAny) -> PyResult<Tz> {
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()))
}
}

View File

@ -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<V
#[cfg(Py_LIMITED_API)]
fn int_to_py_bytes(long: &PyLong, n_bytes: usize, is_signed: bool) -> 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<usize> {
#[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)
}
}

View File

@ -153,7 +153,7 @@ macro_rules! complex_conversion {
let obj = if obj.is_instance_of::<PyComplex>() {
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

View File

@ -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<Py<PyType>> = 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))

View File

@ -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()?,
)
};

View File

@ -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<Self> {
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)))

View File

@ -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 <https://github.com/python/cpython/blob/main/Lib/asyncio/tasks.py#L452C5-L452C5>
#[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::<bool>()? {
future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?;
future.call_method1(
intern_bound!(future.py(), "set_result"),
(future.py().None(),),
)?;
}
Ok(())
}

View File

@ -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<F, T, E>(
name: &PyString,
name: &Bound<'_, PyString>,
qualname_prefix: Option<&'static str>,
throw_callback: Option<ThrowCallback>,
future: F,
@ -22,7 +23,12 @@ where
T: IntoPy<PyObject>,
E: Into<PyErr>,
{
Coroutine::new(Some(name.into()), qualname_prefix, throw_callback, future)
Coroutine::new(
Some(name.clone().into()),
qualname_prefix,
throw_callback,
future,
)
}
fn get_ptr<T: PyClass>(obj: &Py<T>) -> *mut T {

View File

@ -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<T> Py<T> {
///
/// 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<PyModule>, py: Python<'_>) -> PyResult<PyObject> {
/// sys.getattr(py, intern!(py, "version"))
/// sys.getattr(py, intern_bound!(py, "version"))
/// }
/// #
/// # Python::with_gil(|py| {
@ -1001,17 +1001,17 @@ impl<T> Py<T> {
///
/// 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<T> Py<T> {
///
/// 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<N, A>(
&self,
@ -1121,7 +1121,7 @@ impl<T> Py<T> {
///
/// 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<N, A>(&self, py: Python<'_>, name: N, args: A) -> PyResult<PyObject>
where
@ -1138,7 +1138,7 @@ impl<T> Py<T> {
///
/// 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<N>(&self, py: Python<'_>, name: N) -> PyResult<PyObject>
where
@ -1644,8 +1644,8 @@ a = A()
let module = PyModule::from_code(py, CODE, "", "")?;
let instance: Py<PyAny> = 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)?;

View File

@ -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));

View File

@ -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<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.
///
/// 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<Py<PyType>> {
/// 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<Py<PyType>> {
/// # });
/// ```
#[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<Py<PyString>>);
@ -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();

View File

@ -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)]

View File

@ -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<bool> {
/// 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<N>(&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<bool> {
/// 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<N>(&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<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
.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)?

View File

@ -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::<PyBool>()?;

View File

@ -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;

View File

@ -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)]

View File

@ -95,13 +95,13 @@ impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> {
fn format(&self) -> PyResult<String> {
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::<PyString>()?
.to_str()?

View File

@ -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<String> {
#[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 = {

View File

@ -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());
}

View File

@ -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`