Skip to content
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

Refactor loadPackage #1084

Merged
merged 17 commits into from
Jan 12, 2021
Merged

Refactor loadPackage #1084

merged 17 commits into from
Jan 12, 2021

Conversation

dalcde
Copy link
Contributor

@dalcde dalcde commented Jan 9, 2021

From the API point of view, this removes the messageCallback and errorCallback arguments in loadPackage, runPythonAsync etc. These are used for logging. Previously, any message will be written to the console, and then {message,error}Callback is called with the message as an argument. In most of our use cases, these callbacks do nothing.

If we were to reintroduce this feature, I think we should introduce a logger interface, and users can supply custom loggers. This lets them choose to not output to stdout/stderr as well.

Under the hood, the original motivation of this patch was to get rid of using packageCounter to determine whether all packages have been loaded. This is prone to error if we change the way we load packages (which I attempted in a different branch until I encountered an emscripten bug).

One way or another, I ended up rewriting a lot of the code into something with a clearer control flow imo.

dalcde added 2 commits January 9, 2021 12:50
From the API point of view, this removes the messageCallback and
errorCallback arguments in loadPackage, runPythonAsync etc. These are
used for logging. Previously, any message will be written to the
console, and then {message,error}Callback is called with the message as
an argument. In most of our use cases, these callbacks do nothing.

If we were to reintroduce this feature, I think we should introduce a
logger interface, and users can supply custom loggers. This lets them
choose to not output to stdout/stderr as well.

Under the hood, the original motivation of this patch was to get rid of
using packageCounter to determine whether all packages have been loaded.
This is prone to error if we change the way we load packages (which I
attempted in a different branch until I encountered an emscripten bug).

One way or another, I ended up rewriting a lot of the code into
something with a clearer control flow imo.
Copy link
Member

@hoodmane hoodmane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a stab at this. I still have lots of issues with this code but this is a clear and manageable improvement. I would have trouble leaving enough stuff alone to maintain a manageable diff (cf #998).
Maybe change toLoad from an object to a Map? I think that'd be better.

I think the windowErrorHandler is really bad: what sorts of errors hit it and why? Can't we handle such errors in a less brute force way?

docs/js-api/pyodide_loadPackagesFromImports.md Outdated Show resolved Hide resolved
src/pyodide.js Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
dalcde and others added 2 commits January 9, 2021 14:12
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
src/pyodide.js Outdated Show resolved Hide resolved
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
@dalcde
Copy link
Contributor Author

dalcde commented Jan 9, 2021

Just for the record, the very original motivation was to get rid of our custom preloadWasm code and use the one that comes with emscripten, but turns out that's not compatible with -s LZ4

@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

See window.onerror:

  • When a JavaScript runtime error (including syntax errors and exceptions thrown within handlers) occurs, an error event using interface ErrorEvent is fired at window and window.onerror() is invoked (as well as handlers attached by window.addEventListener (not only capturing)).
  • When a resource (such as an <img> or <script>) fails to load, an error event using interface Event is fired at the element that initiated the load, and the onerror() handler on the element is invoked. These error events do not bubble up to window, but can be handled with a window.addEventListener configured with useCapture set to true.

I think we're trying to catch the second type of errors? So the correct way to do it is to attach an onError element to the script tag? What else is going to generate uncaught errors?

@dalcde
Copy link
Contributor Author

dalcde commented Jan 9, 2021

I think I am concerned mostly about the first kind of error within the loaded script. The second kind is captured by the loadScript function already.

dalcde and others added 2 commits January 9, 2021 14:19
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

It seems sad to be unable to catch the error any closer to the source, but if we add my noisy and explicit console.error message, I think it's okay. Can you make a test that hits that execution path and cross reference against the test in the code though? I think it's natural to wonder why the error can't be isolated any better.

Incidentally, it's sort of remarkable that there isn't a less error prone and hacky way to load a script. Why doesn't the loadScript api exist on the main thread? Because it's blocking? Why is it blocking?! Browsers are so weird >_<

@dalcde
Copy link
Contributor Author

dalcde commented Jan 9, 2021

Well, we can fetch the source code and eval it ....

@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

fetch the source code and eval

Do we lose anything by doing that? Maybe weaker CORS restrictions?

@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

Maybe it's forbidden by "Content Security Policy" or something?

@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

Okay so if I understand correctly for the global window handler:

  1. this is necessary and can happen
  2. the other ways to do this have their own drawbacks and it's unclear if they are any better, and
  3. you know how to cause the code to trigger but it's hard to test

In that case I guess the most reasonable thing is just to put a comment explaining the situation and saying that it is technically necessary but unfortunate, and also to make it emit a noisy and explicit error message into the console if this happens.

dalcde added 2 commits January 9, 2021 16:10
The main difference in end result in we no longer produce a hard error
when errors occur during the dependency resolution stage; we simply
ignore the offending packages. We still error if something goes wrong
during package loading, as the system may be left in a broken state.

The error messages produced are, however, slightly different. Now if a
package is loaded from a custom url then the default channels, we no
longer print it as an error; we assume the user is trying to override a
dependency. Since we don't act on these errors anyway, this doesn't
affect the API.

Smaller changes include changing to a recursive DFS algorithm and
turning toLoad into a Map
@rth
Copy link
Member

rth commented Jan 9, 2021

Previously, any message will be written to the console, and then {message,error}Callback is called with the message as an argument. In most of our use cases, these callbacks do nothing.

In our uses cases yes, but it would allow users to customize where errors/messages are shown.

If we were to reintroduce this feature, I think we should introduce a logger interface, and users can supply custom loggers.

In my understanding this feature is critical for downstream applications (e.g. Basthon, Starboard Notebook). Granted our error handling there can certainly be improved, but if we remove it we can't really make a release until a new API is includes. I think it would be better to,

  • have a separate PR with as much of refactoring from this PR as possible
  • open an issue and discuss how we want our error handling API to look. A logger might indeed be nice, but I also don't see it as fundamentally different from error callbacks. For instance Add pyodide-js library (alternative to pyodide.js and webworker.js) #792 still has callbacks.
  • make a PR that replaces the logging API if necessary. Just to avoid a situation where we decide after removing them that callback where actually fine.

Also for all changes that break backward compatibility and change the Public API, it would be better to have the changed method/arguments names in the title, as opposed to just "refactoring". Here this have been, API Remove messageCallback and errorCallback arguments from loadPackage

@dalcde
Copy link
Contributor Author

dalcde commented Jan 9, 2021 via email

@dalcde
Copy link
Contributor Author

dalcde commented Jan 9, 2021

But I think I'll change the behaviour so that the default callbacks are console.log and console.error, and supplying a custom callback would cause it to stop logging to console.

@rth
Copy link
Member

rth commented Jan 9, 2021

In my mind a logger interface is an object with ".log" and ".error"
functions, so basically combining the two into one.

Yeah, that sound good. But let's still open an issue to discuss it before implementing it? I would like to also get feedback from downstream package maintainers.

@hoodmane
Copy link
Member

hoodmane commented Jan 9, 2021

Yeah we should discuss with downstream whether they actually need an error callback. I think the right thing to do is to be able to request a logging level ("error", "warn", "info", "debug"). If they want to execute code when there is an error, what about try + catch?

Copy link
Member

@rth rth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Should be good to merge unless @hoodmane has more comments.

Copy link
Member

@hoodmane hoodmane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really nice!

src/pyodide.js Outdated Show resolved Hide resolved
src/pyodide.js Outdated Show resolved Hide resolved
@hoodmane
Copy link
Member

Okay this looks good to me, I think it should be accepted.

@rth rth merged commit a48a1ff into pyodide:master Jan 12, 2021
@rth
Copy link
Member

rth commented Jan 12, 2021

Thanks @dalcde and @hoodmane !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants