Calling Python functions

The Bound<'py, T> smart pointer (such as Bound<'py, PyAny>, Bound<'py, PyList>, or Bound<'py, MyClass>) can be used to call Python functions.

PyO3 offers two APIs to make function calls:

  • call - call any callable Python object.
  • call_method - call a method on the Python object.

Both of these APIs take args and kwargs arguments (for positional and keyword arguments respectively). There are variants for less complex calls:

For convenience the Py<T> smart pointer also exposes these same six API methods, but needs a Python token as an additional first argument to prove the GIL is held.

The example below calls a Python function behind a PyObject (aka Py<PyAny>) reference:

use pyo3::prelude::*;
use pyo3::types::PyTuple;

fn main() -> PyResult<()> {
    let arg1 = "arg1";
    let arg2 = "arg2";
    let arg3 = "arg3";

    Python::with_gil(|py| {
        let fun: Py<PyAny> = PyModule::from_code_bound(
            py,
            "def example(*args, **kwargs):
                if args != ():
                    print('called with args', args)
                if kwargs != {}:
                    print('called with kwargs', kwargs)
                if args == () and kwargs == {}:
                    print('called with no arguments')",
            "",
            "",
        )?
        .getattr("example")?
        .into();

        // call object without any arguments
        fun.call0(py)?;

        // pass object with Rust tuple of positional arguments
        let args = (arg1, arg2, arg3);
        fun.call1(py, args)?;

        // call object with Python tuple of positional arguments
        let args = PyTuple::new_bound(py, &[arg1, arg2, arg3]);
        fun.call1(py, args)?;
        Ok(())
    })
}

Creating keyword arguments

For the call and call_method APIs, kwargs are Option<&Bound<'py, PyDict>>, so can either be None or Some(&dict). You can use the IntoPyDict trait to convert other dict-like containers, e.g. HashMap or BTreeMap, as well as tuples with up to 10 elements and Vecs where each element is a two-element tuple.

use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use std::collections::HashMap;

fn main() -> PyResult<()> {
    let key1 = "key1";
    let val1 = 1;
    let key2 = "key2";
    let val2 = 2;

    Python::with_gil(|py| {
        let fun: Py<PyAny> = PyModule::from_code_bound(
            py,
            "def example(*args, **kwargs):
                if args != ():
                    print('called with args', args)
                if kwargs != {}:
                    print('called with kwargs', kwargs)
                if args == () and kwargs == {}:
                    print('called with no arguments')",
            "",
            "",
        )?
        .getattr("example")?
        .into();

        // call object with PyDict
        let kwargs = [(key1, val1)].into_py_dict_bound(py);
        fun.call_bound(py, (), Some(&kwargs))?;

        // pass arguments as Vec
        let kwargs = vec![(key1, val1), (key2, val2)];
        fun.call_bound(py, (), Some(&kwargs.into_py_dict_bound(py)))?;

        // pass arguments as HashMap
        let mut kwargs = HashMap::<&str, i32>::new();
        kwargs.insert(key1, 1);
        fun.call_bound(py, (), Some(&kwargs.into_py_dict_bound(py)))?;

        Ok(())
    })
}

During PyO3's migration from "GIL Refs" to the Bound<T> smart pointer, Py<T>::call is temporarily named Py<T>::call_bound (and call_method is temporarily call_method_bound).

(This temporary naming is only the case for the Py<T> smart pointer. The methods on the &PyAny GIL Ref such as call have not been given replacements, and the methods on the Bound<PyAny> smart pointer such as Bound<PyAny>::call already use follow the newest API conventions.)