Skip to content
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

fixes #3325 -- mark AsPyPointer as unsafe trait #3358

Merged
merged 1 commit into from
Sep 4, 2023

Conversation

alex
Copy link
Contributor

@alex alex commented Jul 30, 2023

No description provided.

guide/src/migration.md Outdated Show resolved Hide resolved
@@ -58,7 +58,9 @@ use std::ptr::NonNull;
/// and the Python object is dropped immediately after the `0xabad1dea_u32.into_py(py).as_ptr()`
/// expression is evaluated. To fix the problem, bind Python object to a local variable like earlier
/// to keep the Python object alive until the end of its scope.
pub trait AsPyPointer {
///
/// Implementors must ensure this returns a valid pointer to a Python object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this suffice? Do we want to demand some relation to the &self parameter, e.g. that the resulting must be derived from and only from &self to avoid implementations returning completely unrelated pointers?

As a more general observation. Do we need this in generic context? As an alternative would an inherent PyAny::as_ptr combined with Deref<Target=PyAny> not serve the same purpose without inventing a new contract via this trait?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this probably has a further requirement on the refcount (notably that this borrows a reference from self, whereas into shoudl be an owned reference). I'll update.

I'm not sure to what extent we could use the deref in place of this trait.

Copy link
Member

@adamreichold adamreichold Jul 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to what extent we could use the deref in place of this trait.

My idea is to go from &self to &PyAny via Deref<Target=PyAny> which is unambiguously safe due to the validity requirements of &PyAny. From there we can go to *mut ffi::PyObject via self.0.get() without relying on extra contracts which must be upheld by implementors.

I think the main question is whether we use this in any situation where Deref<Target=PyAny> does not apply. I would be surprised if we did tough, because returning a valid pointer to a Python object basically means I can construct a sufficiently short-lived &PyAny from that pointer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think PyAny::is and also __traverse__ are the only public APIs which immediately come to my mind, but we can find nice solutions to those I'm sure.

I would definitely like to start by seeing how far adding the inherent methods to PyAny and Py<T> gets us, and see if there's much work after that to replace and deprecate the traits entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. I'm not going to have time to work on it today. My suggestion would be: I think this makes sense to merge as is, and then looking to minimize and deprecate the usage of these traits is really orthagonal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #3359 as a starting point.

My only reason to wait on merging this PR here is that if we can get to a point where the traits are deprecated in 0.20 then I'd prefer not to change them at all before that release.

@alex
Copy link
Contributor Author

alex commented Aug 14, 2023

At this point here are the functions that take impl AsPyPointer as an argument:

I think the first four can be straightly switched to something else (e.g. IntoPy<PyObject>) and then we can call the inherent method.

The conversion impls can likely be removed or replaced with impls for each of the concrete types.

PyVisit::call is the one I'm least confident about. I don't think using IntoPy<PyObject> is quite right, since we don't really want to call back to random code there, which many implementors do. Any thoughts?

@davidhewitt
Copy link
Member

PyAny::is and Py::is don't dereference the pointer, so I believe they are safe without adding constraints to AsPyPointer. We could consider leaving the trait and use it just for them. To deprecate the trait they could be changed to take &PyAny (breaking change) or some trait implemented for &PyAny and &Py<T>.

PyIterator::from_object and PyByteArray::from should probably just take a single argument &'py PyAny rather than py: Python<'py> plus AsPyPointer. That's breaking but a consistency improvement anyway. I also suggest changing PyDict::from_sequence at the same time, which currently takes py: Python<'py> and PyObject.

Agreed re the conversion impls.

PyVisit::call will need to be usable without the GIL, so definitely can't use IntoPy<PyObject>. Because it doesn't have access to the GIL I think the only types which will be passed at that callsite will be &Py<T> and &Option<Py<T>>, so I suggest we deprecate call and add PyVisit::visit(&Py<T>) and PyVisit::visit_option(&Option<T>). (That's breaking enough that I think it warrants a deprecation of at least one release.)

@@ -56,13 +56,15 @@ use std::ptr::NonNull;
/// and the Python object is dropped immediately after the `0xabad1dea_u32.into_py(py).as_ptr()`
/// expression is evaluated. To fix the problem, bind Python object to a local variable like earlier
/// to keep the Python object alive until the end of its scope.
pub trait AsPyPointer {
///
/// Implementors must ensure this returns a valid pointer to a Python object, which borrows a reference count from `&self`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pointer may also be null, like in the case of the implementation of Option<T>

@alex
Copy link
Contributor Author

alex commented Aug 18, 2023

Realizing that I missed one on my original list: impl FromPyObject for Py<T>, which I'm having trouble writing without AsPyPointer, would love some thoughts.

@alex alex changed the title fixes #3325 -- mark AsPyPointer and IntoPyPointer as unsafe trait fixes #3325 -- mark AsPyPointer as unsafe trait Aug 18, 2023
@adamreichold
Copy link
Member

Realizing that I missed one on my original list: impl FromPyObject for Py<T>, which I'm having trouble writing without AsPyPointer, would love some thoughts.

This handles the different between going for &T and &PyCell<T> as the target, right? Maybe is #2956 helpful to resolve this?

@alex
Copy link
Contributor Author

alex commented Aug 18, 2023

It handles going from &PyAny to Py<T>, I'm not positive if that PR would help or not.

@davidhewitt
Copy link
Member

Hmm I had a go in #3406 which looks a possible route.

@davidhewitt
Copy link
Member

Given we're not sure about the decision in #3406, I'm beginning to warm to the idea to merging this now and releasing 0.20 with the remaining points addressed by the unsafe bound. We can then make further progress in removing this trait with the API reworks of 0.21?

@alex
Copy link
Contributor Author

alex commented Sep 3, 2023

That works for me.

@davidhewitt
Copy link
Member

davidhewitt commented Sep 4, 2023

Ok, let's do that. I'll create a follow up issue to finish the removal of this trait as we go.

@davidhewitt davidhewitt added this pull request to the merge queue Sep 4, 2023
@davidhewitt davidhewitt mentioned this pull request Sep 4, 2023
4 tasks
Merged via the queue into PyO3:main with commit e67b283 Sep 4, 2023
31 checks passed
@alex alex deleted the ptr-unsafe-trait branch September 4, 2023 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants