-
-
Notifications
You must be signed in to change notification settings - Fork 14
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
binding(Python): Use Python classes for codec messages #559
Comments
I think this is a reasonable next step towards a pythonic API. |
I feel like the current Python side of type hints (pr #568) could still use a little more depth, and the internal structure is still a bit mysterious. I'm not super familiar with Rust and PyO3, so I read the PyO3 documentation, but I couldn't find a way to export fields from a struct with a lifecycle in Rust to Python via PyO3. The PyO3 documentation states that #[pyclass(name = "Greeting", eq, get_all, set_all)] //Err: `get` and `set` with tuple struct fields require `name`
pub(crate) struct PyGreeting(pub(crate) Greeting<'static>); So I gave it a go on my own fork, trying to convert the types in |
@AzideCupric Thank you for your input!
Could you elaborate a bit more on this? Then we can guide further development in the right direction with respect to developer experience. Currently, we aim at providing some basic way of using the capabilities of
Indeed this seems to be one of the largest issues for improving the Python experience for the types. Because Going forward, it might be possible to change automatically switch the lifetime parameters of the types in
I really like the idea of using Pydantic to create a verifyable model. However, looking at your implementation for greeting, wouldn't this basically lead to a complete reimplementation of |
Thank you for your patience.
I am trying to implement an Email adapter for the NoneBot project. To do this, I am looking for a Python library that can effectively decode and encode IMAP protocol commands and responses. When I found For instance, when the server returns a response like:
I would like it to parse into Python classes: # Now:
{'Data': {'Exists': 3}}
{'Data': {'Expunge': 2}}
{'Status': {'Tagged': {'tag': 'A002', 'body': {'kind': 'Ok', 'code': None, 'text': 'IDLE terminated'}}}}
# Want:
Data(data=Exist(3))
Data(data=Expunge(2))
Status(status=Tagged(
tag='A002',
body=Body(
kind="Ok",
code=None,
text="IDLE terminated"
)
)) This approach would allow using match-case to handle different results and benefit from type-checking tools (like pyright) for type inference. match ResponseCodec.decode(...):
case Status() as s:
print(s.body.kind, s.body.text)
case Data(data=d):
match d:
case Exists() as e:
emit_event("new_email", e.exists)
case Expunge() as e:
emit_event("del_email", e.expunge)
case _:
...
case _:
... While pure dicts can also use match-case: match ResponseCodec.decode(...):
case {"Status": s}:
...
case {"Data": d}:
match d:
case {"Exists": e}:
...
case {"Expunge": e}:
...
case _:
...
case _:
... This requires manually entering key values, which has the risk of typos/structural errors, and type-checking tools can't infer the type of Additionally, using classes makes it easier to construct a command to pass to the cmd = {'tag': 'a002', 'body': {'Select': {'mailbox': 'Inbox'}}}
# better
cmd = Command(tag=Tag("a002"), body=Select(mailbox=Mailbox.Inbox))
encoded_cmd = CommandCodec.encode(cmd.model_bump())
Even if PyO3 could convert Rust structs with lifetimes into the correct Python classes, it wouldn't automatically generate the class stubs on the Python side (Python Interface (.pyi) generation and runtime inspection). So, even if
Yes, that's correct. Pydantic only needs to reconstruct the model based on the dict output, without performing complex validation (which is already handled by I hope this clarifies my suggestions and the reasoning behind them. I would be happy to provide more details or assistance if needed. |
Currently, the codec messages are typeless dictionaries, e.g.
{"tag": "a", "body": "Noop"}
.Using classes (e.g.
Command
,Greeting
) for each of these messages would allow making it more clear what type the methods accept and return, even if these classes itself are constructed from dictionaries, e.g.Command({"tag": "a", "body": "Noop"})
.The text was updated successfully, but these errors were encountered: