Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change return type of intern! macro to &Bound<PyString> #3781

Merged
merged 3 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pyo3-benches/benches/bench_intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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());
});
}

Expand Down
8 changes: 4 additions & 4 deletions pyo3-macros-backend/src/frompyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) },
Expand Down
35 changes: 22 additions & 13 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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"));
}
Expand Down Expand Up @@ -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()?
Expand Down Expand Up @@ -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"))
}
Expand All @@ -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_bound!(py_time.py(), "minute"))?
.extract()?,
py_time
.getattr(intern!(py_time.py(), "second"))?
.getattr(intern_bound!(py_time.py(), "second"))?
.extract()?,
py_time
.getattr(intern!(py_time.py(), "microsecond"))?
.getattr(intern_bound!(py_time.py(), "microsecond"))?
.extract()?,
)
.ok_or_else(|| PyValueError::new_err("invalid or out-of-range time"))
Expand Down
6 changes: 4 additions & 2 deletions src/conversions/chrono_tz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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()))
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()?)
Expand All @@ -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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/num_complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions src/conversions/rust_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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))
Expand Down
9 changes: 5 additions & 4 deletions src/conversions/std/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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

Expand Down
6 changes: 4 additions & 2 deletions src/conversions/std/ipaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down
11 changes: 7 additions & 4 deletions src/coroutine/waker.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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(())
}
10 changes: 8 additions & 2 deletions src/impl_/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
Loading
Loading