-
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
Allow more methods to take interned arguments #2312
Conversation
mejrs
commented
Apr 17, 2022
•
edited
Loading
edited
- I have yet to add some docs to these methods
- Changelog
impl<'a> IntoPy<Py<PyString>> for &'a str { | ||
#[inline] | ||
fn into_py(self, py: Python<'_>) -> Py<PyString> { | ||
PyString::new(py, self).into() | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a (mild) breaking change because of inference errors, but not too bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand things correctly: The comment is informational as the other changes are breaking anyway and this is therefore targeting 0.17.0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the previous instance into PyObject even useful anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the previous instance into PyObject even useful anymore?
I think so as Py<PyString>
does not automatically "decay" into Py<PyAny>
and the existing impl might be used in generic context to produce PyObject
for stashing away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easy to get PyObject from a Py though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easy to get PyObject from a Py though.
I think that in generic context, one needs to be able to express this in a single trait bound, e.g. calling into_py
twice or into_py
followed by into
to go from T
to Py<S>
to PyObject
is not necessarily the same as going from T
to PyObject
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand things correctly: The comment is informational as the other changes are breaking anyway and this is therefore targeting 0.17.0?
Mainly it means that things like let thing = "thing".into_py(py);
stop compiling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid making lots of annoying inference errors we could potentially combine this PR with #2316, which would enable us to select on types which have IntoPyObject<Target = PyString>
instead of IntoPy<Py<PyString>>
.
Very nice! |
eb814f4
to
5154778
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
One remaining concern that I see is that we have made several methods "trivially generic" which were not generic before. Should we aim to reduce code bloat and factor out the non-generic parts into inner help functions? On the other hand, the functions in question do not appear to be huge, so I am unsure whether this would be worth the extra effort.
They are pretty small, I think it's fine.
Done 👍 |
Looks good to me too. The only question I have outstanding is whether we want to merge this now or wait until we can also merge with #2316 so that we don't cause users to go through extra churn of type inference failures in the interim. (FWIW I have a bad feeling that #2316 gets a little upset with some of the more gnarly |
I'm feeling that what we're doing with traits is a bit ad-hoc. Maybe we need to sit down and think about what we don't like, what we would like, and what a good path to getting there is. |
I agree very much with this sentiment. I'll write my current backlog of ideas up in a separate issue as a starting point. As for this PR... looks good to me! The concern I have with merging before we make a decision on #2316 is that this may cause users to have to add |
unsafe { | ||
let args = args.into_py(py).into_ptr(); | ||
let kwargs = kwargs.into_ptr(); | ||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name.to_object(py).as_ptr()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry this is a bit messy since resolving the merge conflict.
How do you feel about:
pyo3::ffi::Something(name.to_object(py).as_ptr());
versus
let ptr = name.to_object(py).into_ptr();
pyo3::ffi::Something(ptr);
ffi::Py_DECREF(ptr);
(or something else?)
Personally I prefer the latter. IMO the former is error prone and gives a bad example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you feel the former is error prone? I think the second is more error prone because missing the Py_DECREF
call is leaking memory just like missing free()
in C.
Perhaps a middle ground is to make it obvious we're using RAII through Py
?
let name: PyObject = name.to_object(py);
pyo3::ffi::Something(name.as_ptr());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you feel the former is error prone? I think the second is more error prone because missing the
Py_DECREF
call is leaking memory just like missingfree()
in C.
I'm mostly worried about someone reading it and and writing it slightly differently:
let ptr = name.to_object(py).as_ptr();
pyo3::ffi::Something(ptr);
which produces a dangling pointer, as the python object is dropped at the end of the statement (I should add documentation about this).
Perhaps a middle ground is to make it obvious we're using RAII through
Py
?let name: PyObject = name.to_object(py); pyo3::ffi::Something(name.as_ptr());
Sure 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'm mostly worried about someone reading it and and writing it slightly differently:
This is an excellent point which I'd totally missed. IMO the middle ground above is probably the only sensible way to arrange this then. If you like, I'll submit a PR to fix the whole codebase to use this pattern once this PR merged? That way you can just worry about the modified functions here without another merge conflict.
I think that's fine, it's breaking either way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's head towards merging this in 0.17 and maybe thinking about IntoPyObject
for 0.18. I'll try to write up my thoughts on traits tomorrow.
Got to confess I'm really not a fan of calling Py_DECREF
or Py_XDECREF
whenever we can just let Py
do it in Drop
implementation.
unsafe { | ||
let args = args.into_py(py).into_ptr(); | ||
let kwargs = kwargs.into_ptr(); | ||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name.to_object(py).as_ptr()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you feel the former is error prone? I think the second is more error prone because missing the Py_DECREF
call is leaking memory just like missing free()
in C.
Perhaps a middle ground is to make it obvious we're using RAII through Py
?
let name: PyObject = name.to_object(py);
pyo3::ffi::Something(name.as_ptr());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks great, thanks very much for all the tidy ups!
A final thought: as we're committing to merging this and producing extra inference errors in PyO3 0.17, can we please add a section to migration.md
to explain this to users?
Sure. I'm also concerned about some missing codecov here, so I want to add a couple of tests as well. |
Should be done now :) |
Thanks for merging, looks great, sorry I had a busy last few days and didn't get around to looking at this sooner. 👍 |