Upgrade to Pyodide 0.23 (#1347)

* Upgrade to Pyodide 0.23.2

* Update changelog

* Use @param decorator to fix kmeans examlpe

* Separate zz_example tests to run sequentially

* Remove pytest.raises from pyscript_src_not_found test, use check_js_errors instead

* Add 'check_js_errors' to wait_for_pyscript
This commit is contained in:
Jeff Glass
2023-05-24 07:59:19 -05:00
committed by GitHub
parent 61b3154461
commit e1758ae2e2
15 changed files with 80 additions and 76 deletions

View File

@@ -67,6 +67,9 @@ jobs:
- name: Integration Tests - name: Integration Tests
run: make test-integration-parallel run: make test-integration-parallel
- name: Examples Tests
run: make test-examples
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
name: pyscript name: pyscript

View File

@@ -7,6 +7,7 @@
Features Features
-------- --------
- The default version of Pyodide is now `0.23.2`. See the [Pyodide Changelog](https://pyodide.org/en/stable/project/changelog.html#version-0-23-2) for a detailed list of changes.
- Added the `@when` decorator for attaching Python functions as event handlers - Added the `@when` decorator for attaching Python functions as event handlers
- The `py-mount` attribute on HTML elements has been deprecated, and will be removed in a future release. - The `py-mount` attribute on HTML elements has been deprecated, and will be removed in a future release.

View File

@@ -146,8 +146,8 @@ $ pytest test_01_basic.py -k test_pyscript_hello -s
[ 0.00 page.goto ] pyscript_hello.html [ 0.00 page.goto ] pyscript_hello.html
[ 0.01 request ] 200 - fake_server - http://fake_server/pyscript_hello.html [ 0.01 request ] 200 - fake_server - http://fake_server/pyscript_hello.html
... ...
[ 0.17 console.info ] [py-loader] Downloading pyodide-0.22.1... [ 0.17 console.info ] [py-loader] Downloading pyodide-x.y.z...
[ 0.18 request ] 200 - CACHED - https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js [ 0.18 request ] 200 - CACHED - https://cdn.jsdelivr.net/pyodide/vx.y.z/full/pyodide.js
... ...
[ 3.59 console.info ] [pyscript/main] PyScript page fully initialized [ 3.59 console.info ] [pyscript/main] PyScript page fully initialized
[ 3.60 console.log ] hello pyscript [ 3.60 console.log ] hello pyscript
@@ -217,25 +217,6 @@ If you want to temporarily disable the cache, the easiest thing is to use
If you want to clear the cache, you can use the special option If you want to clear the cache, you can use the special option
`--clear-http-cache`: `--clear-http-cache`:
```
$ pytest --clear-http-cache
...
-------------------- SmartRouter HTTP cache --------------------
Requests found in the cache:
https://raw.githubusercontent.com/pyscript/pyscript/main/README.md
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/repodata.json
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.js
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/micropip-0.1-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.data
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.asm.wasm
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide_py.tar
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyparsing-3.0.9-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/distutils.tar
https://cdn.jsdelivr.net/pyodide/v0.22.1/full/packaging-21.3-py3-none-any.whl
Cache cleared
```
**NOTE**: this works only if you are inside `tests/integration`, or if you **NOTE**: this works only if you are inside `tests/integration`, or if you
explicitly specify `tests/integration` from the command line. This is due to explicitly specify `tests/integration` from the command line. This is due to
how `pytest` decides to search for and load the various `conftest.py`. how `pytest` decides to search for and load the various `conftest.py`.

View File

@@ -25,7 +25,7 @@ To get started, let's create a new `index.html` file and import `pyscript.js`.
</html> </html>
``` ```
We are using the pyodide CDN to setup our interpreter, but you can also download the files from [the pyodide GitHub release](https://github.com/pyodide/pyodide/releases/tag/0.22.0a3), unzip them and use the `pyodide.js` file as your interpreter. We are using the pyodide CDN to setup our interpreter, but you can also download the files from [the pyodide GitHub releases](https://github.com/pyodide/pyodide/releases/), unzip them and use the `pyodide.js` file as your interpreter.
## Setting the interpreter ## Setting the interpreter
@@ -47,8 +47,8 @@ To set the interpreter, you can use the `interpreter` configuration in the `py-c
<body> <body>
<py-config> <py-config>
[[interpreters]] [[interpreters]]
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js" src = "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"
name = "pyodide-0.22.0a3" name = "pyodide-0.23.0"
lang = "python" lang = "python"
</py-config> </py-config>
</body> </body>
@@ -75,8 +75,8 @@ To confirm that the interpreter is set correctly, you can open the DevTools and
<body> <body>
<py-config> <py-config>
[[interpreters]] [[interpreters]]
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js" src = "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"
name = "pyodide-0.22.0a3" name = "pyodide-0.23.0"
lang = "python" lang = "python"
</py-config> </py-config>
<py-script> <py-script>

View File

@@ -86,20 +86,26 @@ run-examples: setup build examples
make dev make dev
test: test:
make examples
make test-ts make test-ts
make test-py make test-py
make test-integration-parallel make test-integration-parallel
make test-examples
# run all integration tests *including examples* sequentially
test-integration: test-integration:
make examples
mkdir -p test_results mkdir -p test_results
$(PYTEST_EXE) -vv $(ARGS) tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml $(PYTEST_EXE) -vv $(ARGS) tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml
# run all integration tests *except examples* in parallel (examples use too much memory)
test-integration-parallel: test-integration-parallel:
mkdir -p test_results
$(PYTEST_EXE) --numprocesses auto -vv $(ARGS) tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml -k 'not zz_examples'
# run integration tests on only examples sequentially (to avoid running out of memory)
test-examples:
make examples make examples
mkdir -p test_results mkdir -p test_results
$(PYTEST_EXE) --numprocesses auto -vv $(ARGS) tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml $(PYTEST_EXE) -vv $(ARGS) tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml -k 'zz_examples'
test-py: test-py:
@echo "Tests from $(src_dir)" @echo "Tests from $(src_dir)"

View File

@@ -21,5 +21,5 @@ dependencies:
- pytest-xdist==3.3.0 - pytest-xdist==3.3.0
# We need Pyodide and micropip so we can import them in our Python # We need Pyodide and micropip so we can import them in our Python
# unit tests # unit tests
- pyodide_py==0.22 - pyodide_py==0.23.2
- micropip==0.2.2 - micropip==0.2.2

View File

@@ -32,7 +32,7 @@
"jest": "29.1.2", "jest": "29.1.2",
"jest-environment-jsdom": "29.1.2", "jest-environment-jsdom": "29.1.2",
"prettier": "2.7.1", "prettier": "2.7.1",
"pyodide": "0.22.1", "pyodide": "0.23.2",
"synclink": "0.2.4", "synclink": "0.2.4",
"ts-jest": "29.0.3", "ts-jest": "29.0.3",
"typescript": "5.0.4" "typescript": "5.0.4"
@@ -5176,9 +5176,9 @@
] ]
}, },
"node_modules/pyodide": { "node_modules/pyodide": {
"version": "0.22.1", "version": "0.23.2",
"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.22.1.tgz", "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.23.2.tgz",
"integrity": "sha512-6+PkFLTC+kcBKtFQxYBxR44J5IBxLm8UGkobLgZv1SxzV9qOU2rb0YYf0qDtlnfDiN/IQd2uckf+D8Zwe88Mqg==", "integrity": "sha512-GK4YDZVgzfAXK/7X0IiCI+142k0Ah/HwYTzDHtG8zC47dflWYuPozeFbOngShuL1M9Un5sCmHFqiH3boxJv0pQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"base-64": "^1.0.0", "base-64": "^1.0.0",
@@ -9878,9 +9878,9 @@
"dev": true "dev": true
}, },
"pyodide": { "pyodide": {
"version": "0.22.1", "version": "0.23.2",
"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.22.1.tgz", "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.23.2.tgz",
"integrity": "sha512-6+PkFLTC+kcBKtFQxYBxR44J5IBxLm8UGkobLgZv1SxzV9qOU2rb0YYf0qDtlnfDiN/IQd2uckf+D8Zwe88Mqg==", "integrity": "sha512-GK4YDZVgzfAXK/7X0IiCI+142k0Ah/HwYTzDHtG8zC47dflWYuPozeFbOngShuL1M9Un5sCmHFqiH3boxJv0pQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"base-64": "^1.0.0", "base-64": "^1.0.0",

View File

@@ -34,7 +34,7 @@
"jest": "29.1.2", "jest": "29.1.2",
"jest-environment-jsdom": "29.1.2", "jest-environment-jsdom": "29.1.2",
"prettier": "2.7.1", "prettier": "2.7.1",
"pyodide": "0.22.1", "pyodide": "0.23.2",
"synclink": "0.2.4", "synclink": "0.2.4",
"ts-jest": "29.0.3", "ts-jest": "29.0.3",
"typescript": "5.0.4" "typescript": "5.0.4"

View File

@@ -54,8 +54,8 @@ export const defaultConfig: AppConfig = {
type: 'app', type: 'app',
interpreters: [ interpreters: [
{ {
src: 'https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js', src: 'https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js',
name: 'pyodide-0.22.1', name: 'pyodide-0.23.2',
lang: 'python', lang: 'python',
}, },
], ],

View File

@@ -72,7 +72,7 @@ export class RemoteInterpreter extends Object {
// TODO: Remove this once `runtimes` is removed! // TODO: Remove this once `runtimes` is removed!
interpreter: InterpreterInterface & ProxyMarked; interpreter: InterpreterInterface & ProxyMarked;
constructor(src = 'https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js') { constructor(src = 'https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js') {
super(); super();
this.src = src; this.src = src;
} }
@@ -91,10 +91,9 @@ export class RemoteInterpreter extends Object {
* *
* This is because, if it's used, loadPyodide * This is because, if it's used, loadPyodide
* behaves mischievously i.e. it tries to load * behaves mischievously i.e. it tries to load
* `pyodide.asm.js` and `pyodide_py.tar` but * additional files but with paths that are wrong such as:
* with paths that are wrong such as:
* *
* http://127.0.0.1:8080/build/pyodide_py.tar * http://127.0.0.1:8080/build/...
* which results in a 404 since `build` doesn't * which results in a 404 since `build` doesn't
* contain these files and is clearly the wrong * contain these files and is clearly the wrong
* path. * path.
@@ -171,13 +170,12 @@ export class RemoteInterpreter extends Object {
* */ * */
async loadPackage(names: string | string[]): Promise<void> { async loadPackage(names: string | string[]): Promise<void> {
logger.info(`pyodide.loadPackage: ${names.toString()}`); logger.info(`pyodide.loadPackage: ${names.toString()}`);
// the new way in v0.22.1 is to pass it as a dict of options i.e. // The signature of `loadPackage` changed in Pyodide 0.22; while we generally
// { messageCallback: logger.info.bind(logger), errorCallback: logger.info.bind(logger) } // don't support older versions of Pyodide in any given release of PyScript, this
// but one of our tests tries to use a locally downloaded older version of pyodide // significant change is useful in some testing scenarios (for now)
// for which the signature of `loadPackage` accepts the above params as args i.e.
// the call uses `logger.info.bind(logger), logger.info.bind(logger)`.
const messageCallback = logger.info.bind(logger) as typeof logger.info; const messageCallback = logger.info.bind(logger) as typeof logger.info;
if (this.interpreter.version.startsWith('0.22')) { // Comparing version as number to avoid issues with lexicographic comparison
if (Number(this.interpreter.version.split('.')[1]) >= 22) {
await this.interface.loadPackage(names, { await this.interface.loadPackage(names, {
messageCallback, messageCallback,
errorCallback: messageCallback, errorCallback: messageCallback,

View File

@@ -493,7 +493,13 @@ class PyScriptTest:
return doc return doc
def pyscript_run( def pyscript_run(
self, snippet, *, extra_head="", wait_for_pyscript=True, timeout=None self,
snippet,
*,
extra_head="",
wait_for_pyscript=True,
timeout=None,
check_js_errors=True,
): ):
""" """
Main entry point for pyscript tests. Main entry point for pyscript tests.
@@ -517,7 +523,7 @@ class PyScriptTest:
self.writefile(filename, doc) self.writefile(filename, doc)
self.goto(filename) self.goto(filename)
if wait_for_pyscript: if wait_for_pyscript:
self.wait_for_pyscript(timeout=timeout) self.wait_for_pyscript(timeout=timeout, check_js_errors=check_js_errors)
def iter_locator(self, loc): def iter_locator(self, loc):
""" """
@@ -630,7 +636,7 @@ TEST_ITERATIONS = math.ceil(
) # 120 iters of 1/4 second ) # 120 iters of 1/4 second
def wait_for_render(page, selector, pattern): def wait_for_render(page, selector, pattern, timeout_seconds: int | None = None):
""" """
Assert that rendering inserts data into the page as expected: search the Assert that rendering inserts data into the page as expected: search the
DOM from within the timing loop for a string that is not present in the DOM from within the timing loop for a string that is not present in the
@@ -639,7 +645,12 @@ def wait_for_render(page, selector, pattern):
re_sub_content = re.compile(pattern) re_sub_content = re.compile(pattern)
py_rendered = False # Flag to be set to True when condition met py_rendered = False # Flag to be set to True when condition met
for _ in range(TEST_ITERATIONS): if timeout_seconds:
check_iterations = math.ceil(timeout_seconds / TEST_TIME_INCREMENT)
else:
check_iterations = TEST_ITERATIONS
for _ in range(check_iterations):
content = page.inner_html(selector) content = page.inner_html(selector)
if re_sub_content.search(content): if re_sub_content.search(content):
py_rendered = True py_rendered = True

View File

@@ -2,7 +2,7 @@ import re
import pytest import pytest
from .support import PageErrors, PyScriptTest, skip_worker from .support import PyScriptTest, skip_worker
class TestBasic(PyScriptTest): class TestBasic(PyScriptTest):
@@ -246,22 +246,23 @@ class TestBasic(PyScriptTest):
assert self.console.log.lines[-1] == "hello from foo" assert self.console.log.lines[-1] == "hello from foo"
def test_py_script_src_not_found(self): def test_py_script_src_not_found(self):
with pytest.raises(PageErrors) as exc: self.pyscript_run(
self.pyscript_run( """
""" <py-script src="foo.py"></py-script>
<py-script src="foo.py"></py-script> """,
""" check_js_errors=False,
) )
assert "Failed to load resource" in self.console.error.lines[0] assert "Failed to load resource" in self.console.error.lines[0]
error_msgs = str(exc.value)
expected_msg = "(PY0404): Fetching from URL foo.py failed with error 404" expected_msg = "(PY0404): Fetching from URL foo.py failed with error 404"
assert expected_msg in error_msgs assert any((expected_msg in line) for line in self.console.js_error.lines)
assert self.assert_banner_message(expected_msg) assert self.assert_banner_message(expected_msg)
pyscript_tag = self.page.locator("py-script") pyscript_tag = self.page.locator("py-script")
assert pyscript_tag.inner_html() == "" assert pyscript_tag.inner_html() == ""
self.check_js_errors(expected_msg)
def test_js_version(self): def test_js_version(self):
self.pyscript_run( self.pyscript_run(
""" """

View File

@@ -80,9 +80,9 @@ class TestConfig(PyScriptTest):
) )
assert self.console.log.lines[-1] == "config name: app with external config" assert self.console.log.lines[-1] == "config name: app with external config"
# The default pyodide version is 0.22.1 as of writing # The default pyodide version is newer than
# this test which is newer than the one we are loading below # the one we are loading below (after downloading locally)
# (after downloading locally) -- which is 0.22.0 # which is 0.22.0
# The test checks if loading a different interpreter is possible # The test checks if loading a different interpreter is possible
# and that too from a locally downloaded file without needing # and that too from a locally downloaded file without needing
@@ -234,8 +234,8 @@ class TestConfig(PyScriptTest):
{ {
"interpreters": [ "interpreters": [
{ {
"src": "https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js", "src": "https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js",
"name": "pyodide-0.22.1", "name": "pyodide-0.23.2",
"lang": "python" "lang": "python"
}, },
{ {

View File

@@ -276,18 +276,20 @@ class TestExamples(PyScriptTest):
self.check_tutor_generated_code() self.check_tutor_generated_code()
def test_panel_kmeans(self): def test_panel_kmeans(self):
# XXX improve this test # XXX improve this test>>>>>>> main
self.goto("examples/panel_kmeans.html") self.goto("examples/panel_kmeans.html")
self.wait_for_pyscript(timeout=90 * 1000) self.wait_for_pyscript(timeout=120 * 1000)
assert self.page.title() == "Pyscript/Panel KMeans Demo" assert self.page.title() == "Pyscript/Panel KMeans Demo"
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>") wait_for_render(
self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>", timeout_seconds=60 * 2
)
self.assert_no_banners() self.assert_no_banners()
self.check_tutor_generated_code() self.check_tutor_generated_code()
def test_panel_stream(self): def test_panel_stream(self):
# XXX improve this test # XXX improve this test
self.goto("examples/panel_stream.html") self.goto("examples/panel_stream.html")
self.wait_for_pyscript(timeout=90 * 1000) self.wait_for_pyscript(timeout=3 * 60 * 1000)
assert self.page.title() == "PyScript/Panel Streaming Demo" assert self.page.title() == "PyScript/Panel Streaming Demo"
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>") wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
self.assert_no_banners() self.assert_no_banners()
@@ -316,7 +318,7 @@ class TestExamples(PyScriptTest):
def test_repl2(self): def test_repl2(self):
self.goto("examples/repl2.html") self.goto("examples/repl2.html")
self.wait_for_pyscript() self.wait_for_pyscript(timeout=1.5 * 60 * 1000)
assert self.page.title() == "Custom REPL Example" assert self.page.title() == "Custom REPL Example"
wait_for_render(self.page, "*", "<py-repl.*?>") wait_for_render(self.page, "*", "<py-repl.*?>")
# confirm we can import utils and run one command # confirm we can import utils and run one command

View File

@@ -130,12 +130,13 @@ class TestDocsSnippets(PyScriptTest):
self.assert_no_banners() self.assert_no_banners()
def test_tutorials_py_config_interpreter(self): def test_tutorials_py_config_interpreter(self):
"""Load a previous version of Pyodide"""
self.pyscript_run( self.pyscript_run(
""" """
<py-config> <py-config>
[[interpreters]] [[interpreters]]
src = "https://cdn.jsdelivr.net/pyodide/v0.22.0a3/full/pyodide.js" src = "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/pyodide.js"
name = "pyodide-0.22.0a3" name = "pyodide-0.23.0"
lang = "python" lang = "python"
</py-config> </py-config>
<py-script> <py-script>
@@ -146,7 +147,7 @@ class TestDocsSnippets(PyScriptTest):
) )
py_terminal = self.page.wait_for_selector("py-terminal") py_terminal = self.page.wait_for_selector("py-terminal")
assert "0.22.0a3" in py_terminal.inner_text() assert "0.23.0" in py_terminal.inner_text()
self.assert_no_banners() self.assert_no_banners()
@skip_worker("FIXME: display()") @skip_worker("FIXME: display()")