Skip to content

Commit

Permalink
migrate call API to IntoPyObject (#4653)
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu authored Oct 26, 2024
1 parent fbeb2b6 commit b56806a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 285 deletions.
136 changes: 1 addition & 135 deletions src/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Defines conversions between Rust and Python types.
use crate::err::PyResult;
use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::pyclass::boolean_struct::False;
use crate::types::any::PyAnyMethods;
use crate::types::{PyDict, PyString, PyTuple};
use crate::types::PyTuple;
use crate::{
ffi, Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyObject, PyRef, PyRefMut, Python,
};
Expand Down Expand Up @@ -177,93 +176,6 @@ pub trait IntoPy<T>: Sized {
fn type_output() -> TypeInfo {
TypeInfo::Any
}

// The following methods are helpers to use the vectorcall API where possible.
// They are overridden on tuples to perform a vectorcall.
// Be careful when you're implementing these: they can never refer to `Bound` call methods,
// as those refer to these methods, so this will create an infinite recursion.
#[doc(hidden)]
#[inline]
fn __py_call_vectorcall1<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
#[inline]
fn inner<'py>(
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
args: Bound<'py, PyTuple>,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::PyObject_Call(function.as_ptr(), args.as_ptr(), std::ptr::null_mut())
.assume_owned_or_err(py)
}
}
inner(
py,
function,
<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py),
)
}

#[doc(hidden)]
#[inline]
fn __py_call_vectorcall<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
#[inline]
fn inner<'py>(
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
args: Bound<'py, PyTuple>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::PyObject_Call(
function.as_ptr(),
args.as_ptr(),
kwargs.map_or_else(std::ptr::null_mut, |kwargs| kwargs.as_ptr()),
)
.assume_owned_or_err(py)
}
}
inner(
py,
function,
<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py),
kwargs,
)
}

#[doc(hidden)]
#[inline]
fn __py_call_method_vectorcall1<'py>(
self,
_py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
// Don't `self.into_py()`! This will lose the optimization of vectorcall.
object
.getattr(method_name)
.and_then(|method| method.call1(self))
}
}

/// Defines a conversion from a Rust type to a Python object, which may fail.
Expand Down Expand Up @@ -593,52 +505,6 @@ impl IntoPy<Py<PyTuple>> for () {
fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
PyTuple::empty(py).unbind()
}

#[inline]
fn __py_call_vectorcall1<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe { ffi::compat::PyObject_CallNoArgs(function.as_ptr()).assume_owned_or_err(py) }
}

#[inline]
fn __py_call_vectorcall<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
match kwargs {
Some(kwargs) => ffi::PyObject_Call(
function.as_ptr(),
PyTuple::empty(py).as_ptr(),
kwargs.as_ptr(),
)
.assume_owned_or_err(py),
None => ffi::compat::PyObject_CallNoArgs(function.as_ptr()).assume_owned_or_err(py),
}
}
}

#[inline]
#[allow(clippy::used_underscore_binding)]
fn __py_call_method_vectorcall1<'py>(
self,
py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::compat::PyObject_CallMethodNoArgs(object.as_ptr(), method_name.as_ptr())
.assume_owned_or_err(py)
}
}
}

impl<'py> IntoPyObject<'py> for () {
Expand Down
16 changes: 8 additions & 8 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,7 @@ impl<T> Py<T> {
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<PyObject>
where
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
}
Expand All @@ -1509,15 +1509,15 @@ impl<T> Py<T> {
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult<PyObject> {
self.call(py, args, kwargs)
self.call(py, args.into_py(py), kwargs)
}

/// Calls the object with only positional arguments.
///
/// This is equivalent to the Python expression `self(*args)`.
pub fn call1<N>(&self, py: Python<'_>, args: N) -> PyResult<PyObject>
pub fn call1<'py, N>(&self, py: Python<'py>, args: N) -> PyResult<PyObject>
where
N: IntoPy<Py<PyTuple>>,
N: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py).as_any().call1(args).map(Bound::unbind)
}
Expand All @@ -1540,11 +1540,11 @@ impl<T> Py<T> {
py: Python<'py>,
name: N,
args: A,
kwargs: Option<&Bound<'_, PyDict>>,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<PyObject>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
Expand All @@ -1566,7 +1566,7 @@ impl<T> Py<T> {
N: IntoPy<Py<PyString>>,
A: IntoPy<Py<PyTuple>>,
{
self.call_method(py, name.into_py(py), args, kwargs)
self.call_method(py, name.into_py(py), args.into_py(py), kwargs)
}

/// Calls a method on the object with only positional arguments.
Expand All @@ -1578,7 +1578,7 @@ impl<T> Py<T> {
pub fn call_method1<'py, N, A>(&self, py: Python<'py>, name: N, args: A) -> PyResult<PyObject>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
Expand Down
73 changes: 38 additions & 35 deletions src/types/any.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::class::basic::CompareOp;
use crate::conversion::{private, AsPyPointer, FromPyObjectBound, IntoPy, IntoPyObject};
use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPyObject};
use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult};
use crate::exceptions::{PyAttributeError, PyTypeError};
use crate::ffi_ptr_ext::FfiPtrExt;
Expand All @@ -11,7 +11,7 @@ use crate::type_object::{PyTypeCheck, PyTypeInfo};
#[cfg(not(any(PyPy, GraalPy)))]
use crate::types::PySuper;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{err, ffi, Borrowed, BoundObject, Py, Python};
use crate::{err, ffi, Borrowed, BoundObject, Python};
use std::cell::UnsafeCell;
use std::cmp::Ordering;
use std::os::raw::c_int;
Expand Down Expand Up @@ -435,9 +435,9 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// })
/// # }
/// ```
fn call<A>(&self, args: A, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Bound<'py, PyAny>>
fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
where
A: IntoPy<Py<PyTuple>>;
A: IntoPyObject<'py, Target = PyTuple>;

/// Calls the object without arguments.
///
Expand Down Expand Up @@ -492,7 +492,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
where
A: IntoPy<Py<PyTuple>>;
A: IntoPyObject<'py, Target = PyTuple>;

/// Calls a method on the object.
///
Expand Down Expand Up @@ -535,11 +535,11 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
&self,
name: N,
args: A,
kwargs: Option<&Bound<'_, PyDict>>,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>;
A: IntoPyObject<'py, Target = PyTuple>;

/// Calls a method on the object without arguments.
///
Expand Down Expand Up @@ -615,7 +615,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>;
A: IntoPyObject<'py, Target = PyTuple>;

/// Returns whether the object is considered to be true.
///
Expand Down Expand Up @@ -1245,15 +1245,30 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
}

fn call<A>(&self, args: A, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Bound<'py, PyAny>>
fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
where
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
args.__py_call_vectorcall(
self.py(),
self.as_borrowed(),
kwargs.map(Bound::as_borrowed),
private::Token,
fn inner<'py>(
any: &Bound<'py, PyAny>,
args: Borrowed<'_, 'py, PyTuple>,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::PyObject_Call(
any.as_ptr(),
args.as_ptr(),
kwargs.map_or(std::ptr::null_mut(), |dict| dict.as_ptr()),
)
.assume_owned_or_err(any.py())
}
}

let py = self.py();
inner(
self,
args.into_pyobject(py).map_err(Into::into)?.as_borrowed(),
kwargs,
)
}

Expand All @@ -1264,29 +1279,24 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {

fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
where
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
args.__py_call_vectorcall1(self.py(), self.as_borrowed(), private::Token)
self.call(args, None)
}

#[inline]
fn call_method<N, A>(
&self,
name: N,
args: A,
kwargs: Option<&Bound<'_, PyDict>>,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
// Don't `args.into_py()`! This will lose the optimization of vectorcall.
match kwargs {
Some(_) => self
.getattr(name)
.and_then(|method| method.call(args, kwargs)),
None => self.call_method1(name, args),
}
self.getattr(name)
.and_then(|method| method.call(args, kwargs))
}

#[inline]
Expand All @@ -1305,16 +1315,9 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPy<Py<PyTuple>>,
A: IntoPyObject<'py, Target = PyTuple>,
{
args.__py_call_method_vectorcall1(
self.py(),
self.as_borrowed(),
name.into_pyobject(self.py())
.map_err(Into::into)?
.as_borrowed(),
private::Token,
)
self.call_method(name, args, None)
}

fn is_truthy(&self) -> PyResult<bool> {
Expand Down
Loading

0 comments on commit b56806a

Please sign in to comment.