add `intern_bound!` macro
This commit is contained in:
parent
fed8bcadaf
commit
aa139ad422
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()?,
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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));
|
||||
|
|
37
src/sync.rs
37
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<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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)?
|
||||
|
|
|
@ -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>()?;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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()?
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
|
Loading…
Reference in New Issue