-
Notifications
You must be signed in to change notification settings - Fork 136
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
Add utility to share rust reference across Python objects #189
Conversation
Note that my original blog post is out of date and contains a few errors that I should really find time to fix. Not that it matters much for this PR. |
I started to read this, but I'm far from being done. @markbt @dgrunwald how do you think we should proceed? @yuja Do you think it could be of use outside of |
That's probably unrelated topic since we can safely take a lifetime-bound iterator in Rust.
Might be doable, but I don't know if we can easily implement a generic PythonObject wrapper |
I really like this idea, so I'm keen to get it merged. What's the status of the PR? I still see a bunch of TODOs in the code, which would be nice to get resolved. For sure we need to sort out the syntax before we can merge it as it will form part of the API. |
I was planning to get back to it some time in February, once things have hopefully settled down for me (Heptapod…) but if someone gets to it faster, I don't mind. Big +1 in principle, of course. |
Yes. Some ideas:
Do we have any plan to add a syntax to define I have a PoC-level implementation of |
I don't like underscores in keywords, so I would prefer
Arguably the For properties, I'd want something similar. We could prefix
|
For getters I would expect the obvious |
Thanks for the info. I didn't know
+1. Even though I also like the current |
This was originally developed for the Mercurial's Rust extension. It allows us to implement a Python iterator over a Rust iterator _relatively_ safely. The problem is that a Rust iterator typically has a reference to the collection behind, and thus cannot be a data member of a Python object. We get around this problem by casting &'a to &'static and add runtime validation instead. The basic idea is described in the following blog post. https://raphaelgomes.dev/blog/articles/2019-07-01-sharing-references-between-python-and-rust.html In order to make the reference-sharing business safe, we have the following structs: - PySharedRefCell defines a storage holding a value to be shared - PySharedRef is a lifetime-bound reference to the value above, which provides a safe interface on top of the PySharedRefCell and its owner PyObject - UnsafePyLeaked is the reference not bound to the real lifetime, and its validity is checked at runtime - PyLeakedRef/PyLeakedRefMut are borrowed references from UnsafePyLeaked In order to create PySharedRef in safe manner, py_class!() macro extension will be added by later patches: py_class!(class List |py| @shared data vec: Vec<i32>; // the storage type is PySharedRefCell<Vec<i32>>, but its accessor // returns PySharedRef<'a, Vec<i32>>. It makes sure that the right // owner PyObject and its data member are paired. }); There are a couple of unsafe business in this module, and some public functions are unfortunately unsafe. Please refer to the inline comments why some of them can be safe and some can't. Thanks to Georges Racinet for the basic ideas and experiments, Raphaël Gomès for the core implementation, Mark Thomas for the generation-counter idea, and Mercurial developers for reviewing the original patches.
I originally tried to extract a helper function from data_decl(), but it didn't look nice since there were subtle, but many differences between data_decl() and shared_data_decl(). shared_data_decl() will be updated by the subsequent patches.
This mostly encapsulates PySharedRef business. The main reason of introducing a dedicated macro syntax is that we need a safe way to bind the correct PyObject reference to PySharedRefCell data. This is handled as follows: $crate::PySharedRef::new(py, &self._unsafe_inner, data) Here `data` is a PySharedRefCell instance, and `self._unsafe_inner` is the owner PyObject. It's still weird that create_instance() takes PySharedRefCell<T> instead of the inner data type T, which will be fixed by the next patch.
Since the use of PySharedRefCell type is encapsulated, it shouldn't be the type of create_instance() arguments. This patch adds $init_expr and $init_ty to map $init_ty argument to the storage $data_ty object. ($data_name: $init_ty) -> $data_ty { $init_expr }
Added |
This was originally developed for the Mercurial's Rust extension. It allows
us to implement a Python iterator over a Rust iterator relatively safely.
The problem is that a Rust iterator typically has a reference to the
collection behind, and thus cannot be a data member of a Python object.
We get around this problem by casting &'a to &'static and add runtime
validation instead. The basic idea is described in the following blog post.
https://raphaelgomes.dev/blog/articles/2019-07-01-sharing-references-between-python-and-rust.html
In order to make the reference-sharing business safe, we have the following
structs:
a safe interface on top of the PySharedRefCell and its owner PyObject
validity is checked at runtime
In order to create PySharedRef in safe manner, we plan to add py_class!()
macro like this:
The macro extension is not included in this PR since we'll probably need
some bikeshedding.
There are a couple of unsafe business in this module, and some public
functions are unfortunately unsafe. Please refer to the inline comments
why some of them can be safe and some can't.
Thanks to @gracinet for the basic ideas and experiments,
@Alphare for the core implementation,
@markbt for the generation-counter idea,
and Mercurial developers for reviewing the original patches.