Skip to content

Contribution opportunity: improved conversion traits #4041

@davidhewitt

Description

@davidhewitt

Following the introduction of the Bound API in 0.21, we have converged on three primary traits which are used to convert Rust datatypes to Python.

  • one trait for from-Python conversions: FromPyObject
  • two traits for to-Python conversions: ToPyObject and IntoPy<PyObject> (and sub-forms IntoPy<Py<PyTuple>> and IntoPy<Py<PyString>>)

There are several opportunities to contribute to PyO3 with a goal of improving these traits. Given that these traits are so central to PyO3's API, they impact developer ergonomics and performance. The right designs could improve PyO3 a lot, though any new API would need a migration pathway which is feasible for users.

Improving FromPyObject

FromPyObject is already in a migration state as its input argument is changing from a GIL Ref to a Bound smart pointer. We also have FromPyObjectBound which allows a few additional optimisations and extractions like &str at cost of complexity. We think the two lifetimes of FromPyObjectBound will be the future design of FromPyObject, though we don't yet know how that will interact with #[derive(FromPyObject)].

Beyond resolving that question, I am aware of at least two other possibilities to refine FromPyObject:

  • Rather than returning PyErr as the error type, we could do something similar to std::str::FromStr and add a type Err associated type. This would allow implementations to use just downcasting errors, or other concrete Rust errors without having to go through the relatively heavy PyErr machinery.
  • In Review conversions and ensure consistency #3226 and similar issues we have discussed how there is a possibility to define either "strict" or "lax" conversions. Maybe FromPyObject could have an extract_exact method added, which defines strict conversions, so that the #[pyfunction] macros can have a #[pyo3(exact)] annotation on arguments to control this better.

Improving to-Python traits

I'm reasonably convinced that there doesn't need to be two (or four, depending how you see it) to-Python traits for PyO3.

In addition, we have a strong need to have a fallible conversion, because there are now several panics inside PyO3 which are caused by the limitation that none of these traits can fail.

I once proposed an IntoPyObject trait in #2316. If FromPyObject is staying around, I think IntoPyObject is the right name to pair with it (though maybe we just introduce a new trait for each direction, for easier migration). I think the design in #2316 is outdated and also it probably should be fallible, as FromPyObject is also fallible.

Maybe the following is closer to the right definition:

trait IntoPyObject {
    type Target;
    type Err;

    fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Err>;
}

... but that's just a sketch, and discussion / research / prototyping would yield valuable insight.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions