diff --git a/.circleci/config.yml b/.circleci/config.yml index 8d98476b2a0..1a4dc6d8ff7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ defaults: &defaults # Note: when updating the docker image version, # make sure there are no extra old versions lying around. # (e.g. `rg -F --hidden `) - - image: pyodide/pyodide-env:20240928-chrome127-firefox128 + - image: pyodide/pyodide-env:20241028-chrome130-firefox131 environment: - EMSDK_NUM_CORES: 3 EMCC_CORES: 3 @@ -29,6 +29,8 @@ jobs: - DISABLE_DYLINK: << parameters.disable_dylink >> steps: - checkout + - run: git submodule sync + - run: git submodule update --init - restore_cache: keys: @@ -132,6 +134,8 @@ jobs: resource_class: large steps: - checkout + - run: git submodule sync + - run: git submodule update --init - attach_workspace: at: . @@ -190,6 +194,8 @@ jobs: resource_class: large steps: - checkout + - run: git submodule sync + - run: git submodule update --init - attach_workspace: at: . @@ -219,6 +225,8 @@ jobs: <<: *defaults steps: - checkout + - run: git submodule sync + - run: git submodule update --init - attach_workspace: at: . @@ -246,6 +254,8 @@ jobs: <<: *defaults steps: - checkout + - run: git submodule sync + - run: git submodule update --init - attach_workspace: at: . diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 528cd57938c..9ecfffcad00 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ { "name": "Docker", // keep in sync with "run_docker" - "image": "pyodide/pyodide-env:20240928-chrome127-firefox128", + "image": "pyodide/pyodide-env:20241028-chrome130-firefox131", "remoteUser": "root", "onCreateCommand": ".devcontainer/onCreate-docker.sh" } diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2327f9dc549..fcbb88f8d9f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,8 @@ jobs: needs: get_python_version steps: - uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/setup-python@v5 with: python-version: ${{ needs.get_python_version.outputs.PYVERSION }} @@ -34,8 +36,8 @@ jobs: shell: bash -l {0} run: | mkdir test-results - python3 -m pip install -r requirements.txt -r requirements-deploy.txt make pyodide_build + python3 -m pip install -r requirements.txt -r requirements-deploy.txt - name: Run tests shell: bash -l {0} run: | @@ -43,7 +45,7 @@ jobs: --junitxml=test-results/junit.xml \ --verbose \ --runtime=host \ - --cov=pyodide_build --cov=pyodide \ + --cov=pyodide \ src packages/_tests tools/ - uses: codecov/codecov-action@v4 with: @@ -68,7 +70,8 @@ jobs: steps: - uses: actions/checkout@v4 - + with: + submodules: recursive - name: Cache ccache output uses: actions/cache@v4 with: @@ -169,6 +172,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Download build artifact uses: actions/download-artifact@v4 diff --git a/.github/workflows/update_cross_build_releases.yml b/.github/workflows/update_cross_build_releases.yml index bcfbda5f1e3..4d75d8a03d7 100644 --- a/.github/workflows/update_cross_build_releases.yml +++ b/.github/workflows/update_cross_build_releases.yml @@ -15,6 +15,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/setup-python@v5 with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..b32d197f231 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "pyodide-build"] + path = pyodide-build + url = https://github.com/pyodide/pyodide-build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b44923b4573..d27f0871a9c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -82,7 +82,7 @@ repos: name: mypy-tests args: [--ignore-missing-imports] files: ^(packages/|docs|/conftest.py|src/tests) - exclude: (^packages/.*/setup.py|/src|^packages/aiohttp/aiohttp_patch.py$) + exclude: (^packages/.*/setup.py|/src|^packages/aiohttp/aiohttp_patch.py$|^packages/zfpy/test_zfpy.py$) additional_dependencies: *mypy-deps - repo: https://github.com/pre-commit/mirrors-prettier diff --git a/Dockerfile b/Dockerfile index f3d47bfe141..ae5eb07b064 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,6 +34,25 @@ RUN pip3 --no-cache-dir install -r requirements.txt \ && pip3 --no-cache-dir install -r requirements-doc.txt \ && rm -rf requirements.txt requirements-doc.txt +RUN cd / \ + && git clone --recursive https://github.com/WebAssembly/wabt \ + && cd wabt \ + && git submodule update --init \ + && make install-gcc-release-no-tests \ + && cd ~ \ + && rm -rf /wabt + +COPY --from=node-image /usr/local/bin/node /usr/local/bin/ +COPY --from=node-image /usr/local/lib/node_modules /usr/local/lib/node_modules +RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx + +RUN npm install -g \ + jsdoc \ + prettier \ + rollup \ + rollup-plugin-terser + # Get Chrome and Firefox (borrowed from https://github.com/SeleniumHQ/docker-selenium) ARG CHROME_VERSION="latest" @@ -99,24 +118,5 @@ RUN if [ $CHROME_VERSION = "latest" ]; \ && echo "Using Chrome version: $(google-chrome --version)" \ && echo "Using Chrome Driver version: $(chromedriver --version)" -COPY --from=node-image /usr/local/bin/node /usr/local/bin/ -COPY --from=node-image /usr/local/lib/node_modules /usr/local/lib/node_modules -RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ - && ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx - -RUN npm install -g \ - jsdoc \ - prettier \ - rollup \ - rollup-plugin-terser - -RUN cd / \ - && git clone --recursive https://github.com/WebAssembly/wabt \ - && cd wabt \ - && git submodule update --init \ - && make install-gcc-release-no-tests \ - && cd ~ \ - && rm -rf /wabt - CMD ["/bin/sh"] WORKDIR /src diff --git a/Makefile b/Makefile index 218619f2cc1..f4f10870b40 100644 --- a/Makefile +++ b/Makefile @@ -225,8 +225,8 @@ $(eval $(call preprocess-js,js2python.js)) .PHONY: pyodide_build pyodide_build: - @echo "Ensuring required pyodide-build version is installed" - ./tools/check_and_install_pyodide_build.py "$(PYODIDE_BUILD_COMMIT)" --repo "$(PYODIDE_BUILD_REPO)" + @echo "Ensuring pyodide-build is installed" + pip install -e ./pyodide-build @which pyodide >/dev/null diff --git a/Makefile.envs b/Makefile.envs index 2b626695d60..54fea4eb4a7 100644 --- a/Makefile.envs +++ b/Makefile.envs @@ -5,15 +5,6 @@ export PYODIDE_ABI_VERSION ?= 2024_0 export PYTHON_ARCHIVE_SHA256=d01ec6a33bc10009b09c17da95cc2759af5a580a7316b3a446eb4190e13f97b2 -# Update the following variables when you want to update the version of the -# pyodide-build version used in the build process. If you want to make breaking -# changes in pyodide-build, it is also useful to change the repository URL -# to your fork to test the changes are working as expected. - -# v0.27.3 -export PYODIDE_BUILD_COMMIT=fac0109aa2acf14469320b049d710dd42639bf94 -export PYODIDE_BUILD_REPO=https://github.com/pyodide/pyodide-build - ifdef CPYTHON_DEBUG export CPYTHON_ABI_FLAGS=d endif @@ -151,8 +142,9 @@ export MAIN_MODULE_LDFLAGS= $(LDFLAGS_BASE) \ -s USE_ZLIB \ -s USE_BZIP2 \ -s FORCE_FILESYSTEM=1 \ - -s TOTAL_MEMORY=20971520 \ + -s INITIAL_MEMORY=20971520 \ -s ALLOW_MEMORY_GROWTH=1 \ + -s MAXIMUM_MEMORY=4GB \ -s EXPORT_ALL=1 \ -s FS_DEBUG=1 \ -s STACK_SIZE=5MB \ @@ -239,8 +231,9 @@ ifeq ($(DISABLE_DYLINK), 1) -s USE_ZLIB \ -s USE_BZIP2 \ -s FORCE_FILESYSTEM=1 \ - -s TOTAL_MEMORY=20971520 \ + -s INITIAL_MEMORY=20971520 \ -s ALLOW_MEMORY_GROWTH=1 \ + -s MAXIMUM_MEMORY=4GB \ -s STACK_SIZE=5MB \ -s ALLOW_TABLE_GROWTH \ -s FS_DEBUG=1 \ diff --git a/conftest.py b/conftest.py index 5765ed1328a..f3d88104fdd 100644 --- a/conftest.py +++ b/conftest.py @@ -31,8 +31,13 @@ def set_configs(): "chrome", pytest_pyodide_config.get_flags("chrome") + [ + # Note: in Chrome > 128 (or so?) WebAssemblyExperimentalJSPI no + # longer implies type reflection. If we passed + # `--enable-experimental-webassembly-features` + # here it would enable type reflection. We'd like to make sure that + # everything works in the configuration where JSPI exists but type + # reflection does not, so we don't pass it. "--enable-features=WebAssemblyExperimentalJSPI", - "--enable-experimental-webassembly-features", ], ) diff --git a/cpython/patches/0001-Public-pymain_run_python.patch b/cpython/patches/0001-Public-pymain_run_python.patch index 9fe9a0cc16f..eb89f2eadfb 100644 --- a/cpython/patches/0001-Public-pymain_run_python.patch +++ b/cpython/patches/0001-Public-pymain_run_python.patch @@ -1,7 +1,7 @@ From 3fe0bfb78022df996fb88da1791d1c8410c07610 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 17 Jul 2022 14:40:39 +0100 -Subject: [PATCH 1/7] Public pymain_run_python +Subject: [PATCH 1/8] Public pymain_run_python Discussion here: https://discuss.python.org/t/unstable-api-for-pymain-run-python-run-python-cli-but-dont-finalize-interpreter/44675 diff --git a/cpython/patches/0002-Add-emscripten-platform-support-to-ctypes.util.find_.patch b/cpython/patches/0002-Add-emscripten-platform-support-to-ctypes.util.find_.patch index 907039c5eae..d66055bdb25 100644 --- a/cpython/patches/0002-Add-emscripten-platform-support-to-ctypes.util.find_.patch +++ b/cpython/patches/0002-Add-emscripten-platform-support-to-ctypes.util.find_.patch @@ -1,7 +1,7 @@ From a68778f7d8d2fa121ff4cab402f2a862e5cb1afe Mon Sep 17 00:00:00 2001 From: ryanking13 Date: Fri, 2 Dec 2022 11:36:44 +0000 -Subject: [PATCH 2/7] Add emscripten platform support to +Subject: [PATCH 2/8] Add emscripten platform support to ctypes.util.find_library --- diff --git a/cpython/patches/0003-Allow-multiprocessing.connection-top-level-import.patch b/cpython/patches/0003-Allow-multiprocessing.connection-top-level-import.patch index 21ba1ee2f03..4c2b54e89c9 100644 --- a/cpython/patches/0003-Allow-multiprocessing.connection-top-level-import.patch +++ b/cpython/patches/0003-Allow-multiprocessing.connection-top-level-import.patch @@ -1,7 +1,7 @@ From cfda6ef57f6c49a3bd8a14ebb989c9e2222f10a6 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 19 Dec 2022 09:09:14 -0800 -Subject: [PATCH 3/7] Allow multiprocessing.connection top level import +Subject: [PATCH 3/8] Allow multiprocessing.connection top level import Upstream PR: https://github.com/python/cpython/pull/114808 diff --git a/cpython/patches/0004-Make-Emscripten-trampolines-work-with-JSPI.patch b/cpython/patches/0004-Make-Emscripten-trampolines-work-with-JSPI.patch index 91c55942917..85483f094f6 100644 --- a/cpython/patches/0004-Make-Emscripten-trampolines-work-with-JSPI.patch +++ b/cpython/patches/0004-Make-Emscripten-trampolines-work-with-JSPI.patch @@ -1,7 +1,7 @@ From a3f1217f7527907c237085c5e900c950c7d2603a Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 28 Jun 2023 10:46:19 -0700 -Subject: [PATCH 4/7] Make Emscripten trampolines work with JSPI +Subject: [PATCH 4/8] Make Emscripten trampolines work with JSPI There is a WIP proposal to enable webassembly stack switching which have been implemented in v8: diff --git a/cpython/patches/0005-Add-fallback-trampoline-for-JSPI-with-no-type-reflec.patch b/cpython/patches/0005-Add-fallback-trampoline-for-JSPI-with-no-type-reflec.patch new file mode 100644 index 00000000000..b5d4ec02925 --- /dev/null +++ b/cpython/patches/0005-Add-fallback-trampoline-for-JSPI-with-no-type-reflec.patch @@ -0,0 +1,34 @@ +From 2119fa65d7af744fd0050214d3452312bbb8d063 Mon Sep 17 00:00:00 2001 +From: Hood Chatham +Date: Tue, 22 Oct 2024 15:16:03 +0200 +Subject: [PATCH 5/8] Add fallback trampoline for JSPI with no type reflection + +--- + Python/emscripten_trampoline.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c +index 8d29393bd87..812cc10167f 100644 +--- a/Python/emscripten_trampoline.c ++++ b/Python/emscripten_trampoline.c +@@ -10,7 +10,7 @@ + * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js + */ + EM_JS(int, _PyEM_detect_type_reflection, (), { +- return "Function" in WebAssembly; ++ return !!Module.PyEM_CountArgs; + }); + + void +@@ -36,7 +36,7 @@ EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { + if (n !== undefined) { + return n; + } +- n = wasmFunctionType(wasmTable.get(func)).parameters.length; ++ n = Module.PyEM_CountArgs(func); + _PyEM_CountFuncParams.cache.set(func, n); + return n; + } +-- +2.34.1 + diff --git a/cpython/patches/0005-Fix-LONG_BIT-constant-to-be-always-32bit.patch b/cpython/patches/0006-Fix-LONG_BIT-constant-to-be-always-32bit.patch similarity index 88% rename from cpython/patches/0005-Fix-LONG_BIT-constant-to-be-always-32bit.patch rename to cpython/patches/0006-Fix-LONG_BIT-constant-to-be-always-32bit.patch index 6f9c1014b05..ec64d1186c8 100644 --- a/cpython/patches/0005-Fix-LONG_BIT-constant-to-be-always-32bit.patch +++ b/cpython/patches/0006-Fix-LONG_BIT-constant-to-be-always-32bit.patch @@ -1,7 +1,7 @@ -From 986bb199865ea5ec81aaec40e02a51d90f339da4 Mon Sep 17 00:00:00 2001 +From 0539585c0d6ed3df3199ce248f5b4d59e89bb6f5 Mon Sep 17 00:00:00 2001 From: ryanking13 Date: Fri, 12 Jan 2024 00:52:57 +0900 -Subject: [PATCH 5/7] Fix LONG_BIT constant to be always 32bit +Subject: [PATCH 6/8] Fix LONG_BIT constant to be always 32bit Starting from Emscripten 3.1.50, there is an issue where LONG_BIT is calculated to 64 for some reason. This is very strange because LONG_MAX diff --git a/cpython/patches/0006-Warn-if-ZoneInfo-is-imported-without-tzdata.patch b/cpython/patches/0007-Warn-if-ZoneInfo-is-imported-without-tzdata.patch similarity index 88% rename from cpython/patches/0006-Warn-if-ZoneInfo-is-imported-without-tzdata.patch rename to cpython/patches/0007-Warn-if-ZoneInfo-is-imported-without-tzdata.patch index 0204bff9693..48798244982 100644 --- a/cpython/patches/0006-Warn-if-ZoneInfo-is-imported-without-tzdata.patch +++ b/cpython/patches/0007-Warn-if-ZoneInfo-is-imported-without-tzdata.patch @@ -1,7 +1,7 @@ -From 2643d68535a5ab6c8b4d9105257e53f3a2498b46 Mon Sep 17 00:00:00 2001 +From 0ccdd69344894759ca9de6d74c32b9843f099c90 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 25 Jul 2024 14:28:57 +0200 -Subject: [PATCH 6/7] Warn if ZoneInfo is imported without tzdata +Subject: [PATCH 7/8] Warn if ZoneInfo is imported without tzdata --- Lib/zoneinfo/_common.py | 6 ++++++ diff --git a/cpython/patches/0007-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch b/cpython/patches/0008-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch similarity index 89% rename from cpython/patches/0007-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch rename to cpython/patches/0008-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch index 4fc8ac90c11..2e805b73e56 100644 --- a/cpython/patches/0007-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch +++ b/cpython/patches/0008-Add-call-to-JsProxy_GetMethod-to-help-remove-tempora.patch @@ -1,9 +1,9 @@ -From 0f4f4f02bda0f6ee4efd24e8c2670e8bc80194a8 Mon Sep 17 00:00:00 2001 +From f9b7a02aba3bf050189f3347dce5fcd39bf7dc8d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 25 Jul 2024 14:41:37 +0200 -Subject: [PATCH 7/7] Add call to `JsProxy_GetMethod` to help remove temporary +Subject: [PATCH 8/8] Add call to `JsProxy_GetMethod` to help remove temporary -_PyObject_GetMethod is a special attribute lookup function that won't call the +`_PyObject_GetMethod` is a special attribute lookup function that won't call the `__get__` descriptor on a method to avoid creating a temporary PyMethodObject. We also want to optimize away a temporary JsProxy in a special way. In order to do this, we patch the behavior of `_PyObject_GetMethod` to use @@ -16,7 +16,6 @@ function. Otherwise, this patch does nothing. See the definition of `JsProxy_GetMethod` in `jsproxy.c` and particularly `JsMethodCallSingleton` for how this is used. - --- Objects/object.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/development/contributing.md b/docs/development/contributing.md index 92283fc807a..b1c7529304c 100644 --- a/docs/development/contributing.md +++ b/docs/development/contributing.md @@ -29,7 +29,7 @@ To contribute code, see the following steps, 3. Clone your fork of Pyodide ``` - git clone https://github.com//pyodide.git + git clone --recursive https://github.com//pyodide.git ``` and add the upstream remote, ``` diff --git a/docs/development/maintainers.md b/docs/development/maintainers.md index f4caa870bf2..0d0e2033d27 100644 --- a/docs/development/maintainers.md +++ b/docs/development/maintainers.md @@ -193,8 +193,27 @@ note to update them independently. ## Updating pyodide-build -to change the version of pyodide-build, update the PYODIDE_BUILD_REPO and PYODIDE_BUILD_COMMIT -variables in `Makefile.envs`. +to change the version of pyodide-build, change the commit of the pyodide-build submodule. + +```bash +cd pyodide-build +git checkout "" +``` + +to test with the fork of pyodide-build, change the `.gitmodules` file to point to your fork and update the commit hash + +```ini +# .gitmodules +[submodule "pyodide-build"] + path = pyodide-build + url = https://github.com//pyodide-build +``` + +```bash +git submodule sync +cd pyodide-build +git checkout " +Date: Wed, 6 Nov 2024 17:37:22 -0500 +Subject: [PATCH 3/3] install_name + +--- + setup.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/setup.py b/setup.py +index 6443cb27..965b2fc9 100644 +--- a/setup.py ++++ b/setup.py +@@ -23,8 +23,8 @@ except: + extra_link_args=[] if sys.platform == 'darwin': - from distutils import sysconfig - vars = sysconfig.get_config_vars() -- vars['LDSHARED'] = vars['LDSHARED'].replace('-bundle', '-shared') + config_vars = sysconfig.get_config_vars() +- config_vars['LDSHARED'] = config_vars['LDSHARED'].replace('-bundle', '-shared') - extra_link_args=['-Wl,-install_name,@rpath/librebound'+suffix] -+ # vars['LDSHARED'] = vars['LDSHARED'].replace('-bundle', '-shared') -+ # extra_link_args=['-Wl,-install_name,@rpath/librebound'+suffix] - - libreboundmodule = Extension('librebound', - sources = [ 'src/rebound.c', ++ #config_vars['LDSHARED'] = config_vars['LDSHARED'].replace('-bundle', '-shared') ++ #extra_link_args=['-Wl,-install_name,@rpath/librebound'+suffix] + if sys.platform == 'win32': + extra_compile_args=[ghash_arg, '-DLIBREBOUND', '-D_GNU_SOURCE', '-DSERVER'] + else: +-- +2.39.5 (Apple Git-154) + diff --git a/packages/rebound/patches/0002-fix-output.patch b/packages/rebound/patches/0002-fix-output.patch new file mode 100644 index 00000000000..1b7bc3296de --- /dev/null +++ b/packages/rebound/patches/0002-fix-output.patch @@ -0,0 +1,57 @@ +From f875c41a58ae4b3d59d97a5787ec3e546c737860 Mon Sep 17 00:00:00 2001 +From: Hanno Rein +Date: Wed, 6 Nov 2024 17:21:47 -0500 +Subject: [PATCH] output + +--- + src/output.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/output.c b/src/output.c +index e897743a..fdd4ea45 100644 +--- a/src/output.c ++++ b/src/output.c +@@ -349,7 +349,7 @@ void reb_simulation_output_timing(struct reb_simulation* r, const double tmax){ + r->output_timing_last = temp; + }else{ + #ifdef __EMSCRIPTEN__ +- reb_remove_last_line(); ++ // reb_remove_last_line(); + #else + printf("\r"); + #endif +--- a/src/output.c ++++ b/src/output.c +@@ -257,17 +257,17 @@ void profiling_stop(int cat){ + + #ifdef __EMSCRIPTEN__ + // fflush does not work in emscripten. Workaround. +-EM_JS(void, reb_remove_last_line, (), { +- var output = document.getElementById("output"); +- if (output){ +- const lastIndex1 = output.value.lastIndexOf("\n"); +- const lastIndex2 = output.value.lastIndexOf("\n",lastIndex1-1); +- const lastIndexNtot = output.value.lastIndexOf("N_tot="); +- if(lastIndex1>0 && lastIndex20 && lastIndex2 +Date: Thu, 7 Nov 2024 09:40:09 -0500 +Subject: [PATCH 4/4] no emscripten + +--- + src/rebound.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/rebound.c b/src/rebound.c +index 82f6ac60..accea978 100644 +--- a/src/rebound.c ++++ b/src/rebound.c +@@ -665,7 +665,7 @@ int reb_check_exit(struct reb_simulation* const r, const double tmax, double* la + while(r->status == REB_STATUS_PAUSED || r->status == REB_STATUS_SCREENSHOT){ + // Wait for user to disable paused simulation + #ifdef __EMSCRIPTEN__ +- emscripten_sleep(100); ++ // emscripten_sleep(100); + #else + usleep(1000); + #endif +@@ -820,16 +820,16 @@ static void* reb_simulation_integrate_raw(void* args){ + } + reb_run_heartbeat(r); + #ifdef __EMSCRIPTEN__ +- double t0 = emscripten_performance_now(); ++// double t0 = emscripten_performance_now(); + #endif + while(reb_check_exit(r,thread_info->tmax,&last_full_dt)<0){ + #ifdef __EMSCRIPTEN__ +- double t1 = emscripten_performance_now(); +- if (t1-t0>1000./120.){ // max framerate 120Hz +- t0 = t1; +- emscripten_sleep(0); // allow drawing and event handling +- } +- ++// double t1 = emscripten_performance_now(); ++// if (t1-t0>1000./120.){ // max framerate 120Hz ++// t0 = t1; ++// emscripten_sleep(0); // allow drawing and event handling ++// } ++// + #endif + #ifdef OPENGL + if (r->display_data){ +-- +2.39.5 (Apple Git-154) + diff --git a/packages/reboundx/meta.yaml b/packages/reboundx/meta.yaml index fa52882cb4d..1ff826103a9 100644 --- a/packages/reboundx/meta.yaml +++ b/packages/reboundx/meta.yaml @@ -1,6 +1,6 @@ package: name: reboundx - version: 3.10.1 + version: 4.3.0 top-level: - reboundx build: @@ -10,8 +10,8 @@ build: ldflags: | -L$(WASM_LIBRARY_DIR)/lib/ source: - url: https://files.pythonhosted.org/packages/14/08/c1c0b83c90ea43ea8e218beadb13332259e930a2ca62f85024be89d811f3/reboundx-3.10.1.tar.gz - sha256: 65b2fcecf296661aad15a81a8d7d7b7b96cc2ba834c89e49dba24445f146d642 + url: https://files.pythonhosted.org/packages/2c/c2/32bb671c3f6944755b90c75d20f387daf4b9fc1e6919264cb0e9e26af8d7/reboundx-4.3.0.tar.gz + sha256: 94fc5b0993b623faa9964800f87fbc97236f7f12cb639c659d25702f1e0ac0ec requirements: run: - rebound diff --git a/packages/zfpy/meta.yaml b/packages/zfpy/meta.yaml new file mode 100644 index 00000000000..b883a3a76df --- /dev/null +++ b/packages/zfpy/meta.yaml @@ -0,0 +1,37 @@ +package: + name: zfpy + version: 1.0.1 + top-level: + - zfpy + +source: + # There's no sdist for zfpy on PyPI, so we get the tarball from the GitHub release instead + url: https://github.com/LLNL/zfp/archive/refs/tags/1.0.1.tar.gz + sha256: 4984db6a55bc919831966dd17ba5e47ca7ac58668f4fd278ebd98cd2200da66f + patches: + - patches/Modernise-packaging.patch + +build: + exports: requested + cflags: | + -I$(PYTHONINCLUDE) + -I$(WASM_LIBRARY_DIR)/include + ldflags: | + -L$(WASM_LIBRARY_DIR)/lib + +requirements: + host: + - libzfp + - numpy + run: + - numpy + +test: + imports: + - zfpy + +about: + home: https://zfp.llnl.gov/ + PyPI: https://pypi.org/project/zfpy/ + summary: zfp compression in Python + license: BSD-3-Clause diff --git a/packages/zfpy/patches/Modernise-packaging.patch b/packages/zfpy/patches/Modernise-packaging.patch new file mode 100644 index 00000000000..411f7835bf2 --- /dev/null +++ b/packages/zfpy/patches/Modernise-packaging.patch @@ -0,0 +1,69 @@ +From f36ef86e67185b16b65445092c40a3d75d0be45c Mon Sep 17 00:00:00 2001 +From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> +Date: Fri, 8 Nov 2024 02:46:00 +0530 +Subject: [PATCH] Modernise packaging + +This patch introduces a PEP 517/518 compliant build system using setuptools and +declarative metadata in pyproject.toml, and fixes syntax errors in setup.py. The +sources for the extension module are correctly specified as Cython source files. + +Some part of the credits go to the upstream +PR at https://github.com/LLNL/zfp/pull/234 + +Co-Authored-By: David M. Rogers <2243447+frobnitzem@users.noreply.github.com> +--- + pyproject.toml | 13 +++++++++++++ + setup.py | 18 +++++++++--------- + 2 files changed, 22 insertions(+), 9 deletions(-) + create mode 100644 pyproject.toml + +diff --git a/pyproject.toml b/pyproject.toml +new file mode 100644 +index 0000000..88adcc8 +--- /dev/null ++++ b/pyproject.toml +@@ -0,0 +1,13 @@ ++[build-system] ++requires = ["setuptools", "cython", "numpy"] ++build-backend = "setuptools.build_meta" ++ ++[project] ++name = "zfpy" ++version = "1.0.1" ++authors = [ ++ { name = "Peter Lindstrom", email = "zfp@llnl.gov" }, ++ { name = "Danielle Asher", email = "zfp@llnl.gov" }, ++] ++description = "zfp compression in Python" ++requires-python = ">=3.0" +diff --git a/setup.py b/setup.py +index fa2da6e..919fa7c 100644 +--- a/setup.py ++++ b/setup.py +@@ -2,14 +2,14 @@ from setuptools import setup, Extension + import numpy as np + + setup( +- name="zfpy", +- version="1.0.1", +- author="Peter Lindstrom, Danielle Asher", +- author_email="zfp@llnl.gov", + url="https://zfp.llnl.gov", +- description="zfp compression in Python", +- long_description="zfp is a compressed format for representing multidimensional floating-point and integer arrays. zfp provides compressed-array classes that support high throughput read and write random access to individual array elements. zfp also supports serial and parallel compression of whole arrays using both lossless and lossy compression with error tolerances. zfp is primarily written in C and C++ but also includes Python and Fortran bindings.", +- ext_modules=[Extension("zfpy", ["build/python/zfpy.c"], +- include_dirs=["include", np.get_include()], +- libraries=["zfp"], library_dirs=["build/lib64", "build/lib/Release"]), language_level = "3"] ++ ext_modules= ++ [ ++ Extension( ++ name="zfpy", ++ sources=["python/zfpy.pyx"], ++ include_dirs=["include", np.get_include()], ++ libraries=["zfp"], ++ library_dirs=["build/lib64", "build/lib"]) ++ ], + ) +-- +2.39.3 (Apple Git-146) + diff --git a/packages/zfpy/test_zfpy.py b/packages/zfpy/test_zfpy.py new file mode 100644 index 00000000000..0896f323d32 --- /dev/null +++ b/packages/zfpy/test_zfpy.py @@ -0,0 +1,74 @@ +from pytest_pyodide import run_in_pyodide + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_compression(selenium): + import numpy as np + import zfpy + + my_array = np.arange(1, 20) + compressed_data = zfpy.compress_numpy(my_array) + decompressed_array = zfpy.decompress_numpy(compressed_data) + np.testing.assert_array_equal(my_array, decompressed_array) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_compression_with_tolerance(selenium): + import numpy as np + import zfpy + + my_array = np.linspace(0, 1, 1000) + compressed_data = zfpy.compress_numpy(my_array, tolerance=1e-3) + decompressed_array = zfpy.decompress_numpy(compressed_data) + np.testing.assert_allclose(my_array, decompressed_array, atol=1e-3) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_different_dimensions(selenium) -> None: + import numpy as np + import zfpy + + np.random.seed(42) + + # Test arrays; from 1D to 4D + for dimensions in range(1, 5): + # 1. test with uniform dimensions + shape1 = tuple([5] * dimensions) + array = np.random.rand(*shape1).astype(np.float64) + compressed1 = zfpy.compress_numpy(array, write_header=True) + decompressed1 = zfpy.decompress_numpy(compressed1) + np.testing.assert_array_equal(decompressed1, array) + + # 2. test with increasing dimensions + shape2 = tuple(range(2, 2 + dimensions)) + array = np.random.rand(*shape2).astype(np.float64) + compressed2 = zfpy.compress_numpy(array, write_header=True) + decompressed2 = zfpy.decompress_numpy(compressed2) + np.testing.assert_array_equal(decompressed2, array) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_different_dtypes(selenium) -> None: + """Test ZFP compression/decompression with different numeric dtypes.""" + import numpy as np + import zfpy + + np.random.seed(42) + + shape = (5, 5) + num_elements = np.prod(shape) + + # Test floating-point types + for dtype in [np.float32, np.float64]: + elements = np.random.random_sample(num_elements) + array = np.reshape(elements, shape).astype(dtype) + compressed1 = zfpy.compress_numpy(array, write_header=True) + decompressed1 = zfpy.decompress_numpy(compressed1) + np.testing.assert_array_equal(decompressed1, array) + + # Test integer types + for dtype in [np.int32, np.int64]: + array = np.random.randint(low=-(2**30), high=2**30, size=shape, dtype=dtype) + compressed2 = zfpy.compress_numpy(array, write_header=True) + decompressed2 = zfpy.decompress_numpy(compressed2) + np.testing.assert_array_equal(decompressed2, array) diff --git a/pyodide-build b/pyodide-build new file mode 160000 index 00000000000..fac0109aa2a --- /dev/null +++ b/pyodide-build @@ -0,0 +1 @@ +Subproject commit fac0109aa2acf14469320b049d710dd42639bf94 diff --git a/pyodide-cross-build-environments.json b/pyodide-cross-build-environments.json index f60588ccdbb..d69c3d5437e 100644 --- a/pyodide-cross-build-environments.json +++ b/pyodide-cross-build-environments.json @@ -8,6 +8,14 @@ "emscripten_version": "3.1.58", "min_pyodide_build_version": "0.26.0" }, + "0.26.3": { + "version": "0.26.3", + "url": "https://github.com/pyodide/pyodide/releases/download/0.26.3/xbuildenv-0.26.3.tar.bz2", + "sha256": "538b00bb7b329f96e6f981464bc7875109b9779f8d121e2140235c375f530675", + "python_version": "3.12.1", + "emscripten_version": "3.1.58", + "min_pyodide_build_version": "0.26.0" + }, "0.26.2": { "version": "0.26.2", "url": "https://github.com/pyodide/pyodide/releases/download/0.26.2/xbuildenv-0.26.2.tar.bz2", diff --git a/requirements.txt b/requirements.txt index a520fb49b29..55363655789 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,6 @@ pytest-cov pytest-httpserver pytest-benchmark pytest-pyodide==0.58.3 +setuptools; python_version >= '3.12' +# TODO(cclauss): Remove the Selenium line. +selenium==4.25.0 diff --git a/run_docker b/run_docker index 33907da1002..0632be6739c 100755 --- a/run_docker +++ b/run_docker @@ -1,7 +1,7 @@ #!/usr/bin/env bash PYODIDE_IMAGE_REPO="pyodide" -PYODIDE_IMAGE_TAG="20240928-chrome127-firefox128" +PYODIDE_IMAGE_TAG="20241028-chrome130-firefox131" DEFAULT_PYODIDE_DOCKER_IMAGE="${PYODIDE_IMAGE_REPO}/pyodide-env:${PYODIDE_IMAGE_TAG}" DEFAULT_PYODIDE_SYSTEM_PORT="none" DOCKER_COMMAND="/bin/bash" diff --git a/src/core/stack_switching/calculate_wasm_func_nargs_fallback.mjs b/src/core/stack_switching/calculate_wasm_func_nargs_fallback.mjs new file mode 100644 index 00000000000..0fef878e5ab --- /dev/null +++ b/src/core/stack_switching/calculate_wasm_func_nargs_fallback.mjs @@ -0,0 +1,90 @@ +// Various function pointer mismatch bugs occur because people declare a +// `METH_NOARGS` function which should take 2 arguments: +// ``` +// my_no_arg_meth(PyObject* module, PyObject* always_null); +// ``` +// but leave off the `always_null` second argument or both arguments. Similar +// errors occur less frequently with `METH_VARARGS | METH_KWDS` functions. When +// the interpreter tries to use a call_indirect to invoke these methods, we hit +// an indirect call signature mismatch fatal error. +// +// Traditionally we used a JS trampoline to deal with this, because calls from +// JS to Wasm don't care if the wrong number of arguments are passed. However, +// these trampolines do not work with JSPI because we cannot stack switch +// through JavaScript frames. +// +// Originally JSPI implied wasm type reflection, so we could ask JS what the +// type of the function was and then select a `call_indirect` with the right +// number of arguments based on this result. +// +// The new JSPI does not imply that wasm type reflection exists, so we need a +// way to handle the case when JSPI is available but wasm type reflection is +// not. What we do here is make a tiny WebAssembly module that attempts to +// import a single function. If the signature of the function matches the +// signature of the import, it will succeed. Otherwise, we will raise a +// LinkError. +// +// We only need to handle functions with four different possible signatures: +// (n i32's) => i32 where n is between 0 and 3. So we try to link at most 4 +// different Wasm modules and find out the signature. + +const checkerModules = [undefined, undefined, undefined, undefined]; + +function makeCheckerModule(n) { + // Make a webassembly module of the form: + // + // (module + // (import "e" "f" (func (param i32 ... i32) (result i32))) + // ) + // + // with n i32's. We'll try to import the function to this module to see if it + // has this signature. If not it rasises a link error. + return new WebAssembly.Module( + // prettier-ignore + new Uint8Array([ + 0, 97, 115, 109, // magic number + 1, 0, 0, 0, // wasm version + // Type section code and byte length + 1, 5 + n, + 1, // One type + // Type is (i32 i32 ... i32) => i32 where there are n i32's + 96, n, ...Array.from({ length: n }, () => 127), + 1, 127, + // Import section code and byte length + 2, 7, + // One import + 1, + // "e" "f" + 1, 101, 1, 102, + // A function of type 0 + 0, 0, + ]), + ); +} + +function getCheckerModule(n) { + if (checkerModules[n] === undefined) { + checkerModules[n] = makeCheckerModule(n); + } + return checkerModules[n]; +} + +export function calculateWasmFuncNargsFallback(functionPtr) { + for (let n = 0; n < 4; n++) { + const mod = getCheckerModule(n); + const imports = { + e: { f: wasmTable.get(functionPtr) }, + }; + try { + new WebAssembly.Instance(mod, imports); + return n; + } catch (e) { + // Should be a LinkError, if not we have a logic error. Raise fatal error + // so it's noisy. + if (!(e instanceof WebAssembly.LinkError)) { + throw e; + } + } + } + return -1; +} diff --git a/src/core/stack_switching/stack_switching.mjs b/src/core/stack_switching/stack_switching.mjs index d1ef8091c9b..9b36be2a222 100644 --- a/src/core/stack_switching/stack_switching.mjs +++ b/src/core/stack_switching/stack_switching.mjs @@ -10,6 +10,7 @@ import { createInvoke, } from "./create_invokes.mjs"; import { initSuspenders } from "./suspenders.mjs"; +import { calculateWasmFuncNargsFallback } from "./calculate_wasm_func_nargs_fallback.mjs"; export { promisingApply, @@ -48,3 +49,22 @@ if (jspiSupported) { Module.wrapException = wrapException; Module.createInvoke = createInvoke; } + +function getPyEmCountArgs() { + if ("Function" in WebAssembly) { + if (WebAssembly.Function.type) { + // Node v20 + return (func) => + WebAssembly.Function.type(wasmTable.get(func)).parameters.length; + } else { + // Node >= 22, v8-based browsers + return (func) => wasmTable.get(func).type().parameters.length; + } + } + if ("Suspending" in WebAssembly) { + return calculateWasmFuncNargsFallback; + } + return undefined; +} + +Module.PyEM_CountArgs = getPyEmCountArgs(); diff --git a/src/js/environments.ts b/src/js/environments.ts index bc42780b922..f9ce46d9af7 100644 --- a/src/js/environments.ts +++ b/src/js/environments.ts @@ -33,7 +33,7 @@ export const IN_BROWSER_MAIN_THREAD = typeof window === "object" && typeof document === "object" && typeof document.createElement === "function" && - typeof sessionStorage === "object" && + "sessionStorage" in window && typeof importScripts !== "function"; /** @private */ diff --git a/src/tests/test_cmdline_runner.py b/src/tests/test_cmdline_runner.py index a03f69f0289..cad4cd7ae09 100644 --- a/src/tests/test_cmdline_runner.py +++ b/src/tests/test_cmdline_runner.py @@ -445,7 +445,7 @@ def test_package_index(tmp_path): version = "0.26.0" # just need some version that already exists + contains pyodide-lock.json mgr = CrossBuildEnvManager(path) - mgr.install(version, skip_install_cross_build_packages=True) + mgr.install(version, skip_install_cross_build_packages=True, force_install=True) env_path = mgr.symlink_dir.resolve() diff --git a/src/tests/test_typeconversions.py b/src/tests/test_typeconversions.py index 36151a80d18..6e9c9afdfe9 100644 --- a/src/tests/test_typeconversions.py +++ b/src/tests/test_typeconversions.py @@ -1657,234 +1657,6 @@ class A: assert res.a == 2 -@pytest.mark.parametrize("n", [1 << 31, 1 << 32, 1 << 33, 1 << 63, 1 << 64, 1 << 65]) -@run_in_pyodide -def test_very_large_length(selenium, n): - from unittest import TestCase - - from pyodide.code import run_js - - raises = TestCase().assertRaises( - OverflowError, msg=f"length {n} of object is larger than INT_MAX (2147483647)" - ) - - o = run_js(f"({{length : {n}}})") - with raises: - len(o) - - # 1. Set toStringTag to NodeList to force JsProxy to feature detect this object - # as an array - # 2. Return a very large length - # 3. JsProxy_subscript_array should successfully handle this and propagate the error. - a = run_js(f"({{[Symbol.toStringTag] : 'NodeList', length: {n}}})") - with raises: - a[-1] - - -@pytest.mark.parametrize( - "n", [-1, -2, -3, -100, -1 << 31, -1 << 32, -1 << 33, -1 << 63, -1 << 64, -1 << 65] -) -@run_in_pyodide -def test_negative_length(selenium, n): - from unittest import TestCase - - from pyodide.code import run_js - - raises = TestCase().assertRaises( - ValueError, msg=f"length {n} of object is negative" - ) - - o = run_js(f"({{length : {n}}})") - with raises: - len(o) - - # 1. Set toStringTag to NodeList to force JsProxy to feature detect this object - # as an array - # 2. Return a negative length - # 3. JsProxy_subscript_array should successfully handle this and propagate the error. - a = run_js(f"({{[Symbol.toStringTag] : 'NodeList', length: {n}}})") - with raises: - a[-1] - - -@std_hypothesis_settings -@given(l=st.lists(st.integers()), slice=st.slices(50)) -@example(l=[0, 1], slice=slice(None, None, -1)) -@example(l=list(range(4)), slice=slice(None, None, -2)) -@example(l=list(range(10)), slice=slice(-1, 12)) -@example(l=list(range(10)), slice=slice(12, -1)) -@example(l=list(range(10)), slice=slice(12, -1, -1)) -@example(l=list(range(10)), slice=slice(-1, 12, 2)) -@example(l=list(range(10)), slice=slice(12, -1, -1)) -@example(l=list(range(10)), slice=slice(12, -1, -2)) -@run_in_pyodide -def test_array_slices(selenium, l, slice): - expected = l[slice] - from pyodide.ffi import JsArray, to_js - - jsl = to_js(l) - assert isinstance(jsl, JsArray) - result = jsl[slice] - assert result.to_py() == expected - - -@std_hypothesis_settings -@given(l=st.lists(st.integers()), slice=st.slices(50)) -@example(l=[0, 1], slice=slice(None, None, -1)) -@example(l=list(range(4)), slice=slice(None, None, -2)) -@example(l=list(range(10)), slice=slice(-1, 12)) -@example(l=list(range(10)), slice=slice(12, -1)) -@example(l=list(range(10)), slice=slice(12, -1, -1)) -@example(l=list(range(10)), slice=slice(-1, 12, 2)) -@example(l=list(range(10)), slice=slice(12, -1, -1)) -@example(l=list(range(10)), slice=slice(12, -1, -2)) -@run_in_pyodide -def test_array_slice_del(selenium, l, slice): - from pyodide.ffi import JsArray, to_js - - jsl = to_js(l) - assert isinstance(jsl, JsArray) - del l[slice] - del jsl[slice] - assert jsl.to_py() == l - - -@st.composite -def list_slice_and_value(draw): - l = draw(st.lists(st.integers())) - step_one = draw(st.booleans()) - if step_one: - start = draw(st.integers(0, max(len(l) - 1, 0)) | st.none()) - stop = draw(st.integers(start, len(l)) | st.none()) - if draw(st.booleans()) and start is not None: - start -= len(l) - if draw(st.booleans()) and stop is not None: - stop -= len(l) - s = slice(start, stop) - vals = draw(st.lists(st.integers())) - else: - s = draw(st.slices(50)) - vals_len = len(l[s]) - vals = draw(st.lists(st.integers(), min_size=vals_len, max_size=vals_len)) - return (l, s, vals) - - -@std_hypothesis_settings -@given(lsv=list_slice_and_value()) -@example(lsv=(list(range(5)), slice(5, 2), [])) -@example(lsv=(list(range(5)), slice(2, 5, -1), [])) -@example(lsv=(list(range(5)), slice(5, 2), [-1, -2, -3])) -@run_in_pyodide -def test_array_slice_assign_1(selenium, lsv): - from pyodide.ffi import JsArray, to_js - - [l, s, v] = lsv - jsl = to_js(l) - assert isinstance(jsl, JsArray) - l[s] = v - jsl[s] = v - assert jsl.to_py() == l - - -@run_in_pyodide -def test_array_slice_assign_2(selenium): - import pytest - - from pyodide.ffi import JsArray, to_js - - l = list(range(10)) - with pytest.raises(ValueError) as exc_info_1a: - l[0:4:2] = [1, 2, 3, 4] - - jsl = to_js(l) - assert isinstance(jsl, JsArray) - with pytest.raises(ValueError) as exc_info_1b: - jsl[0:4:2] = [1, 2, 3, 4] - - l = list(range(10)) - with pytest.raises(ValueError) as exc_info_2a: - l[0:4:2] = [] - - with pytest.raises(ValueError) as exc_info_2b: - jsl[0:4:2] = [] - - with pytest.raises(TypeError) as exc_info_3a: - l[:] = 1 # type: ignore[call-overload] - - with pytest.raises(TypeError) as exc_info_3b: - jsl[:] = 1 # type: ignore[call-overload] - - assert exc_info_1a.value.args == exc_info_1b.value.args - assert exc_info_2a.value.args == exc_info_2b.value.args - assert exc_info_3a.value.args == exc_info_3b.value.args - - -@std_hypothesis_settings -@given(l1=st.lists(st.integers()), l2=st.lists(st.integers())) -@example(l1=[], l2=[]) -@example(l1=[], l2=[1]) -@run_in_pyodide -def test_array_extend(selenium_module_scope, l1, l2): - from pyodide.ffi import to_js - - l1js1 = to_js(l1) - l1js1.extend(l2) - - l1js2 = to_js(l1) - l1js2 += l2 - - l1.extend(l2) - - assert l1 == l1js1.to_py() - assert l1 == l1js2.to_py() - - -@run_in_pyodide -def test_typed_array(selenium): - from pyodide.code import run_js - - a = run_js("self.a = new Uint8Array([1,2,3,4]); a") - assert a[0] == 1 - assert a[-1] == 4 - a[-2] = 7 - assert run_js("self.a[2]") == 7 - - import pytest - - with pytest.raises(TypeError, match="does ?n[o']t support item deletion"): - del a[0] - - msg = "Slice subscripting isn't implemented for typed arrays" - with pytest.raises(NotImplementedError, match=msg): - a[:] - - msg = "Slice assignment isn't implemented for typed arrays" - with pytest.raises(NotImplementedError, match=msg): - a[:] = [-1, -2, -3, -4] - - assert not hasattr(a, "extend") - with pytest.raises(TypeError): - a += [1, 2, 3] - - -@pytest.mark.xfail_browsers(node="No document in node") -@run_in_pyodide -def test_html_array(selenium): - from pyodide.code import run_js - - x = run_js("document.querySelectorAll('*')") - assert run_js("(a, b) => a === b[0]")(x[0], x) - assert run_js("(a, b) => a === Array.from(b).pop()")(x[-1], x) - - import pytest - - with pytest.raises(TypeError, match="does ?n[o']t support item assignment"): - x[0] = 0 - - with pytest.raises(TypeError, match="does ?n[o']t support item deletion"): - del x[0] - - @run_in_pyodide def test_bind_attrs(selenium): from typing import Annotated diff --git a/tools/bump_version.py b/tools/bump_version.py index 1e5c81cab41..e5cec786793 100755 --- a/tools/bump_version.py +++ b/tools/bump_version.py @@ -6,7 +6,7 @@ import itertools import pathlib import re -from ast import Str +from ast import Constant from collections import namedtuple from collections.abc import Callable @@ -81,7 +81,7 @@ def build_version_pattern(pattern): @functools.lru_cache -def python_version_to_js_version(version: str) -> Str: +def python_version_to_js_version(version: str) -> Constant: """ Convert Python version name to JS version name These two are different in prerelease or dev versions. diff --git a/tools/check_and_install_pyodide_build.py b/tools/check_and_install_pyodide_build.py deleted file mode 100755 index a77cc96097e..00000000000 --- a/tools/check_and_install_pyodide_build.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import subprocess as sp -import sys - - -def get_pyodide_build_install_url() -> str | None: - """ - Return the version of the pyodide-build package or the URL to the repository. - """ - freeze_result = sp.check_output( - [ - sys.executable, - "-m", - "pip", - "freeze", - ] - ) - - for line in freeze_result.decode().split("\n"): - if line.startswith("pyodide-build"): - try: - return line.split(" @ ")[1] - except IndexError: - print("pyodide-build is not installed from a VCS: ", line) - return None - - return None - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("commit", type=str) - parser.add_argument( - "--repo", type=str, default="https://github.com/pyodide/pyodide-build" - ) - - return parser.parse_args() - - -def main(): - args = parse_args() - install_url = f"git+{args.repo}@{args.commit}" - installed_url = get_pyodide_build_install_url() - - if not installed_url or installed_url != install_url: - sp.check_call( - [ - sys.executable, - "-m", - "pip", - "install", - install_url, - ] - ) - - -if __name__ == "__main__": - main()