-
-
Notifications
You must be signed in to change notification settings - Fork 28
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
Support serialising to named mappings #62
Conversation
@davidhewitt what are your thoughts on this? |
@davidhewitt I apologise for pinging again - I'm hoping that this PR could move forward soon-ish as I'm working on publishing a crate of mine (which would need a feature similar to this) for a publication. |
@juntyr Perhaps he unfollowed this repository or pull request. Maybe try pinging him on X, for example, as he has a few social links on his GitHub profile |
Hi @juntyr sorry for the delay! I have seen this PR but haven't had a chance to investigate yet. Please hold on and I'll do my best to get to this soon - I'm trying to wrap up PyO3 0.22 and then let's aim to ship this in |
@davidhewitt Congrats on shipping pyo3 v0.22, I'm excited to start using it! I'm looking forward to collaborating with you to push this PR forward :) |
23565b6
to
a95c5fe
Compare
The merge conflict should now be resolved again :) |
Once #66 is merged, I will very quickly rebase this PR again so that we can hopefully still get it into v0.22 |
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.
Thanks very much for this and sorry for the slow reply. Yes let's get this into 0.22. Can you please add CHANGELOG entries too?
I think overall I'm 👍 for this and understand the motivation, however want to just discuss a couple of the design choices especially with the intention to avoid performance overhead.
src/ser.rs
Outdated
pub trait PythonizeDictType { | ||
/// Constructor | ||
fn create_mapping(py: Python) -> PyResult<Bound<PyMapping>>; | ||
|
||
/// Constructor | ||
fn create_mapping_with_items< | ||
K: ToPyObject, | ||
V: ToPyObject, | ||
U: ExactSizeIterator<Item = (K, V)>, | ||
>( | ||
py: Python, | ||
items: impl IntoIterator<Item = (K, V), IntoIter = U>, | ||
) -> PyResult<Bound<PyMapping>> { | ||
let mapping = Self::create_mapping(py)?; | ||
|
||
for (key, value) in items { | ||
mapping.set_item(key, value)?; | ||
} | ||
|
||
Ok(mapping) | ||
} | ||
|
||
/// Constructor, allows the mappings to be named | ||
fn create_mapping_with_items_name< | ||
'py, | ||
K: ToPyObject, | ||
V: ToPyObject, | ||
U: ExactSizeIterator<Item = (K, V)>, | ||
>( | ||
py: Python<'py>, | ||
name: &str, | ||
items: impl IntoIterator<Item = (K, V), IntoIter = U>, | ||
) -> PyResult<Bound<'py, PyMapping>> { | ||
let _name = name; | ||
Self::create_mapping_with_items(py, items) | ||
} | ||
} |
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.
I have a couple of thoughts here:
- I wonder, is the split between
create_mapping_with_items
andcreate_mapping_with_items_name
a bit confusing? Should we potentially instead model this by havingPythonizeTypes
have aNamedMap
type to handle the named cases? - I slightly worry about the performance regression here by adding an intermediate
Vec
. I think the way to solve this would be to replacecreate_mapping
withcreate_builder(len)
, which returns an associated type implementingtrait MappingBuilder { fn push_item(k, v); fn finish() -> Bound<'py, PyMapping> }
. For the default dict implementation, the builder is then just a thin wrapper around the dict, in the named tuple case the builder can do the pre-collecting into a Vec.
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.
I implemented both suggestions. I generally like both, even though they are more infective than the previous change. Perhaps you have some suggestions for how to further improve them?
a95c5fe
to
28f7bec
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #62 +/- ##
==========================================
- Coverage 83.83% 83.41% -0.42%
==========================================
Files 3 3
Lines 1169 1224 +55
Branches 1169 1224 +55
==========================================
+ Hits 980 1021 +41
- Misses 140 144 +4
- Partials 49 59 +10 ☔ View full report in Codecov by Sentry. |
GATs were only stabilised in 1.65 but pythonize uses 1.63 … so the builder can’t use a GAT. Perhaps I can inline the builder methods into the main trait itself (hopefully while keeping Map = PyDict) |
I also tried to add a small convenience wrapper type
|
4972787
to
dade974
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.
Looking great, thanks for the repeated rounds and sorry again for the delays!
Just a few finishing touches...
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 perfect, thanks! One final small thing to check, and once we've agreed on that, let's merge 👍
The adapter is anyways constructed through the builder method Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Thanks for your review @davidhewitt! |
I’m unsure if the coverage can be easily improved as all new misses seem to be partial and short-circuiting related |
Agreed, I'm very happy to call this as good as it gets! Thanks again 🚀 |
This PR includes four changes:
PythonizeListType
forPyTuple
, a small convenience that cannot be achieved outside the crateI have found them to be very useful to serialise to and from class-like Python types (e.g.
namedtuple
which keep more type information around).