⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here

pyo3/
call.rs

1//! Defines how Python calls are dispatched, see [`PyCallArgs`].for more information.
2
3use crate::ffi_ptr_ext::FfiPtrExt as _;
4use crate::types::{PyAnyMethods as _, PyDict, PyString, PyTuple};
5use crate::{ffi, Borrowed, Bound, IntoPyObjectExt as _, Py, PyAny, PyResult};
6
7pub(crate) mod private {
8    use super::*;
9
10    pub trait Sealed {}
11
12    impl Sealed for () {}
13    impl Sealed for Bound<'_, PyTuple> {}
14    impl Sealed for Py<PyTuple> {}
15
16    pub struct Token;
17}
18
19/// This trait marks types that can be used as arguments to Python function
20/// calls.
21///
22/// This trait is currently implemented for Rust tuple (up to a size of 12),
23/// [`Bound<'py, PyTuple>`] and [`Py<PyTuple>`]. Custom types that are
24/// convertable to `PyTuple` via `IntoPyObject` need to do so before passing it
25/// to `call`.
26///
27/// This trait is not intended to used by downstream crates directly. As such it
28/// has no publicly available methods and cannot be implemented ouside of
29/// `pyo3`. The corresponding public API is available through [`call`]
30/// ([`call0`], [`call1`] and friends) on [`PyAnyMethods`].
31///
32/// # What is `PyCallArgs` used for?
33/// `PyCallArgs` is used internally in `pyo3` to dispatch the Python calls in
34/// the most optimal way for the current build configuration. Certain types,
35/// such as Rust tuples, do allow the usage of a faster calling convention of
36/// the Python interpreter (if available). More types that may take advantage
37/// from this may be added in the future.
38///
39/// [`call0`]: crate::types::PyAnyMethods::call0
40/// [`call1`]: crate::types::PyAnyMethods::call1
41/// [`call`]: crate::types::PyAnyMethods::call
42/// [`PyAnyMethods`]: crate::types::PyAnyMethods
43#[cfg_attr(
44    diagnostic_namespace,
45    diagnostic::on_unimplemented(
46        message = "`{Self}` cannot used as a Python `call` argument",
47        note = "`PyCallArgs` is implemented for Rust tuples, `Bound<'py, PyTuple>` and `Py<PyTuple>`",
48        note = "if your type is convertable to `PyTuple` via `IntoPyObject`, call `<arg>.into_pyobject(py)` manually",
49        note = "if you meant to pass the type as a single argument, wrap it in a 1-tuple, `(<arg>,)`"
50    )
51)]
52pub trait PyCallArgs<'py>: Sized + private::Sealed {
53    #[doc(hidden)]
54    fn call(
55        self,
56        function: Borrowed<'_, 'py, PyAny>,
57        kwargs: Borrowed<'_, 'py, PyDict>,
58        token: private::Token,
59    ) -> PyResult<Bound<'py, PyAny>>;
60
61    #[doc(hidden)]
62    fn call_positional(
63        self,
64        function: Borrowed<'_, 'py, PyAny>,
65        token: private::Token,
66    ) -> PyResult<Bound<'py, PyAny>>;
67
68    #[doc(hidden)]
69    fn call_method_positional(
70        self,
71        object: Borrowed<'_, 'py, PyAny>,
72        method_name: Borrowed<'_, 'py, PyString>,
73        _: private::Token,
74    ) -> PyResult<Bound<'py, PyAny>> {
75        object
76            .getattr(method_name)
77            .and_then(|method| method.call1(self))
78    }
79}
80
81impl<'py> PyCallArgs<'py> for () {
82    fn call(
83        self,
84        function: Borrowed<'_, 'py, PyAny>,
85        kwargs: Borrowed<'_, 'py, PyDict>,
86        token: private::Token,
87    ) -> PyResult<Bound<'py, PyAny>> {
88        let args = self.into_pyobject_or_pyerr(function.py())?;
89        args.call(function, kwargs, token)
90    }
91
92    fn call_positional(
93        self,
94        function: Borrowed<'_, 'py, PyAny>,
95        token: private::Token,
96    ) -> PyResult<Bound<'py, PyAny>> {
97        let args = self.into_pyobject_or_pyerr(function.py())?;
98        args.call_positional(function, token)
99    }
100}
101
102impl<'py> PyCallArgs<'py> for Bound<'py, PyTuple> {
103    fn call(
104        self,
105        function: Borrowed<'_, 'py, PyAny>,
106        kwargs: Borrowed<'_, '_, PyDict>,
107        _: private::Token,
108    ) -> PyResult<Bound<'py, PyAny>> {
109        unsafe {
110            ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), kwargs.as_ptr())
111                .assume_owned_or_err(function.py())
112        }
113    }
114
115    fn call_positional(
116        self,
117        function: Borrowed<'_, 'py, PyAny>,
118        _: private::Token,
119    ) -> PyResult<Bound<'py, PyAny>> {
120        unsafe {
121            ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), std::ptr::null_mut())
122                .assume_owned_or_err(function.py())
123        }
124    }
125}
126
127impl<'py> PyCallArgs<'py> for Py<PyTuple> {
128    fn call(
129        self,
130        function: Borrowed<'_, 'py, PyAny>,
131        kwargs: Borrowed<'_, '_, PyDict>,
132        _: private::Token,
133    ) -> PyResult<Bound<'py, PyAny>> {
134        unsafe {
135            ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), kwargs.as_ptr())
136                .assume_owned_or_err(function.py())
137        }
138    }
139
140    fn call_positional(
141        self,
142        function: Borrowed<'_, 'py, PyAny>,
143        _: private::Token,
144    ) -> PyResult<Bound<'py, PyAny>> {
145        unsafe {
146            ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), std::ptr::null_mut())
147                .assume_owned_or_err(function.py())
148        }
149    }
150}