Skip to content

Commit

Permalink
add intern_bound! macro
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jan 30, 2024
1 parent fed8bca commit aa139ad
Show file tree
Hide file tree
Showing 25 changed files with 171 additions and 127 deletions.
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

0 comments on commit aa139ad

Please sign in to comment.