-
Notifications
You must be signed in to change notification settings - Fork 763
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
Shortcut macros for collections #1703
base: main
Are you sure you want to change the base?
Shortcut macros for collections #1703
Conversation
Very cool, thanks for working on this! Let's take some time to get this right; we can merge it after the 0.14 release so that there's plenty of time to make the right compromises.
Ah, that's really frustrating. There is precedent for I'd be interested in hearing some other maintainer's opinions on the design considerations here, as the way they're implemented at the moment is basically exactly the way I suggested in #1463. One final syntax idea I have is that we could consider using py_list!(py => [1, 2, 3]); I saw this in the |
Thank you for such a quick reaction!
Absolutely! Let's try to make this as pleasant to use as possible.
Sure, idents shouldn't be a problem. A proc macro sounds even better, I did not know it was an option here, thanks for the hint! I will see what we can do with it.
Me too! let my_complex_dict = py_dict!(py, {
"username": "admin",
"permissions": py_set!(py, {"read", "write"}).unwrap(),
"default_args": py_tuple!(py, (py_list!(py, [1, "order_id", py_dict!(py, {"meta": py_list!(py, ["abcd", "efg"])}).unwrap()])))
}).unwrap(); compared to python my_complex_dict = {
'default_args': (
[1, 'order_id', {'meta': ['abcd', 'efg']}],
),
'permissions': {'read', 'write'},
'username': 'admin',
} I wonder if we can do better here.
I like it! It makes for a nice distinction between |
Hmm, that's true. There could be some kind of |
I love this, thanks ❤️
I think this is also because nested objects are convoluted.
This sounds pretty complicated to use. I can't say I've ever had a good time making/manipulating complex "stringly typed" structures. So I don't think this is something we should encourage.
People can just define nested Rust structs and derive ToPyObject for it? Is this something we could make a derive macro for? |
Yes definitely. I think at the time that I think you're right that that would be a cleaner way to create a deeply-nested object.
Just noticed this item. Do we expect many errors here? It might be nice to panic on invalid input to avoid the error handling, but it depends on how easy it is to write invalid code. |
I agree, creating deeply nested objects is probably not the intended use of these macros. I really like the
I was thinking about passing unhashable objects as dict keys or set elements. I'm not sure how common that might be, so I made |
That's a fair point. I still think that even if users make an error here, it's pretty much hard-coded into the program with the macro, so it'll be fixed pretty quick after they see a panic.
To avoid suprising users I would suggest making either all of the macros of none of them return I'm not particularly convinced we would need non-panicking |
Also - prior art on complex "collections" is the Though there is also the |
I'm in favor or the non-panicking variant - the case of unhashable objects is one of those instances where everything works perfectly in tests, and then somebody passes an unexpected object, and poof. Let the user handle that case and panic if they want. It's not like memory allocation where you can consistently expect a panic from Rust. (I also think py_run! should not panic; it's pretty useful but hampered by that behavior.) |
- extract common logic into py_object_vec!
a3c4702
to
85b8dbc
Compare
Update: I took a long break, but now I'm back! I have recently figured out how to allow expressions in dict literal macros, thanks to @dtolnay's awesome proc-macro workshop and dtolnay/syn#704. Python::with_gil(|py| {
let my_dict = py_dict!(py => {"KeY".to_lowercase(): 7 + 10}).unwrap();
py_run!(py, my_dict, "assert my_dict['key'] == 17");
}) I have also implemented the The code for |
Awesome! I've been quite busy recently so don't wait for my review, although I will definitely try to give one soon.
Wow, I didn't even realise this was valid Python syntax! I confirmed it is indeed 😅 >>> {"KeY".lower(): 7 + 10}
{'key': 17}
Yeah, I was thinking on this. The awesome thing about macros is that actually we can support both. As long as it's clearly documented, this seems ok to me. Something like: py_dict!{ key: value } // Assumes `py: Python` is already in scope
py_dict!( other_py => { key: value }) // To pass `other_py: Python` explicitly I'd be interested to hear whether everyone else thinks that's an acceptable idea, or if we should aim to choose just one syntax. I think if we decided to pick just one, I'd go for |
It didn't even come to me, thanks for pointing that out! Let's see what others think of it :) |
I prefer the original syntax, e.g.
I agree
What kind of errors do you get if you do this but |
Hi everyone! I'm sorry for the lack of progress and updates here. 2022 was an awful year for me, I had to move countries, and I didn't really have time time to finish this PR. I don't think that I'll be able to finish this anytime soon, so if anyone wants to pick this up, I'll be happy to share the existing code and findings. If not, that's fine too, I think I'll get back to it eventually, but I can't really say when. |
@daniil-konovalenko no problem, hope things settle down soon for you and family. I have had this PR in the back of my mind, I think I'd like to create a |
This is an implementation of #1463
It currently provides the following macros:
py_list!(py, [1, 2, 3]) → &PyList
py_tuple!(py, (1, 2, 3)) → &PyTuple
py_dict!(py, {"key": value}) → PyResult<&PyDict>
py_set!(py, {"item1", "item2", 1, 2, 3}) → PyResult<&PySet>
py_frozenset!(py, {"item1", "item2", 1, 2, 3}) → PyResult<&PyFrozenSet>
py_dict!
only supports literals as keys because we use colons:
as key-value separators, and colons are not allowed afterexpr
fragments. If we would like to use expressions as keys, we have to use=>
as a separator.TODO:
:
vs=>
for key-value separator