-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
NF: explaining the bridge between languages #3572
base: main
Are you sure you want to change the base?
Conversation
a19c20e
to
975e317
Compare
The technical details appear to be correct. This reads like some rough notes though, and needs some polishing before it's ready for inclusion (things like "as far as I understand" don't really belong in a document that is telling people what to do!) |
975e317
to
e5042b3
Compare
Appart from the sentence you quoted, is there anything you believe needs more polishing? I would have hoped everything is understandable. |
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.
This still feels pretty rough at the moment I'm afraid. Maybe someone else would like to take a stab at rewriting it?
docs/protobuf.md
Outdated
|
||
# Communication between languages | ||
|
||
Currently, Rust does not send message to Python or Ts. That is, the |
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.
'send' is somewhat misleading here, since the request does not return until the call has completed and a return value is provided - I'd use things like "call" or "invoke" a method instead.
I would lede with something like:
Anki's codebase uses three layers:
- The core Rust layer
- The Python layer
- The web frontend/TypeScript layer
Each layer only makes RPC calls to the layer below it. TypeScript code makes calls to the Python-level HTTP server. Python code makes calls to the underlying Rust layer. The Rust layer never makes RPC calls.
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'd appreciate that, except that it's currently false. We have calls from Python to ts. And as far as I can see, it won't be going away soon, given that both of them have part of the UI responsibilities. We can aim to reduce the number of such calls.
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.
Also, given that the C of RPC is already "call", I admit I would not feel okay using "RPC call", but if you really want it, I can do that
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.
RPC call is tautological, but pretty common in my experience. But my point was more that Python→Rust and web→Python are 'calls' in the sense that they block until they receive a reply (assuming the TS promise is awaited). As you know, web.eval(WithCallback) is more akin to injecting some text into the JavaScript console, and it's not possible for the Python code to block until the reply is received.
This commit explains how to calls a method implemented in a language from a different language. This explains how to declare the RPCs, how to call them and how to implement them. This is based on examples of code at main at the time of writting. I used permalink to ensure that the links remains relevant even if the specific examples change later. The last section is about the special case of calling TypeScript from Python, which does not use RPC but is still relevant in a bridge document. This commit also add a paragraph explaining what protobuf is in the protobuf documentation, so that new contributors who don't know what protobuf is can understand why we use it.
5093c60
to
2eae678
Compare
In order to take your feedback into account I:
|
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.
The new structure is better, thank you. There's still a number of minor grammatical mistakes. It's 2025, and we shouldn't need to go back and forth on these - please use an LLM to fix your wording.
|
||
Read [protobuf](./protobuf.md) to learn more about how those input and output types are defined. | ||
|
||
If the RPC implementation is in Python, it should be declared in [frontend.proto](https://github.com/ankitects/anki/blob/acaeee91fa853e4a7a78dcddbb832d009ec3529a/proto/anki/frontend.proto#L24C3-L24C66)'s `FrontendService`. Otherwise, it can be declared in any other service. Obviously, the service should be consistent with what the method do. |
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.
While you mention it below, you should probably explicitly state here that all RPCs are implement at the Rust layer, except when they are placed in frontend.proto.
|
||
If the RPC implementation is in Python, it should be declared in [frontend.proto](https://github.com/ankitects/anki/blob/acaeee91fa853e4a7a78dcddbb832d009ec3529a/proto/anki/frontend.proto#L24C3-L24C66)'s `FrontendService`. Otherwise, it can be declared in any other service. Obviously, the service should be consistent with what the method do. | ||
|
||
## Making RPC |
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.
## Making RPC | |
## Making a Remote Procedure Call |
|
||
It must be noted that: | ||
|
||
- the RPC is not received on main. In order to do any work with window, we should call `aqt.mw.taskman.run_on_main`. |
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.
- the RPC is not received on main. In order to do any work with window, we should call `aqt.mw.taskman.run_on_main`. | |
- the incoming HTTP request is not processed on the main thread. In order to do any work with the GUI, we should call `aqt.mw.taskman.run_on_main`. |
|
||
- the RPC is not received on main. In order to do any work with window, we should call `aqt.mw.taskman.run_on_main`. | ||
- at the time the RPC is received, we have no guarantee that the main view's active window is a DeckOptionsDialog. So this should be checked before doing anything else. If it's not the case, the call should be ignored. | ||
- While MediaServer handles all request from typescript, it's not the appropriate place for most implementation. So it's expected that, once it has determined that the active view has the expected type, this active view actually handle the request. |
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.
The last two points seem somewhat specific to this particular RPC, and I'm not sure they're adding much.
Note that if the function is asynchronous, you can't directly send the | ||
result to a callback. Instead your function will have to call a post | ||
method that will be sent to Python or Rust. Which leads us to the next | ||
section. |
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.
What next section?
It was, honestly, quite confusing. And if I got it wrong, I guess this code review will be the time for me to learn what was wrong.