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

[Worker support] Error handling and warnings #1381

Open
antocuni opened this issue Apr 12, 2023 · 1 comment
Open

[Worker support] Error handling and warnings #1381

antocuni opened this issue Apr 12, 2023 · 1 comment
Labels
classic Issues only applicable to PyScript versions 2023.05.1 and earlier ("PyScript Classic")

Comments

@antocuni
Copy link
Contributor

This is a follow-up of #1333 (once it's merged).

It seems that for some reason, error and warning banners don't appear when we run in a worker. There are 3 tests which are skipped because of that:

@skip_worker("FIXME: the banner doesn't appear")
def test_non_existent_package(self):
self.pyscript_run(
"""
<py-config>
packages = ["nonexistendright"]
</py-config>
""",
wait_for_pyscript=False,
)
expected_alert_banner_msg = (
"(PY1001): Unable to install package(s) 'nonexistendright'. "
"Unable to find package in PyPI. Please make sure you have "
"entered a correct package name."
)
alert_banner = self.page.wait_for_selector(".alert-banner")
assert expected_alert_banner_msg in alert_banner.inner_text()

@skip_worker("FIXME: the banner doesn't appear")
def test_no_python_wheel(self):
self.pyscript_run(
"""
<py-config>
packages = ["opsdroid"]
</py-config>
""",
wait_for_pyscript=False,
)
expected_alert_banner_msg = (
"(PY1001): Unable to install package(s) 'opsdroid'. "
"Reason: Can't find a pure Python 3 Wheel for package(s) 'opsdroid'"
)
alert_banner = self.page.wait_for_selector(".alert-banner")
assert expected_alert_banner_msg in alert_banner.inner_text()

@skip_worker("FIXME: showWarning()")
def test_assert_no_banners(self):
"""
Test that the DOM doesn't contain error/warning banners
"""
self.pyscript_run(
"""
<py-script>
from _pyscript_js import showWarning
showWarning("hello")
showWarning("world")
</py-script>
"""
)
with pytest.raises(AssertionError, match="Found 2 alert banners"):
self.assert_no_banners()

@antocuni
Copy link
Contributor Author

I have started to investigate the issue.
In theory the execution flows like this:

  1. We are in the main thread: PyScriptApp.installPackage calls interpreter._remote.installPackage: interpreter._remote is a Synclink proxy, so the call happens through synclink:
    await this.interpreter._remote.installPackage(this.config.packages);
  2. We are in the worker: installPackage throws InstallError, which is a subclass of UserError:
    throw new InstallError(ErrorCode.MICROPIP_INSTALL_ERROR, exceptionMessage);
  3. in theory, the main() function in the main thread catches the UserError and displays the appropriate banner:
    try {
    await this._realMain();
    } catch (error) {
    await this._handleUserErrorMaybe(error);
    }

It seems that synclink propagates the UserError correctly in the main thread case, but it doesn't in the worker case.

As an additional data point, I also tried to run the test with this patch:

diff --git a/pyscriptjs/src/remote_interpreter.ts b/pyscriptjs/src/remote_interpreter.ts
index a245aaa..7fd2f94 100644
--- a/pyscriptjs/src/remote_interpreter.ts
+++ b/pyscriptjs/src/remote_interpreter.ts
@@ -2,7 +2,7 @@ import type { AppConfig } from './pyconfig';
 import { version } from './version';
 import { getLogger } from './logger';
 import { Stdio } from './stdio';
-import { InstallError, ErrorCode } from './exceptions';
+import { InstallError, ErrorCode, UserError } from './exceptions';
 import { robustFetch } from './fetch';
 import type { loadPyodide as loadPyodideDeclaration, PyodideInterface, PyProxy, PyProxyDict } from 'pyodide';
 import type { ProxyMarked } from 'synclink';
@@ -114,6 +114,9 @@ export class RemoteInterpreter extends Object {
                 fullStdLib: false,
             }),
         );
+
+        throw new UserError("hello UserError");
+
         this.interface.registerComlink(Synclink);
         // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
         this.FS = this.interface.FS;

And I confirm that with pytest -m main the hello UserError banner appears correctly, while in the -m worker case it doesn't.

In the worker case, the console shows this log:

transfer_handlers.ts:134 Uncaught (in promise) UserError: (hello UserError): undefined
    at RemoteInterpreter.loadInterpreter (interpreter_worker.js:3046:13)
deserialize @ transfer_handlers.ts:134
fromWireValue @ transfer_handlers.ts:208
(anonymous) @ task.ts:195
fulfilled @ pyconfig.ts:266

I guess it's an upstream bug of Synclink.

/cc @hoodmane

@JeffersGlass JeffersGlass added the classic Issues only applicable to PyScript versions 2023.05.1 and earlier ("PyScript Classic") label Sep 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
classic Issues only applicable to PyScript versions 2023.05.1 and earlier ("PyScript Classic")
Projects
None yet
Development

No branches or pull requests

2 participants