-
Notifications
You must be signed in to change notification settings - Fork 778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contribution opportunity: improved conversion traits #4041
Comments
One point to consider is whether I guess one downside would be the identity conversion of a Python type to itself (or another python type?), which would have to perform a (relatively cheap) reference count increase (which would most likely be happening in generic code). |
Note |
As with e.g. |
Any particular reason to fix the |
That makes sense, I agree with that. (I always forget that you can implement traits on references as well 🤦 )
I think it could be nice to support this. For example a |
This is what I was thinking too. I think
This is an interesting idea, and it would probably make migration easier than trying to refactor to solve existing One case related to "generic overloads" worth mentioning is |
A reason against the generic overloads is type inference. Assuming the function is Examples to consider is e.g. I'm assuming that all types usable for Currently for |
True, and type hints for generic traits are always a bit awkward, because one can't use a turbo fish on the method (and thus it often interrupts method chains).
Hmm, I can see how this can be surprising. I guess this is a general problem of the generic case. If there is more than one implementation of
This might not be necessarily needed, because |
Potentially, yes. My worry is that if a type has two implementations of |
Couldn't we provide an extension trait to make the turbo fish available, e.g. trait IntoPyObjectExt {
fn into_pyobject<T, E>(&self) -> Result<Bound<'py, T>, E> where Self: IntoPyObject<Target=T, Err=E>;
}
impl<T> IntoPyObjectExt for T {
fn into_pyobject<T, E>(&self) -> Result<Bound<'py, T>, E> where Self: IntoPyObject<Target=T, Err=E> {
<self as &IntoPyObject>::into_pyobject_impl(self)
}
} That one probably does not even compile, but I think some way of shuffling the generic parameters around should be available? Other than that, I don't think this particularly worse than e.g. |
Hey, while working on porting the trait bounds of our diff --git a/src/types/mapping.rs b/src/types/mapping.rs
index 537959719..84fa577df 100644
--- a/src/types/mapping.rs
+++ b/src/types/mapping.rs
@@ -189,19 +189,20 @@ mod tests {
use crate::{exceptions::PyKeyError, types::PyTuple};
use super::*;
+ use crate::conversion::IntoPyObject;
#[test]
fn test_len() {
Python::with_gil(|py| {
let mut v = HashMap::new();
- let ob = v.to_object(py);
- let mapping = ob.downcast_bound::<PyMapping>(py).unwrap();
+ let ob = (&v).into_pyobject(py).unwrap();
+ let mapping = ob.downcast::<PyMapping>().unwrap();
assert_eq!(0, mapping.len().unwrap());
assert!(mapping.is_empty().unwrap());
v.insert(7, 32);
- let ob = v.to_object(py);
- let mapping2 = ob.downcast_bound::<PyMapping>(py).unwrap();
+ let ob = v.into_pyobject(py).unwrap();
+ let mapping2 = ob.downcast::<PyMapping>().unwrap();
assert_eq!(1, mapping2.len().unwrap());
assert!(!mapping2.is_empty().unwrap());
}); Unfortunately I get a rather puzzling compile error:
I don't really have an idea why The question is now, can anyone think of a workaround here (or maybe there is something wrong with these impl that I didn't see). I already tried for a bit now, but could not make anything work so far. It would be rather unfortunate if we could not provide these reference impls. |
I suppose the problem is that type inference searches for types which might satisfy I feel like it's definitely a compiler bug and that we worked around a similar overflow with |
I assume that annotating the hashmap fixes the problem? |
Hmm, I will look into that, but the advantage with
Actually no, it still does not pickup the impl and wants me to clone that map... |
There was an error in the impl, the I will provide a PR shortly to fix the bounds on the hash impls. diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs
index f19b5671d..e16381505 100644
--- a/src/conversions/std/map.rs
+++ b/src/conversions/std/map.rs
@@ -76,7 +76,7 @@ impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap<K, V, H>
where
&'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
&'a V: IntoPyObject<'py>,
- &'a H: hash::BuildHasher,
+ H: hash::BuildHasher,
PyErr: From<<&'a K as IntoPyObject<'py>>::Error> + From<<&'a V as IntoPyObject<'py>>::Error>,
{ |
Progress
|
Additional thought on |
Interesting thought, I'll play around with the idea! I think it should be possible to reuse nearly everything of the current derive. I believe there are only real assumption made about the |
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.
FromPyObject
ToPyObject
andIntoPy<PyObject>
(and sub-formsIntoPy<Py<PyTuple>>
andIntoPy<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 haveFromPyObjectBound
which allows a few additional optimisations and extractions like&str
at cost of complexity. We think the two lifetimes ofFromPyObjectBound
will be the future design ofFromPyObject
, 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
:PyErr
as the error type, we could do something similar tostd::str::FromStr
and add atype Err
associated type. This would allow implementations to use just downcasting errors, or other concrete Rust errors without having to go through the relatively heavyPyErr
machinery.FromPyObject
could have anextract_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. IfFromPyObject
is staying around, I thinkIntoPyObject
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, asFromPyObject
is also fallible.Maybe the following is closer to the right definition:
... but that's just a sketch, and discussion / research / prototyping would yield valuable insight.
The text was updated successfully, but these errors were encountered: