From 7bdeaff6e8a2c5cbdb01aed817c4a66605cd8abc Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sat, 10 May 2025 11:08:09 -0400 Subject: [PATCH 01/44] Pyodide: force only one thread --- src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index dad6d95648..e80d04f083 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -703,5 +703,21 @@ fn rustworkx(py: Python<'_>, m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_wrapped(wrap_pymodule!(generators::generators))?; + #[cfg(target_os = "emscripten")] + setup_rayon_for_pyodide(); Ok(()) } + +#[cfg(target_os = "emscripten")] +static PYODIDE_INIT: std::sync::Once = std::sync::Once::new(); + +#[cfg(target_os = "emscripten")] +pub fn setup_rayon_for_pyodide() { + PYODIDE_INIT.call_once(|| { + rayon::ThreadPoolBuilder::new() + .num_threads(1) + .use_current_thread() + .build_global() + .expect("failing setting up threads for pyodide"); + }); +} From 7ce8078d3d76cf5c83634a3a2f7cb9e2a7006da4 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sat, 10 May 2025 23:24:17 -0400 Subject: [PATCH 02/44] Add .cargo/config.toml --- .cargo/config.toml | 10 ++++++++++ Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d47f983e47..e8d250e937 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -9,3 +9,13 @@ rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup", ] + +[target.wasm32-unknown-emscripten] +rustflags = [ + "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", + "-C", "link-arg=-s", "-C", "link-arg=ALLOW_MEMORY_GROWTH=1", + "-C", "link-arg=-s", "-C", "link-arg=MODULARIZE=1", + "-C", "link-arg=-s", "-C", "link-arg=EXPORT_NAME=createModule", + "-C", "link-arg=-s", "-C", "link-arg=EXPORT_ES6=1", + "-C", "link-arg=-s", "-C", "link-arg=ASSERTIONS=1" +] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 2b2c09c0b0..fb7d64731c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,5 +75,5 @@ default-features = false features = ["multi_thread"] [profile.release] -lto = 'fat' +lto = "fat" codegen-units = 1 From b440e6bdb4e4563b82dd378f9ae83b933db92762 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 00:28:13 -0400 Subject: [PATCH 03/44] Unstable feature that makes rayon work --- .cargo/config.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index e8d250e937..d73c9f2ca4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -18,4 +18,7 @@ rustflags = [ "-C", "link-arg=-s", "-C", "link-arg=EXPORT_NAME=createModule", "-C", "link-arg=-s", "-C", "link-arg=EXPORT_ES6=1", "-C", "link-arg=-s", "-C", "link-arg=ASSERTIONS=1" -] \ No newline at end of file +] + +[unstable] +build-std = ["std", "panic_abort"] \ No newline at end of file From e70b2638f2fe169fc5cbd899611331e5e80cd373 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 11:45:48 -0400 Subject: [PATCH 04/44] Newline --- .cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d73c9f2ca4..24fd64b189 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -21,4 +21,4 @@ rustflags = [ ] [unstable] -build-std = ["std", "panic_abort"] \ No newline at end of file +build-std = ["std", "panic_abort"] From 76f5dffa4cf53a3be4cf147a638e699cb5f15cfa Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 12:12:35 -0400 Subject: [PATCH 05/44] Document pyodide --- docs/source/install.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/source/install.rst b/docs/source/install.rst index aeaace305e..f88767817a 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -113,6 +113,10 @@ source. - i686 or x86_64 - :ref:`tier-2` (Python < 3.10), :ref:`tier-3` (Python >= 3.10) - + * - Pyodide + - WASM (Emscripten) + - :ref:`tier-experimental` + - .. _manylinux 2014: https://peps.python.org/pep-0599/> @@ -166,6 +170,18 @@ functioning Python environment and may require a C/C++ compiler or additional programs to build dependencies from source as part of the installation process. Support for these platforms are best effort only. +.. _tier-experimental: + +Tier Experimental +------ + +Tier Experimental platforms are not tested upstream as part of the development process. +Pre-compiled binaries are built by the external community in separate repositories. Not all of rustworkx might compile for +platforms of this tier and features can be removed. Often, platforms in this tier use unstable features +from the Rust compiler and might break at any time. Support for these platforms are best effort only. + +Currently, the only platform in this tier is Pyodide, which is a port of Python that can run in the browser and on Node.js. + Using rustworkx =============== From 4b17893fabefd3b2f2de036a2d132ae5f686ad99 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 12:19:14 -0400 Subject: [PATCH 06/44] Add release note for pyodide --- releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml diff --git a/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml new file mode 100644 index 0000000000..1a87815cd9 --- /dev/null +++ b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + `rustworkx` now has experimental support for `Pyodide `__. + Pyodide is a Python distribution for WebAssembly that runs in the browser. + Although higly unstable, this is the first release that compiles with Pyodide and will allow + users to run `rustworkx` in web applications. Because Pyodide wheels are not available in PyPI, + we are working with the Pyodide team to publish them in the Pyodide package index. From 0e3b0ce34c6ef318e5bf3cdc0fe8b96cdedde5eb Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 15:59:40 -0400 Subject: [PATCH 07/44] Fix separator --- docs/source/install.rst | 2 +- releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/install.rst b/docs/source/install.rst index f88767817a..42bfb2b806 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -173,7 +173,7 @@ Support for these platforms are best effort only. .. _tier-experimental: Tier Experimental ------- +----------------- Tier Experimental platforms are not tested upstream as part of the development process. Pre-compiled binaries are built by the external community in separate repositories. Not all of rustworkx might compile for diff --git a/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml index 1a87815cd9..7e81fdb3d0 100644 --- a/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml +++ b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml @@ -3,6 +3,6 @@ upgrade: - | `rustworkx` now has experimental support for `Pyodide `__. Pyodide is a Python distribution for WebAssembly that runs in the browser. - Although higly unstable, this is the first release that compiles with Pyodide and will allow - users to run `rustworkx` in web applications. Because Pyodide wheels are not available in PyPI, + This is the first release that compiles with Pyodide and will allow users to run `rustworkx` + in web applications. Because Pyodide wheels are not available in PyPI, we are working with the Pyodide team to publish them in the Pyodide package index. From 3527a6fb58d981fcb1f6cdacc5b9a11a517c7cf9 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 23:06:14 -0400 Subject: [PATCH 08/44] Consolidate the bare minimum of flags --- .cargo/config.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 24fd64b189..c44ff90bc1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -13,11 +13,8 @@ rustflags = [ [target.wasm32-unknown-emscripten] rustflags = [ "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", - "-C", "link-arg=-s", "-C", "link-arg=ALLOW_MEMORY_GROWTH=1", - "-C", "link-arg=-s", "-C", "link-arg=MODULARIZE=1", - "-C", "link-arg=-s", "-C", "link-arg=EXPORT_NAME=createModule", - "-C", "link-arg=-s", "-C", "link-arg=EXPORT_ES6=1", - "-C", "link-arg=-s", "-C", "link-arg=ASSERTIONS=1" + "-C", "link-arg=-sSIDE_MODULE=2", + "-C", "link-arg=-sWASM_BIGINT", ] [unstable] From 72ceb1a9bceda47534fc166e765c60154cc4cc00 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sun, 11 May 2025 23:46:11 -0400 Subject: [PATCH 09/44] Add emscripten-wasm-eh flag --- .cargo/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index c44ff90bc1..1c09ed667c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -15,6 +15,7 @@ rustflags = [ "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", "-C", "link-arg=-sSIDE_MODULE=2", "-C", "link-arg=-sWASM_BIGINT", + "-Z", "emscripten-wasm-eh", ] [unstable] From 65d4eb8f2a6da5c3cf668618e210e7b6c3e28106 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 18:59:53 -0400 Subject: [PATCH 10/44] Use pixi to have a reproducible pyodide build --- .gitattributes | 2 + .gitignore | 7 + pixi.lock | 1618 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 42 +- 4 files changed, 1662 insertions(+), 7 deletions(-) create mode 100644 pixi.lock diff --git a/.gitattributes b/.gitattributes index 091a2bbb4f..a55fee526b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ docs/* linguist-documentation releasenotes/* linguist-documentation +# SCM syntax highlighting & preventing 3-way merges +pixi.lock merge=binary linguist-language=YAML linguist-generated=true diff --git a/.gitignore b/.gitignore index ee3e0af630..d1a7bfa375 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,10 @@ rustworkx-core/Cargo.lock **/.DS_Store venv/ .python-version + +# pixi environments +.pixi +*.egg-info + +# pyodide xbuild +.xbuildenv \ No newline at end of file diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000000..48cebde8bf --- /dev/null +++ b/pixi.lock @@ -0,0 +1,1618 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/auditwheel-emscripten-0.1.0-pyh5d45ec7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binaryen-118-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-20-20.1.4-default_h1df26ce_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-20.1.4-default_hfa515fb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clangxx-20.1.4-default_h0982aa1_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.9-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/emscripten-3.1.58-h84d6215_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-15.1.0-h4393ad2_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/leb128-1.0.8-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.1.0-h4c094af_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.1.0-h97b714f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.1.0-h4c094af_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lld-20.1.4-hd51abae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-tools-20-20.1.4-h48f18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-tools-20.1.4-h84d6215_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-22.13.0-hf235a45_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.2-pyhac95a24_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-lock-0.1.0a7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.2.2.post1-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-7_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/resolvelib-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.10-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.8-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rust-1.86.0-h1a8d7c4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rust-src-1.86.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rust-std-wasm32-unknown-emscripten-1.86.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rust-std-x86_64-unknown-linux-gnu-1.86.0-h2c6d0dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.3-pyhf21524f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.3-h1a15894_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.13.2-h0e9735f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.17.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.31.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h66e93f0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/b0/d9/7c338b923c53d431bc837b5b787052fef9ae68a56fe91e325aac0d48226e/numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + purls: [] + size: 2562 + timestamp: 1578324546067 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + build_number: 16 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 23621 + timestamp: 1650670423406 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping + size: 18074 + timestamp: 1733247158254 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda + sha256: b28e0f78bb0c7962630001e63af25a89224ff504e135a02e50d4d80b6155d386 + md5: 9749a2c77a7c40d432ea0927662d7e52 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + - python + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + purls: + - pkg:pypi/anyio?source=hash-mapping + size: 126346 + timestamp: 1742243108743 +- conda: https://conda.anaconda.org/conda-forge/noarch/auditwheel-emscripten-0.1.0-pyh5d45ec7_0.conda + sha256: f4be6666aa141e734c40ef9f2cc554e7042bbc4c3626af36c489e991c69a43b4 + md5: 30fb346da1bfa660012f3418bae7833a + depends: + - leb128 + - packaging + - pyodide-cli + - python >=3.12 + - typer-slim + - wheel + - python + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/auditwheel-emscripten?source=hash-mapping + size: 36278 + timestamp: 1743805929606 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binaryen-118-he02047a_1.conda + sha256: 1da7491c524e9c0a41ff3734ded249f4615b9ca63982301a6abc425e5e5a9180 + md5: 096a5c01f3ae8373e27ba4c3bb8523f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 5804456 + timestamp: 1722550497517 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_4.conda + sha256: 194d771be287dc973f6057c0747010ce28adf960f38d6e03ce3e828d7b74833e + md5: ef67db625ad0d2dce398837102f875ed + depends: + - ld_impl_linux-64 2.43 h712a8e2_4 + - sysroot_linux-64 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 6111717 + timestamp: 1740155471052 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 349867 + timestamp: 1725267732089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 252783 + timestamp: 1720974456583 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + sha256: 2a70ed95ace8a3f8a29e6cd1476a943df294a7111dfb3e152e3478c4c889b7ac + md5: 95db94f75ba080a22eb623590993167b + depends: + - __unix + license: ISC + purls: [] + size: 152283 + timestamp: 1745653616541 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + noarch: python + sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 + md5: 9b347a7ec10940d3f7941ff6c460b551 + depends: + - cached_property >=1.5.2,<1.5.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4134 + timestamp: 1615209571450 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 + md5: 576d629e47797577ab0f1b351297ef4a + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cached-property?source=hash-mapping + size: 11065 + timestamp: 1615209567874 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + sha256: 52aa837642fd851b3f7ad3b1f66afc5366d133c1d452323f786b0378a391915c + md5: c33eeaaa33f45031be34cda513df39b6 + depends: + - python >=3.9 + license: ISC + purls: + - pkg:pypi/certifi?source=hash-mapping + size: 157200 + timestamp: 1746569627830 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 + md5: a861504bbea4161a9170b85d4d2be840 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 294403 + timestamp: 1725560714366 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.2-pyhd8ed1ab_0.conda + sha256: 535ae5dcda8022e31c6dc063eb344c80804c537a5a04afba43a845fa6fa130f5 + md5: 40fe4284b8b5835a9073a645139f35af + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=compressed-mapping + size: 50481 + timestamp: 1746214981991 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clang-20.1.4-default_hfa515fb_0.conda + sha256: 5931227b657e46393e70e63ba7b42f34ba2f870d89bc7c2e3d1c85faee05e190 + md5: 171787632fe22a28f58af2ea2efe61af + depends: + - binutils_impl_linux-64 + - clang-20 20.1.4 default_h1df26ce_0 + - libgcc-devel_linux-64 + - sysroot_linux-64 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 24099 + timestamp: 1746074888590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clang-20-20.1.4-default_h1df26ce_0.conda + sha256: 0fc43c57a8a61424c079336aa9c7870355d8d0574280a10a74dac90438c3c8d0 + md5: 5401ebac37b610f8c6ffb074ab9ae57e + depends: + - __glibc >=2.17,<3.0.a0 + - libclang-cpp20.1 20.1.4 default_h1df26ce_0 + - libgcc >=13 + - libllvm20 >=20.1.4,<20.2.0a0 + - libstdcxx >=13 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 810614 + timestamp: 1746074830986 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clangxx-20.1.4-default_h0982aa1_0.conda + sha256: e4b18ab8835d54ea4b44d20da41e420d84521d5b9a2a019507ae5f5c11073c09 + md5: d16eef83db3fa90441c8607ea39011fa + depends: + - clang 20.1.4 default_hfa515fb_0 + - libstdcxx-devel_linux-64 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 24216 + timestamp: 1746074899002 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.0-pyh707e725_0.conda + sha256: 910f0e5e74a75f6e270b9dedd0f8ac55830250b0874f9f67605816fd069af283 + md5: 4d4f33c3d9e5a23a7f4795d327a5d1f0 + depends: + - __unix + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping + size: 87705 + timestamp: 1746951781787 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.9-pyhd8ed1ab_1.conda + sha256: 0e160c21776bd881b79ce70053e59736f51036784fa43a50da10a04f0c1b9c45 + md5: 8d88f4a2242e6b96f9ecff9a6a05b2f1 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/distlib?source=hash-mapping + size: 274151 + timestamp: 1733238487461 +- conda: https://conda.anaconda.org/conda-forge/linux-64/emscripten-3.1.58-h84d6215_3.conda + sha256: ecec5c721720bfd0763e12bebf696ab79327e1d1cc7d13056b8da2b2feb6a0a8 + md5: 67d4b6102e464fc63a575f2bb4ec4074 + depends: + - __glibc >=2.17,<3.0.a0 + - binaryen >=118,<119.0a0 + - clang + - clangxx + - libgcc >=13 + - libstdcxx >=13 + - lld + - llvm-tools + - nodejs >=18 + - python * + - zlib + constrains: + - python >=3.9 + license: MIT OR NCSA OR MPL-2.0 + purls: [] + size: 118090569 + timestamp: 1725439283866 +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca + md5: 72e42d28960d875c7654614f8b50939a + depends: + - python >=3.9 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=compressed-mapping + size: 21284 + timestamp: 1746947398083 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda + sha256: de7b6d4c4f865609ae88db6fa03c8b7544c2452a1aa5451eb7700aad16824570 + md5: 4547b39256e296bb758166893e909a7c + depends: + - python >=3.9 + license: Unlicense + purls: + - pkg:pypi/filelock?source=hash-mapping + size: 17887 + timestamp: 1741969612334 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-15.1.0-h4393ad2_2.conda + sha256: 99c545b842edd356d907c51ccd6f894ef6db042c6ebebefd14eb844f53ff8f73 + md5: 240c2af59f95792f60193c1cb9ee42c5 + depends: + - binutils_impl_linux-64 >=2.40 + - libgcc >=15.1.0 + - libgcc-devel_linux-64 15.1.0 h4c094af_102 + - libgomp >=15.1.0 + - libsanitizer 15.1.0 h97b714f_2 + - libstdcxx >=15.1.0 + - sysroot_linux-64 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 76467375 + timestamp: 1746642316078 +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 + md5: 4b69232755285701bc86a5afe4d9933a + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + purls: + - pkg:pypi/h11?source=hash-mapping + size: 37697 + timestamp: 1745526482242 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + sha256: 0aa1cdc67a9fe75ea95b5644b734a756200d6ec9d0dff66530aec3d1c1e9df75 + md5: b4754fb1bdcb70c8fd54f918301582c6 + depends: + - hpack >=4.1,<5 + - hyperframe >=6.1,<7 + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/h2?source=hash-mapping + size: 53888 + timestamp: 1738578623567 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hpack?source=hash-mapping + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b + md5: 4f14640d58e2cc0aa0819d9d8ba125bb + depends: + - python >=3.9 + - h11 >=0.16 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=4.0,<5.0 + - certifi + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/httpcore?source=hash-mapping + size: 49483 + timestamp: 1745602916758 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/httpx?source=hash-mapping + size: 63082 + timestamp: 1733663449209 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hyperframe?source=hash-mapping + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 12129203 + timestamp: 1720853576813 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/idna?source=hash-mapping + size: 49765 + timestamp: 1733211921194 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda + sha256: 598951ebdb23e25e4cec4bbff0ae369cec65ead80b50bc08b441d8e54de5cf03 + md5: f4b39bf00c69f56ac01e020ebfac066c + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-metadata?source=hash-mapping + size: 29141 + timestamp: 1737420302391 +- conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda + sha256: a922841ad80bd7b222502e65c07ecb67e4176c4fa5b03678a005f39fcc98be4b + md5: ad8527bf134a90e1c9ed35fa0b64318c + constrains: + - sysroot_linux-64 ==2.17 + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later AND MPL-2.0 + license_family: GPL + purls: [] + size: 943486 + timestamp: 1729794504440 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda + sha256: db73f38155d901a610b2320525b9dd3b31e4949215c870685fd92ea61b5ce472 + md5: 01f8d123c96816249efd255a31ad7712 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 671240 + timestamp: 1740155456116 +- conda: https://conda.anaconda.org/conda-forge/noarch/leb128-1.0.8-pyhd8ed1ab_1.conda + sha256: 1400274f49883f1c374472f912c960067f5e3201dc1608a4ee7d43074861c2f9 + md5: 8d4fc4a682e89e6d86ab58eeb617fdcd + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/leb128?source=hash-mapping + size: 9108 + timestamp: 1735272991001 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda + sha256: 5c28304eaabc2aaeb47e2ba847ae41e0ad7e2a520a7e19a2c5e846c69b417a5b + md5: 96f8d5b2e94c9ba4fef19f1adf068a15 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 >=20.1.4,<20.2.0a0 + - libstdcxx >=13 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 20897372 + timestamp: 1746074729385 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda + sha256: 33ab03438aee65d6aa667cf7d90c91e5e7d734c19a67aa4c7040742c0a13d505 + md5: db0bfbe7dd197b68ad5f30333bae6ce0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.7.0.* + license: MIT + license_family: MIT + purls: [] + size: 74427 + timestamp: 1743431794976 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + sha256: 764432d32db45466e87f10621db5b74363a9f847d2b8b1f9743746cd160f06ab + md5: ede4673863426c0883c0063d853bbd85 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 57433 + timestamp: 1743434498161 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda + sha256: 0024f9ab34c09629621aefd8603ef77bf9d708129b0dd79029e502c39ffc2195 + md5: ea8ac52380885ed41c1baa8f1d6d2b93 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==15.1.0=*_2 + - libgomp 15.1.0 h767d61c_2 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 829108 + timestamp: 1746642191935 +- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.1.0-h4c094af_102.conda + sha256: f379980c3d48ceabf8ade0ebc5bdb4acd41e4b1c89023b673deb212e51b7a7f6 + md5: c49792270935d4024aef12f8e49f0f9c + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 2733483 + timestamp: 1746642098272 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_2.conda + sha256: 0ab5421a89f090f3aa33841036bb3af4ed85e1f91315b528a9d75fab9aad51ae + md5: ddca86c7040dd0e73b2b69bd7833d225 + depends: + - libgcc 15.1.0 h767d61c_2 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 34586 + timestamp: 1746642200749 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_2.conda + sha256: 05fff3dc7e80579bc28de13b511baec281c4343d703c406aefd54389959154fb + md5: fbe7d535ff9d3a168c148e07358cd5b1 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 452635 + timestamp: 1746642113092 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda + sha256: 18a4afe14f731bfb9cf388659994263904d20111e42f841e9eea1bb6f91f4ab4 + md5: e796ff8ddc598affdf7c173d6145f087 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + purls: [] + size: 713084 + timestamp: 1740128065462 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda + sha256: 56a375dc36df1a4e2061e30ebbacbc9599a11277422a9a3145fd228f772bab53 + md5: 96c33bbd084ef2b2463503fb7f1482ae + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.13.7,<2.14.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 43004201 + timestamp: 1746052658083 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_1.conda + sha256: eeff241bddc8f1b87567dd6507c9f441f7f472c27f0860a07628260c000ef27c + md5: a76fd702c93cd2dfd89eff30a5fd45a8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + - xz ==5.8.1=*_1 + license: 0BSD + purls: [] + size: 112845 + timestamp: 1746531470399 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_1.conda + sha256: f157a2da5f7bf2c5ce5a18c52ccc76c39f075f7fbb1584d585a8d25c1b17cb92 + md5: 5499e2dd2f567a818b9f111e47caebd2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_1 + license: 0BSD + purls: [] + size: 441592 + timestamp: 1746531484594 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33408 + timestamp: 1697359010159 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-15.1.0-h97b714f_2.conda + sha256: 3f573329431523b2510b9374f4048d87bb603536bc63f66910cd47b5347ea37f + md5: 4de6cfe35a4736a63e4f59602a8ebeec + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.1.0 + - libstdcxx >=15.1.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 4539916 + timestamp: 1746642248624 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.2-hee588c1_0.conda + sha256: 525d4a0e24843f90b3ff1ed733f0a2e408aa6dd18b9d4f15465595e078e104a2 + md5: 93048463501053a00739215ea3f36324 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + purls: [] + size: 916313 + timestamp: 1746637007836 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_2.conda + sha256: 6ae3d153e78f6069d503d9309f2cac6de5b93d067fc6433160a4c05226a5dad4 + md5: 1cb1c67961f6dd257eae9e9691b341aa + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.1.0 h767d61c_2 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 3902355 + timestamp: 1746642227493 +- conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.1.0-h4c094af_102.conda + sha256: 1a401e2256d30b3ef3c93a5f553442748a7045bd19360bb28eaf39458e24e7d6 + md5: 9931ac667ef44e13405ce95c601af775 + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 14749051 + timestamp: 1746642129544 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_2.conda + sha256: 11bea86e11de7d6bce87589197a383344df3fa0a3552dab7e931785ff1159a5b + md5: 9d2072af184b5caa29492bf2344597bb + depends: + - libstdcxx 15.1.0 h8f9b012_2 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 34647 + timestamp: 1746642266826 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 33601 + timestamp: 1680112270483 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda + sha256: b4a8890023902aef9f1f33e3e35603ad9c2f16c21fdb58e968fa6c1bd3e94c0b + md5: 771ee65e13bc599b0b62af5359d80169 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 891272 + timestamp: 1737016632446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda + sha256: b0b3a96791fa8bb4ec030295e8c8bf2d3278f33c0f9ad540e73b5e538e6268e7 + md5: 14dbe05b929e329dbaa6f2d0aa19466d + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 690864 + timestamp: 1746634244154 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 60963 + timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lld-20.1.4-hd51abae_0.conda + sha256: c5e12a2bd9e3faff69b2510923f64aa463e8f81f9751f9e9c0dd347db9a70525 + md5: 8e744c37ae5a4f11c70b620e17bd08e8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 >=20.1.4,<20.2.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - llvm ==20.1.4 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 7642872 + timestamp: 1746055864247 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-tools-20.1.4-h84d6215_0.conda + sha256: b1cc120328b5608cb216c7fac1cc3e714a302698c96565a7675984c3977e7174 + md5: 0ecc18d6579ffb9f6e981b566ea0463b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 20.1.4 he9d0ab4_0 + - libstdcxx >=13 + - llvm-tools-20 20.1.4 h48f18f5_0 + constrains: + - clang 20.1.4 + - llvmdev 20.1.4 + - llvm 20.1.4 + - clang-tools 20.1.4 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 87987 + timestamp: 1746052887836 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-tools-20-20.1.4-h48f18f5_0.conda + sha256: c4c21d6cad99f47581cbdf84d07a3bcd823d621ef515d6005b4b637fc51fae82 + md5: 9342c917b5e1fa34d14c3f9e21c7d87e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libllvm20 20.1.4 he9d0ab4_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 25715324 + timestamp: 1746052816518 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/markdown-it-py?source=hash-mapping + size: 64430 + timestamp: 1733250550053 +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/mdurl?source=hash-mapping + size: 14465 + timestamp: 1733255681319 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + purls: [] + size: 891641 + timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-22.13.0-hf235a45_0.conda + sha256: 925ea8839d6f26d0eb4204675b98a862803a9a9657fd36a4a22c4c29a479a911 + md5: 1f9efd96347aa008bd2c735d7d88fc75 + depends: + - __glibc >=2.28,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libuv >=1.50.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.1,<4.0a0 + - zlib + license: MIT + license_family: MIT + purls: [] + size: 21691794 + timestamp: 1741809786920 +- pypi: https://files.pythonhosted.org/packages/b0/d9/7c338b923c53d431bc837b5b787052fef9ae68a56fe91e325aac0d48226e/numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: numpy + version: 2.2.5 + sha256: 3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda + sha256: b4491077c494dbf0b5eaa6d87738c22f2154e9277e5293175ec187634bd808a0 + md5: de356753cfdbffcde5bb1e86e3aa6cd0 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3117410 + timestamp: 1746223723843 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 + md5: 58335b26c38bf4a20f399384c33cbcf9 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=compressed-mapping + size: 62477 + timestamp: 1745345660407 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.1-pyh8b19718_0.conda + sha256: 81a7ffe7b7ca8718bc09476a258cd48754e1d4e1bd3b80a6fef41ffb71e3bfc8 + md5: 2247aa245832ea47e8b971bef73d7094 + depends: + - python >=3.9,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + purls: + - pkg:pypi/pip?source=hash-mapping + size: 1247085 + timestamp: 1745671930895 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda + sha256: 0f48999a28019c329cd3f6fd2f01f09fc32cc832f7d6bbe38087ddac858feaa3 + md5: 424844562f5d337077b445ec6b1398a7 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/platformdirs?source=compressed-mapping + size: 23531 + timestamp: 1746710438805 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 110100 + timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + sha256: a522473505ac6a9c10bb304d7338459a406ba22a6d3bb1a355c1b5283553a372 + md5: 8ad3ad8db5ce2ba470c9facc37af00a9 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.33.2 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing-inspection >=0.4.0 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic?source=compressed-mapping + size: 306304 + timestamp: 1746632069456 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + sha256: 4d14d7634c8f351ff1e63d733f6bb15cba9a0ec77e468b0de9102014a4ddc103 + md5: cfbd96e5a0182dfb4110fc42dda63e57 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1890081 + timestamp: 1746625309715 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + sha256: 28a3e3161390a9d23bc02b4419448f8d27679d9e2c250e29849e37749c8de86b + md5: 232fb4577b6687b2d503ef8e254270c9 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping + size: 888600 + timestamp: 1736243563082 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.2-pyhac95a24_0.conda + sha256: 3b07fb5ce926ebaeb018df47a5a64b3238f07598009b223aae346c2e664cd47d + md5: 903583e853d3cc2c82fcdcc698b66940 + depends: + - auditwheel-emscripten ==0.1.0 + - packaging + - platformdirs + - pydantic >=2,<3 + - pyodide-cli >=0.3.0 + - pyodide-lock ==0.1.0a7 + - python >=3.12 + - python-build >=1.2.0,<1.3.dev0 + - requests + - resolvelib + - rich + - ruamel.yaml + - typer >=0.13 + - unearth >=0.6,<1.dev0 + - virtualenv + - wheel + - python + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/pyodide-build?source=hash-mapping + size: 101351 + timestamp: 1746476227410 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda + sha256: 30e4a37a40d7ee47b8be64015674ad7323c6f41e935d080fb6bcfb8ec1f02b3d + md5: 0947e2092ff75a34f5e58bc5f689e247 + depends: + - python >=3.12 + - rich + - typer >=0.6.0 + - python + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/pyodide-cli?source=hash-mapping + size: 24452 + timestamp: 1743860686761 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-lock-0.1.0a7-pyhd8ed1ab_0.conda + sha256: b73e038d5d0d0a83372d033a79c2f725de151bd4938dd492e7bf650d91376554 + md5: 15a95627f7cd53c532e6403680656a99 + depends: + - pydantic >=2 + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pyodide-lock?source=hash-mapping + size: 15151 + timestamp: 1723471784826 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + sha256: 065ac44591da9abf1ff740feb25929554b920b00d09287a551fcced2c9791092 + md5: d4582021af437c931d7d77ec39007845 + depends: + - python >=3.9 + - tomli >=1.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyproject-hooks?source=hash-mapping + size: 15528 + timestamp: 1733710122949 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda + sha256: 674be31ff152d9f0e0fe16959a45e3803a730fc4f54d87df6a9ac4e6a698c41d + md5: 0515111a9cdf69f83278f7c197db9807 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.3,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.46.1,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.2,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 31574780 + timestamp: 1728059777603 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.2.2.post1-pyhff2d567_1.conda + sha256: da40ab7413029351852268ca479e5cc642011c72317bd02dba28235c5c5ec955 + md5: 0903621fe8a9f37286596529528f4f74 + depends: + - colorama + - importlib-metadata >=4.6 + - packaging >=19.0 + - pyproject_hooks + - python >=3.9 + - tomli >=1.1.0 + constrains: + - build <0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/build?source=hash-mapping + size: 25108 + timestamp: 1733230700715 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-7_cp312.conda + build_number: 7 + sha256: a1bbced35e0df66cc713105344263570e835625c28d1bdee8f748f482b2d7793 + md5: 0dfcdc155cf23812a0c9deada86fb723 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6971 + timestamp: 1745258861359 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c + md5: 283b96675859b20a825f8fa30f311446 + depends: + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 282480 + timestamp: 1740379431762 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/requests?source=hash-mapping + size: 58723 + timestamp: 1733217126197 +- conda: https://conda.anaconda.org/conda-forge/noarch/resolvelib-1.1.0-pyhd8ed1ab_1.conda + sha256: c48f49bfae4f642364d59fcc7eb328822e9611ef3a393e67d8cbd54753110667 + md5: 0880e9e633e4e5e2b76593f761ac154e + depends: + - python >=3.9 + license: ISC + purls: + - pkg:pypi/resolvelib?source=hash-mapping + size: 335514 + timestamp: 1733599765789 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + sha256: d10e2b66a557ec6296844e04686db87818b0df87d73c06388f2332fda3f7d2d5 + md5: 202f08242192ce3ed8bdb439ba40c0fe + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/rich?source=hash-mapping + size: 200323 + timestamp: 1743371105291 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.10-py312h66e93f0_0.conda + sha256: cd8ed10671111f15245cebadc06b88d6f5fc91f1f7f92456daa568e9d9f5bc42 + md5: 5260b7fb19694ee5bc4ed0ee7a2a769f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ruamel.yaml.clib >=0.1.2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ruamel-yaml?source=hash-mapping + size: 267560 + timestamp: 1736248154294 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.8-py312h66e93f0_1.conda + sha256: ac987b1c186d79e4e1ce4354a84724fc68db452b2bd61de3a3e1b6fc7c26138d + md5: 532c3e5d0280be4fea52396ec1fa7d5d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ruamel-yaml-clib?source=hash-mapping + size: 145481 + timestamp: 1728724626666 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rust-1.86.0-h1a8d7c4_0.conda + sha256: fa3b6757df927a24c3006bc5bffbac5b0c9a54b9755c08847f8a832ec1b79300 + md5: 98eab8148e1447e79f9e03492d04e291 + depends: + - __glibc >=2.17,<3.0.a0 + - gcc_impl_linux-64 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - rust-std-x86_64-unknown-linux-gnu 1.86.0 h2c6d0dc_0 + - sysroot_linux-64 >=2.17 + license: MIT + license_family: MIT + purls: [] + size: 218638108 + timestamp: 1743697775334 +- conda: https://conda.anaconda.org/conda-forge/noarch/rust-src-1.86.0-unix_0.conda + sha256: 73cc7c49ef8088fc5186f6aee48166ca71c7dc527997a592f5b76a815f4bda88 + md5: 9c2af1ca0493191466b300691edc2145 + depends: + - __unix + constrains: + - rust >=1.86.0,<1.86.1.0a0 + license: MIT + license_family: MIT + purls: [] + size: 3568928 + timestamp: 1743696829470 +- conda: https://conda.anaconda.org/conda-forge/noarch/rust-std-wasm32-unknown-emscripten-1.86.0-unix_0.conda + sha256: c5372b6d23b8826d275e6e09f73a9ea1f7a2e77c3c9fe487f166879c5269e44a + md5: a618a92c573bcf85c9c357f73e35d95e + depends: + - __unix + constrains: + - rust >=1.86.0,<1.86.1.0a0 + license: MIT + license_family: MIT + purls: [] + size: 25522077 + timestamp: 1743697288036 +- conda: https://conda.anaconda.org/conda-forge/noarch/rust-std-x86_64-unknown-linux-gnu-1.86.0-h2c6d0dc_0.conda + sha256: 8c1c68b7a8ce9657fea7d266607c21c9a00a382c346348a232e539c8a3266e84 + md5: 2fcc4c775a50bd2ce3ccb8dc56e4fb47 + depends: + - __unix + constrains: + - rust >=1.86.0,<1.86.1.0a0 + license: MIT + license_family: MIT + purls: [] + size: 37636509 + timestamp: 1743697574868 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda + sha256: 777d34ed359cedd5a5004c930077c101bb3b70e5fbb04d29da5058d75b0ba487 + md5: f6f72d0837c79eaec77661be43e8a691 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=compressed-mapping + size: 778484 + timestamp: 1746085063737 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/shellingham?source=compressed-mapping + size: 14462 + timestamp: 1733301007770 +- conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/sniffio?source=hash-mapping + size: 15019 + timestamp: 1733244175724 +- conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda + sha256: 69ab5804bdd2e8e493d5709eebff382a72fab3e9af6adf93a237ccf8f7dbd624 + md5: 460eba7851277ec1fd80a1a24080787a + depends: + - kernel-headers_linux-64 3.10.0 he073ed8_18 + - tzdata + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later AND MPL-2.0 + license_family: GPL + purls: [] + size: 15166921 + timestamp: 1735290488259 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3318875 + timestamp: 1699202167581 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + sha256: 18636339a79656962723077df9a56c0ac7b8a864329eb8f847ee3d38495b863e + md5: ac944244f1fed2eb49bae07193ae8215 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=hash-mapping + size: 19167 + timestamp: 1733256819729 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.3-pyhf21524f_0.conda + sha256: 8cd849ceb5e2f50481b1f30f083ee134fac706a56d7879c61248f0aadad4ea5b + md5: b4bed8eb8dd4fe076f436e5506d31673 + depends: + - typer-slim-standard ==0.15.3 h1a15894_0 + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer?source=compressed-mapping + size: 77044 + timestamp: 1745886712803 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.3-pyh29332c3_0.conda + sha256: 1768d1d9914d4237b0a1ae8bcb30dace44ac80b9ab1516a2d429d0b27ad70ab9 + md5: 20c0f2ae932004d7118c172eeb035cea + depends: + - python >=3.9 + - click >=8.0.0 + - typing_extensions >=3.7.4.3 + - python + constrains: + - typer 0.15.3.* + - rich >=10.11.0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer-slim?source=compressed-mapping + size: 46152 + timestamp: 1745886712803 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.3-h1a15894_0.conda + sha256: 72f77e8e61b28058562f2782cf32ff84f14f6c11c6cea7a3fe2839d34654ea45 + md5: 120216d3a2e51dfbb87bbba173ebf210 + depends: + - typer-slim ==0.15.3 pyh29332c3_0 + - rich + - shellingham + license: MIT + license_family: MIT + purls: [] + size: 5411 + timestamp: 1745886712803 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.13.2-h0e9735f_0.conda + sha256: 4865fce0897d3cb0ffc8998219157a8325f6011c136e6fd740a9a6b169419296 + md5: 568ed1300869dca0ba09fb750cda5dbb + depends: + - typing_extensions ==4.13.2 pyh29332c3_0 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 89900 + timestamp: 1744302253997 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.0-pyhd8ed1ab_0.conda + sha256: 172f971d70e1dbb978f6061d3f72be463d0f629155338603450d8ffe87cbf89d + md5: c5c76894b6b7bacc888ba25753bc8677 + depends: + - python >=3.9 + - typing_extensions >=4.12.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=hash-mapping + size: 18070 + timestamp: 1741438157162 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda + sha256: a8aaf351e6461de0d5d47e4911257e25eec2fa409d71f3b643bb2f748bde1c08 + md5: 83fc6ae00127671e301c9f44254c31b8 + depends: + - python >=3.9 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=compressed-mapping + size: 52189 + timestamp: 1744302253997 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 + md5: 4222072737ccff51314b5ece9c7d6f5a + license: LicenseRef-Public-Domain + purls: [] + size: 122968 + timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.17.5-pyhd8ed1ab_0.conda + sha256: bd17f8de2b814083113d964794babc5f8bdaaf590640d27a0395b47eb5b8f499 + md5: c645046441194a2d4fa726c2629f2461 + depends: + - cached-property >=1.5.2 + - httpx + - packaging >=20 + - python >=3.9 + - requests >=2.25 + license: MIT + license_family: MIT + purls: + - pkg:pypi/unearth?source=hash-mapping + size: 285892 + timestamp: 1744177720866 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.4.0-pyhd8ed1ab_0.conda + sha256: a25403b76f7f03ca1a906e1ef0f88521edded991b9897e7fed56a3e334b3db8c + md5: c1e349028e0052c4eea844e94f773065 + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping + size: 100791 + timestamp: 1744323705540 +- conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.31.2-pyhd8ed1ab_0.conda + sha256: 763dc774200b2eebdf5437b112834c5455a1dd1c9b605340696950277ff36729 + md5: c0600c1b374efa7a1ff444befee108ca + depends: + - distlib >=0.3.7,<1 + - filelock >=3.12.2,<4 + - platformdirs >=3.9.1,<5 + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/virtualenv?source=hash-mapping + size: 4133755 + timestamp: 1746781585998 +- conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + sha256: 1b34021e815ff89a4d902d879c3bd2040bc1bd6169b32e9427497fa05c55f1ce + md5: 75cb7132eb58d97896e173ef12ac9986 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/wheel?source=hash-mapping + size: 62931 + timestamp: 1733130309598 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_1.conda + sha256: 178b04c2f35261a1f9a272901d2533c88d50416745509450ca56f7bc76f4a268 + md5: 46600bb863ef3f2f565e7e0458f3d3bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_1 + - liblzma-devel 5.8.1 hb9d3cd8_1 + - xz-gpl-tools 5.8.1 hbcc6ac9_1 + - xz-tools 5.8.1 hb9d3cd8_1 + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 23933 + timestamp: 1746531531849 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_1.conda + sha256: 1f66e240fd37f66dfaff27f55f0e69008c4fdbbb02766cd2e0a60d2de85d49b4 + md5: 23d49402c43d1ea67aca3685acedc0ae + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_1 + constrains: + - xz 5.8.1.* + - xz ==5.8.1=*_1 + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 33852 + timestamp: 1746531515485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_1.conda + sha256: d219c162f2bf0bb851bfe4fbcb70f118b4f518e0e1c46ae72726bc5b9dde6f24 + md5: 966d5f3958ecfaf49a9a060dfb81eb85 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_1 + constrains: + - xz 5.8.1.* + - xz ==5.8.1=*_1 + license: 0BSD AND LGPL-2.1-or-later + purls: [] + size: 96134 + timestamp: 1746531500507 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/zipp?source=hash-mapping + size: 21809 + timestamp: 1732827613585 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + purls: [] + size: 92286 + timestamp: 1727963153079 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312h66e93f0_2.conda + sha256: ff62d2e1ed98a3ec18de7e5cf26c0634fd338cb87304cf03ad8cbafe6fe674ba + md5: 630db208bc7bbb96725ce9832c7423bb + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/zstandard?source=compressed-mapping + size: 732224 + timestamp: 1745869780524 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb + md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 567578 + timestamp: 1742433379869 diff --git a/pyproject.toml b/pyproject.toml index 2bbfea637b..0783c88e18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,19 +111,19 @@ target-version = ['py39', 'py310', 'py311', 'py312'] [tool.ruff] line-length = 105 # more lenient than black due to long function signatures src = ["rustworkx", "setup.py", "tests"] -lint.select = [ +target-version = "py39" +extend-exclude = ["doc"] + +[tool.ruff.lint] +select = [ "E", # pycodestyle "F", # pyflakes "UP", # pyupgrade "PYI", # flake8-pyi "Q", # flake8-quotes ] -target-version = "py39" -extend-exclude = ["doc"] +per-file-ignores = {"rustworkx/__init__.py" = ["F405", "F403"], "*.pyi" = ["F403", "F405", "PYI001", "PYI002"]} -[tool.ruff.lint.per-file-ignores] -"rustworkx/__init__.py" = ["F405", "F403"] -"*.pyi" = ["F403", "F405", "PYI001", "PYI002"] [tool.typos.default] extend-ignore-words-re = [ @@ -157,4 +157,32 @@ environment = "MACOSX_DEPLOYMENT_TARGET=10.12" repair-wheel-command = "brew install pipx && pipx ensurepath && pipx run --spec delocate==0.11.0 delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} && pipx run abi3audit==0.0.9 --strict --report {wheel}" [tool.cibuildwheel.windows] -repair-wheel-command = "cp {wheel} {dest_dir}/. && pipx run abi3audit==0.0.9 --strict --report {wheel}" \ No newline at end of file +repair-wheel-command = "cp {wheel} {dest_dir}/. && pipx run abi3audit==0.0.9 --strict --report {wheel}" + +[tool.pixi.workspace] +channels = ["conda-forge"] +platforms = ["linux-64"] + +[tool.pixi.environments] +default = {features = [], solve-group = "default"} + +[tool.pixi.dependencies] +python = "==3.12.7" +pip="==25.1" +pyodide-build = "==0.30.2" +emscripten = "==3.1.58" +nodejs = ">=22.13.0,<22.14" +rust = "==1.86" +rust-std-wasm32-unknown-emscripten = "==1.86" +rust-src = "==1.86" + +[tool.pixi.tasks.install_xbuildenv] +cmd = ["pyodide", "xbuildenv", "install", "0.27.5"] + +[tool.pixi.tasks.build_pyodide] +cmd = ["pyodide", "build"] +env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-memory,+mutable-globals $(pyodide config get rustflags)" } +depends-on = ["install_xbuildenv"] + +[tool.pyodide.build] +xbuildenv_path = ".xbuildenv" \ No newline at end of file From 8c4ed7b2dd769571ea2855136b28b5467fb33dd1 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 19:03:04 -0400 Subject: [PATCH 11/44] Add GitHub Actions to execute --- .github/workflows/main.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f1c73f8287..59208c6350 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -198,3 +198,14 @@ jobs: with: name: html_docs path: docs/build/html + pyodide-build: + if: github.repository_owner == 'Qiskit' + needs: [tests] + name: Pyodide Build + runs-on: ubuntu-latest + steps: + - uses: prefix-dev/setup-pixi@v0.8.8 + with: + pixi-version: v0.46.0 + environments: default + - run: pixi run build_pyodide From d99c029460e7433c455fcbe990040474ac75f334 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 19:16:38 -0400 Subject: [PATCH 12/44] Fix main.yml syntax --- .github/workflows/main.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 59208c6350..f434792a5b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -198,14 +198,14 @@ jobs: with: name: html_docs path: docs/build/html - pyodide-build: - if: github.repository_owner == 'Qiskit' - needs: [tests] - name: Pyodide Build - runs-on: ubuntu-latest - steps: - - uses: prefix-dev/setup-pixi@v0.8.8 - with: - pixi-version: v0.46.0 - environments: default - - run: pixi run build_pyodide + pyodide-build: + if: github.repository_owner == 'Qiskit' + needs: [tests] + name: Pyodide Build + runs-on: ubuntu-latest + steps: + - uses: prefix-dev/setup-pixi@v0.8.8 + with: + pixi-version: v0.46.0 + environments: default + - run: pixi run build_pyodide From 615d5bdca2598951fdc0ca5aff4cb0d536579022 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 19:31:05 -0400 Subject: [PATCH 13/44] Specify pyproject.toml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f434792a5b..033f88693b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -206,6 +206,7 @@ jobs: steps: - uses: prefix-dev/setup-pixi@v0.8.8 with: + manifest-path: pyproject.toml pixi-version: v0.46.0 environments: default - run: pixi run build_pyodide From 32e44cfe1ca68ab810411904a10e96b496599106 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 20:41:20 -0400 Subject: [PATCH 14/44] Actually download repo --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 033f88693b..6b09dc6457 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -204,6 +204,7 @@ jobs: name: Pyodide Build runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: prefix-dev/setup-pixi@v0.8.8 with: manifest-path: pyproject.toml From ad1f19d88234255b2fb82c2384ae3988c8cb3f0a Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 22:07:37 -0400 Subject: [PATCH 15/44] Add pyodide build docs --- CONTRIBUTING.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c02d3180d6..3e82c482a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -542,6 +542,58 @@ has the necessary approvals and is tagged as `automerge` unless it has a merge conflict or has a failed CI run. Doing so will just waste CI resources and delay everything from merging, including your PR. +### Pyodide Support + +`rustworkx` has experimental support for Pyodide, the Python distribution for +the browser. + +Because building for Pyodide is a more involved process, we use [Pixi](https://pixi.sh/latest/) +to manage the dependencies of the build. Currently, the scripts work only for Linux x86-64. +It is also possible to run the build on Windows with Windows Subsystem for Linux (WSL). At +the moment, aarch64 platforms like macOS cannot run the script. + +Please refer to the [Pixi](https://pixi.sh/latest/) page for the latest instructions on how +to install Pixi. Once installed, there's a single command that needs to be run. + +#### Building for Pyodide + +At the root of the directory, simply run: + +```bash +pixi run build_pyodide +``` + +This will create a separate environment with all of the required toolchains. At the end, +a Pyodide wheel will be available in the `dist` folder if the build is successful. + +#### Testing Pyodide Wheels + +Currently, there are no tests for Pyodide wheels. In the future, we plan to add smoke tests +like those in the `pyodide-recipes` repository. + +#### Updating `pyodide-build` and dependencies + +All the dependencies for the Pyodide build are listed under `[tool.pixi.dependencies]`. To find a set +of versions that works, visit the [pyodide-cross-build-environments.json](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json) file in the `pyodide` repository. + +We'll need to align the [Emscripten](https://anaconda.org/conda-forge/emscripten) version from `conda-forge` with one +of the public releases. Then, we pick a `pyodide-build` version higher than the required build version and the equivalent Python +version also specified in the cross build environments. + +Lastly, we need to pin the Rust compiler. To find an appropriate Rust compiler version, run: + +```bash +pixi shell +pyodide config list +``` + +This will output a list including `rust_toolchain`. Currently, `pyodide-build` requires Rust Nightly. Because conda-forge +only provides stable releases, we'll need to map a nightly version to a stable version. Some repositories like [https://github.com/oxalica/rust-overlay/tree/master/manifests/nightly/](oxalica/rust-overlay) contain a list of the nightly releases. For example, `nightly-2025-02-01` +maps roughly to `1.86`. If that version was not yet stable, we could try picking `1.85` as well. + +After updating the versions in `[tool.pixi.dependencies]`, run `pixi lock` which will update `pixi.lock`. Onwards, all builds +will use the same environment. As long as `pixi run build_pyodide` passes locally or on CI it should keep compiling and building. + ### Stable Branch Policy and Backporting The stable branch is intended to be a safe source of fixes for high-impact bugs, From 901c6748f7b6a56b97db0624e40459f9e366c303 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 22:11:10 -0400 Subject: [PATCH 16/44] Minor stuff --- .gitignore | 2 +- CONTRIBUTING.md | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d1a7bfa375..d2ce5a97a4 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ venv/ *.egg-info # pyodide xbuild -.xbuildenv \ No newline at end of file +.xbuildenv diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e82c482a9..9ddd990515 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -588,7 +588,7 @@ pyodide config list ``` This will output a list including `rust_toolchain`. Currently, `pyodide-build` requires Rust Nightly. Because conda-forge -only provides stable releases, we'll need to map a nightly version to a stable version. Some repositories like [https://github.com/oxalica/rust-overlay/tree/master/manifests/nightly/](oxalica/rust-overlay) contain a list of the nightly releases. For example, `nightly-2025-02-01` +only provides stable releases, we'll need to map a nightly version to a stable version. Some repositories like [oxalica/rust-overlay]([oxalica/rust-overlay](https://github.com/oxalica/rust-overlay/tree/master/manifests/nightly/)) contain a list of the nightly releases. For example, `nightly-2025-02-01` maps roughly to `1.86`. If that version was not yet stable, we could try picking `1.85` as well. After updating the versions in `[tool.pixi.dependencies]`, run `pixi lock` which will update `pixi.lock`. Onwards, all builds diff --git a/pyproject.toml b/pyproject.toml index 0783c88e18..6fba9434af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -185,4 +185,4 @@ env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-mem depends-on = ["install_xbuildenv"] [tool.pyodide.build] -xbuildenv_path = ".xbuildenv" \ No newline at end of file +xbuildenv_path = ".xbuildenv" From 92b89102bfdd098edca1513a2648e2237e908a42 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Tue, 13 May 2025 22:15:49 -0400 Subject: [PATCH 17/44] Minor update to CONTRIBUTING.md --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ddd990515..7c75cb9b6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -578,7 +578,8 @@ of versions that works, visit the [pyodide-cross-build-environments.json](https: We'll need to align the [Emscripten](https://anaconda.org/conda-forge/emscripten) version from `conda-forge` with one of the public releases. Then, we pick a `pyodide-build` version higher than the required build version and the equivalent Python -version also specified in the cross build environments. +version also specified in the cross build environments. Lastly, update `[tool.pixi.tasks.install_xbuildenv]` to install +the selected version of Pyodide. Lastly, we need to pin the Rust compiler. To find an appropriate Rust compiler version, run: From 25a5763a5aea2fc2b5149367553f23f972bc0f66 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 08:09:56 -0400 Subject: [PATCH 18/44] Add very first smoke test --- .gitignore | 5 +++ tests/pyodide_smoke_test/package-lock.json | 45 ++++++++++++++++++++++ tests/pyodide_smoke_test/package.json | 5 +++ tests/pyodide_smoke_test/smoke_test.mjs | 15 ++++++++ 4 files changed, 70 insertions(+) create mode 100644 tests/pyodide_smoke_test/package-lock.json create mode 100644 tests/pyodide_smoke_test/package.json create mode 100644 tests/pyodide_smoke_test/smoke_test.mjs diff --git a/.gitignore b/.gitignore index d2ce5a97a4..bf64c44dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,8 @@ venv/ # pyodide xbuild .xbuildenv + +# Node.js +node_modules/ +jspm_packages/ +.npm diff --git a/tests/pyodide_smoke_test/package-lock.json b/tests/pyodide_smoke_test/package-lock.json new file mode 100644 index 0000000000..f3f3289627 --- /dev/null +++ b/tests/pyodide_smoke_test/package-lock.json @@ -0,0 +1,45 @@ +{ + "name": "pyodide-nodejs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "pyodide": "^0.27.5" + } + }, + "node_modules/pyodide": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.5.tgz", + "integrity": "sha512-nXErpLzEdtQolt+sNQ/5mKuN9XTUwhxR2MRhRhZ6oDRGpYLXrOp5+kkTPGEwK+wn1ZA8+poNmoxKTj2sq/p9og==", + "license": "Apache-2.0", + "dependencies": { + "ws": "^8.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/tests/pyodide_smoke_test/package.json b/tests/pyodide_smoke_test/package.json new file mode 100644 index 0000000000..0876dd9cf6 --- /dev/null +++ b/tests/pyodide_smoke_test/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "pyodide": "^0.27.5" + } +} diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs new file mode 100644 index 0000000000..8ec65c1d4d --- /dev/null +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -0,0 +1,15 @@ +import { loadPyodide } from "pyodide"; + +async function rustworkx_python() { + let pyodide = await loadPyodide(); + await pyodide.loadPackage("micropip"); + const micropip = pyodide.pyimport("micropip"); + await micropip.install(`http://localhost:8000/rustworkx-0.17.0-cp39-abi3-pyodide_2024_0_wasm32.whl`); + return pyodide.runPythonAsync(` + import rustworkx + print(rustworkx.__version__) + `); +} + +const result = await rustworkx_python(); +console.log("Smoke test completed successfully"); From a44bcfc0c0ae36ea188218d8188e17fbec41a267 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 08:45:24 -0400 Subject: [PATCH 19/44] Trying to make local test happen --- tests/pyodide_smoke_test/smoke_test.mjs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index 8ec65c1d4d..f26043302f 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -1,13 +1,28 @@ import { loadPyodide } from "pyodide"; +import * as fs from 'fs'; +import * as path from 'path'; async function rustworkx_python() { let pyodide = await loadPyodide(); await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); - await micropip.install(`http://localhost:8000/rustworkx-0.17.0-cp39-abi3-pyodide_2024_0_wasm32.whl`); + + // Load rustworkx wheel into memory + const filename = 'rustworkx-0.17.0-cp39-abi3-pyodide_2024_0_wasm32.whl'; + + const wheelPath = path.resolve(filename); + const wheelData = fs.readFileSync(wheelPath); + // console.log("Read data"); + + pyodide.FS.mkdir('/tmp', 0o777); + pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); + + // console.log(`Successfully loaded ${filename} into Pyodide's virtual file system`); + + await micropip.install(`emfs://tmp/${filename}`); return pyodide.runPythonAsync(` - import rustworkx - print(rustworkx.__version__) + import rustworkx + print(rustworkx.__version__) `); } From 2bce487f0e2e8e6ee2a379a6c7bfb3b60f27ab4c Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 19:13:21 -0400 Subject: [PATCH 20/44] More smoke test --- tests/pyodide_smoke_test/smoke_test.mjs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index f26043302f..c1cb35a70f 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -2,29 +2,28 @@ import { loadPyodide } from "pyodide"; import * as fs from 'fs'; import * as path from 'path'; -async function rustworkx_python() { +async function run_smoke_test() { let pyodide = await loadPyodide(); await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); // Load rustworkx wheel into memory - const filename = 'rustworkx-0.17.0-cp39-abi3-pyodide_2024_0_wasm32.whl'; + const filePath = process.argv[2]; + const filename = path.basename(filePath); - const wheelPath = path.resolve(filename); + const wheelPath = path.resolve(filePath); const wheelData = fs.readFileSync(wheelPath); - // console.log("Read data"); - pyodide.FS.mkdir('/tmp', 0o777); pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); - // console.log(`Successfully loaded ${filename} into Pyodide's virtual file system`); - - await micropip.install(`emfs://tmp/${filename}`); + await micropip.install(`emfs:/tmp/${filename}`); + console.log("installed wheel"); + return null; return pyodide.runPythonAsync(` import rustworkx print(rustworkx.__version__) `); } -const result = await rustworkx_python(); +const result = await run_smoke_test(); console.log("Smoke test completed successfully"); From d303f91c8fdaff270c095136456c753f7147153f Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 19:28:45 -0400 Subject: [PATCH 21/44] Use unittest module!? --- tests/pyodide_smoke_test/smoke_test.mjs | 42 ++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index c1cb35a70f..a907588396 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -2,7 +2,7 @@ import { loadPyodide } from "pyodide"; import * as fs from 'fs'; import * as path from 'path'; -async function run_smoke_test() { +async function get_pyodide_with_rustworkx() { let pyodide = await loadPyodide(); await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); @@ -17,12 +17,44 @@ async function run_smoke_test() { pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); await micropip.install(`emfs:/tmp/${filename}`); - console.log("installed wheel"); - return null; + return pyodide; +} + +async function run_smoke_test() { + let pyodide = await get_pyodide_with_rustworkx(); return pyodide.runPythonAsync(` +import rustworkx +import unittest + +class TestPyodide(unittest.TestCase): + def test_isomorphism(self): + # Adapted from tests/graph/test_isomorphic.py import rustworkx - print(rustworkx.__version__) - `); + + n = 15 + upper_bound_k = (n - 1) // 2 + for k in range(1, upper_bound_k + 1): + for t in range(k, upper_bound_k + 1): + result = rustworkx.is_isomorphic( + rustworkx.generators.generalized_petersen_graph(n, k), + rustworkx.generators.generalized_petersen_graph(n, t), + ) + expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) + self.assertEqual(result, expected) + + def test_rayon_works(self): + # This essentially tests that multi-threading is set to one core and does not panic + import rustworkx + graph = rustworkx.generators.cycle_graph(10) + path_lenghts_floyd = rustworkx.floyd_warshall(graph) + path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) + path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} + self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) + +suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) +runner = unittest.TextTestRunner() +runner.run(suite) +`); } const result = await run_smoke_test(); From c03fc6af93f0950756a3b3d636ca9738c1823c9f Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 20:17:18 -0400 Subject: [PATCH 22/44] Add Python smoke test file separately --- tests/pyodide_smoke_test/smoke_test.mjs | 43 ++++++------------------- tests/pyodide_smoke_test/test_smoke.py | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 tests/pyodide_smoke_test/test_smoke.py diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index a907588396..c9beeeb688 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -7,7 +7,7 @@ async function get_pyodide_with_rustworkx() { await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); - // Load rustworkx wheel into memory + // Load rustworkx from local file system to Pyodide's const filePath = process.argv[2]; const filename = path.basename(filePath); @@ -16,45 +16,20 @@ async function get_pyodide_with_rustworkx() { pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); + // Install with micropip await micropip.install(`emfs:/tmp/${filename}`); return pyodide; } +async function getUnitTest() { + let unitTestFile = `${__dirname}/test_smoke.py`; + let unitTestData = fs.readFileSync(unitTestFile); + return unitTestData.toString(); +} + async function run_smoke_test() { let pyodide = await get_pyodide_with_rustworkx(); - return pyodide.runPythonAsync(` -import rustworkx -import unittest - -class TestPyodide(unittest.TestCase): - def test_isomorphism(self): - # Adapted from tests/graph/test_isomorphic.py - import rustworkx - - n = 15 - upper_bound_k = (n - 1) // 2 - for k in range(1, upper_bound_k + 1): - for t in range(k, upper_bound_k + 1): - result = rustworkx.is_isomorphic( - rustworkx.generators.generalized_petersen_graph(n, k), - rustworkx.generators.generalized_petersen_graph(n, t), - ) - expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) - self.assertEqual(result, expected) - - def test_rayon_works(self): - # This essentially tests that multi-threading is set to one core and does not panic - import rustworkx - graph = rustworkx.generators.cycle_graph(10) - path_lenghts_floyd = rustworkx.floyd_warshall(graph) - path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) - path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} - self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) - -suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) -runner = unittest.TextTestRunner() -runner.run(suite) -`); + return pyodide.runPythonAsync(getUnitTest()); } const result = await run_smoke_test(); diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_smoke_test/test_smoke.py new file mode 100644 index 0000000000..a76e49bdd2 --- /dev/null +++ b/tests/pyodide_smoke_test/test_smoke.py @@ -0,0 +1,34 @@ +import sys +import rustworkx +import unittest + +@unittest.skipUnless(sys.platform == "emscripten", "Smoke tests target Pyodide") +class TestPyodide(unittest.TestCase): + def test_isomorphism(self): + # Adapted from tests/graph/test_isomorphic.py + import rustworkx + + n = 15 + upper_bound_k = (n - 1) // 2 + for k in range(1, upper_bound_k + 1): + for t in range(k, upper_bound_k + 1): + result = rustworkx.is_isomorphic( + rustworkx.generators.generalized_petersen_graph(n, k), + rustworkx.generators.generalized_petersen_graph(n, t), + ) + expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) + self.assertEqual(result, expected) + + def test_rayon_works(self): + # This essentially tests that multi-threading is set to one core and does not panic + import rustworkx + graph = rustworkx.generators.cycle_graph(10) + path_lenghts_floyd = rustworkx.floyd_warshall(graph) + path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) + path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} + self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) + +if sys.platform == "emscripten": + suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) + runner = unittest.TextTestRunner() + runner.run(suite) From 1ca13176f7df6e76bff138be1ae4de4ea4053d5a Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 20:21:05 -0400 Subject: [PATCH 23/44] Handle dirname correctly --- tests/pyodide_smoke_test/smoke_test.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index c9beeeb688..47f51b5c99 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -1,6 +1,7 @@ import { loadPyodide } from "pyodide"; import * as fs from 'fs'; import * as path from 'path'; +import { fileURLToPath } from 'url'; async function get_pyodide_with_rustworkx() { let pyodide = await loadPyodide(); @@ -22,6 +23,8 @@ async function get_pyodide_with_rustworkx() { } async function getUnitTest() { + const __filename = fileURLToPath(import.meta.url); + const __dirname = dirname(__filename); let unitTestFile = `${__dirname}/test_smoke.py`; let unitTestData = fs.readFileSync(unitTestFile); return unitTestData.toString(); From d30857954d185f5e0c601b3b4f71436db921475c Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 20:26:24 -0400 Subject: [PATCH 24/44] Smoke test works now --- tests/pyodide_smoke_test/smoke_test.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index 47f51b5c99..f7da6b4ecc 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -22,9 +22,9 @@ async function get_pyodide_with_rustworkx() { return pyodide; } -async function getUnitTest() { +function getUnitTest() { const __filename = fileURLToPath(import.meta.url); - const __dirname = dirname(__filename); + const __dirname = path.dirname(__filename); let unitTestFile = `${__dirname}/test_smoke.py`; let unitTestData = fs.readFileSync(unitTestFile); return unitTestData.toString(); @@ -32,7 +32,8 @@ async function getUnitTest() { async function run_smoke_test() { let pyodide = await get_pyodide_with_rustworkx(); - return pyodide.runPythonAsync(getUnitTest()); + let unitTest = await getUnitTest(); + return pyodide.runPythonAsync(unitTest); } const result = await run_smoke_test(); From 765ba1ed468e1bde99abc7f1d9fd5cdeda60d4e9 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 20:37:08 -0400 Subject: [PATCH 25/44] Actually catch failures --- tests/pyodide_smoke_test/smoke_test.mjs | 10 +++++----- tests/pyodide_smoke_test/test_smoke.py | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index f7da6b4ecc..e52e57a886 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; -async function get_pyodide_with_rustworkx() { +async function getPyodideWithRustworkx() { let pyodide = await loadPyodide(); await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); @@ -22,7 +22,7 @@ async function get_pyodide_with_rustworkx() { return pyodide; } -function getUnitTest() { +async function getUnitTest() { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); let unitTestFile = `${__dirname}/test_smoke.py`; @@ -30,11 +30,11 @@ function getUnitTest() { return unitTestData.toString(); } -async function run_smoke_test() { - let pyodide = await get_pyodide_with_rustworkx(); +async function runSmokeTest() { + let pyodide = await getPyodideWithRustworkx(); let unitTest = await getUnitTest(); return pyodide.runPythonAsync(unitTest); } -const result = await run_smoke_test(); +await runSmokeTest(); console.log("Smoke test completed successfully"); diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_smoke_test/test_smoke.py index a76e49bdd2..d01020d9aa 100644 --- a/tests/pyodide_smoke_test/test_smoke.py +++ b/tests/pyodide_smoke_test/test_smoke.py @@ -32,3 +32,7 @@ def test_rayon_works(self): suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) runner = unittest.TextTestRunner() runner.run(suite) + if result.wasSuccessful(): + sys.exit(0) + else: + sys.exit(1) From b63e334748e9fe87a4f941050b3955a16ee912fb Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 20:40:19 -0400 Subject: [PATCH 26/44] Local imports only --- tests/pyodide_smoke_test/test_smoke.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_smoke_test/test_smoke.py index d01020d9aa..8417c3884a 100644 --- a/tests/pyodide_smoke_test/test_smoke.py +++ b/tests/pyodide_smoke_test/test_smoke.py @@ -1,5 +1,4 @@ import sys -import rustworkx import unittest @unittest.skipUnless(sys.platform == "emscripten", "Smoke tests target Pyodide") @@ -31,8 +30,6 @@ def test_rayon_works(self): if sys.platform == "emscripten": suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) runner = unittest.TextTestRunner() - runner.run(suite) - if result.wasSuccessful(): - sys.exit(0) - else: + result = runner.run(suite) + if not result.wasSuccessful(): sys.exit(1) From f393309dce702fec52f1e906ed8df28b77ba93e9 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 21:13:30 -0400 Subject: [PATCH 27/44] Add pixi tasks --- .github/workflows/main.yml | 4 +- pyproject.toml | 13 ++++++ tests/pyodide_smoke_test/test_smoke.py | 55 ++++++++++++++------------ tools/find_only_wheel.py | 37 +++++++++++++++++ 4 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 tools/find_only_wheel.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b09dc6457..18b754f405 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -201,7 +201,7 @@ jobs: pyodide-build: if: github.repository_owner == 'Qiskit' needs: [tests] - name: Pyodide Build + name: Pyodide Build + Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -210,4 +210,4 @@ jobs: manifest-path: pyproject.toml pixi-version: v0.46.0 environments: default - - run: pixi run build_pyodide + - run: pixi run build_pyodide_and_test diff --git a/pyproject.toml b/pyproject.toml index 6fba9434af..33139b712a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -184,5 +184,18 @@ cmd = ["pyodide", "build"] env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-memory,+mutable-globals $(pyodide config get rustflags)" } depends-on = ["install_xbuildenv"] +[tool.pixi.tasks.install_smoke_env] +cmd = ["pushd tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "popd", "&&", "popd"] + +[tool.pixi.tasks.smoke_test] +cmd = ["pushd tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}"] +args = [ + { "arg" = "path", "default" = "$(python tools/find_only_wheel.py)" } +] +depends-on = ["install_smoke_env", "find_pyodide_wheel"] + +[tool.pixi.tasks.build_pyodide_and_test] +depends-on = ["build_pyodide", "smoke_test"] + [tool.pyodide.build] xbuildenv_path = ".xbuildenv" diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_smoke_test/test_smoke.py index 8417c3884a..2a8f673449 100644 --- a/tests/pyodide_smoke_test/test_smoke.py +++ b/tests/pyodide_smoke_test/test_smoke.py @@ -1,35 +1,38 @@ import sys import unittest + @unittest.skipUnless(sys.platform == "emscripten", "Smoke tests target Pyodide") class TestPyodide(unittest.TestCase): - def test_isomorphism(self): - # Adapted from tests/graph/test_isomorphic.py - import rustworkx + def test_isomorphism(self): + # Adapted from tests/graph/test_isomorphic.py + import rustworkx + + n = 15 + upper_bound_k = (n - 1) // 2 + for k in range(1, upper_bound_k + 1): + for t in range(k, upper_bound_k + 1): + result = rustworkx.is_isomorphic( + rustworkx.generators.generalized_petersen_graph(n, k), + rustworkx.generators.generalized_petersen_graph(n, t), + ) + expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) + self.assertEqual(result, expected) + + def test_rayon_works(self): + # This essentially tests that multi-threading is set to one core and does not panic + import rustworkx - n = 15 - upper_bound_k = (n - 1) // 2 - for k in range(1, upper_bound_k + 1): - for t in range(k, upper_bound_k + 1): - result = rustworkx.is_isomorphic( - rustworkx.generators.generalized_petersen_graph(n, k), - rustworkx.generators.generalized_petersen_graph(n, t), - ) - expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) - self.assertEqual(result, expected) + graph = rustworkx.generators.cycle_graph(10) + path_lenghts_floyd = rustworkx.floyd_warshall(graph) + path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) + path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} + self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) - def test_rayon_works(self): - # This essentially tests that multi-threading is set to one core and does not panic - import rustworkx - graph = rustworkx.generators.cycle_graph(10) - path_lenghts_floyd = rustworkx.floyd_warshall(graph) - path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) - path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} - self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) if sys.platform == "emscripten": - suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) - runner = unittest.TextTestRunner() - result = runner.run(suite) - if not result.wasSuccessful(): - sys.exit(1) + suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) + runner = unittest.TextTestRunner() + result = runner.run(suite) + if not result.wasSuccessful(): + sys.exit(1) diff --git a/tools/find_only_wheel.py b/tools/find_only_wheel.py new file mode 100644 index 0000000000..c00c7abe19 --- /dev/null +++ b/tools/find_only_wheel.py @@ -0,0 +1,37 @@ +import pathlib + + +def find_only_pyodide_wheel(directory): + """ + Find the only pyodide wheel in the current directory. + Throws an error if there is not exactly one .whl file, + to remind users to clean up the directory. + """ + # Filter for .whl files + whl_files = [ + f for f in directory.iterdir() + if f.is_file() and f.suffix == ".whl" + ] + pyodide_whl_files = [ + f for f in whl_files + if "rustworkx" in f.name and "pyodide" in f.name + ] + + # Check if there is exactly one .whl file + if len(whl_files) != 1: + raise RuntimeError( + "There should be exactly one .whl file in the dist/ directory. Please clean up the directory." + ) + + return whl_files[0] # Return the name of the .whl file + +if __name__ == "__main__": + current_dir = pathlib.Path(__file__) + dist_dir = pathlib.Path(__file__).parent / "../dist" + absolute_dist_path = dist_dir.resolve() + + # Find the only pyodide wheel + wheel_path = find_only_pyodide_wheel(absolute_dist_path) + + # Print the name of the wheel file + print(wheel_path) \ No newline at end of file From e2bad5018e8dfb2f43c06f0e200abcdcf95cf251 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 21:26:22 -0400 Subject: [PATCH 28/44] It works! --- pyproject.toml | 8 ++++---- tests/pyodide_smoke_test/package-lock.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 33139b712a..fe1fd3857b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -185,14 +185,14 @@ env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-mem depends-on = ["install_xbuildenv"] [tool.pixi.tasks.install_smoke_env] -cmd = ["pushd tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "popd", "&&", "popd"] +cmd = ["cd", "tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "cd", ".."] [tool.pixi.tasks.smoke_test] -cmd = ["pushd tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}"] +cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "cd", ".."] args = [ - { "arg" = "path", "default" = "$(python tools/find_only_wheel.py)" } + { "arg" = "wheel_path", "default" = "$(python ../../tools/find_only_wheel.py)" }, ] -depends-on = ["install_smoke_env", "find_pyodide_wheel"] +depends-on = ["install_smoke_env"] [tool.pixi.tasks.build_pyodide_and_test] depends-on = ["build_pyodide", "smoke_test"] diff --git a/tests/pyodide_smoke_test/package-lock.json b/tests/pyodide_smoke_test/package-lock.json index f3f3289627..f8fb54afb9 100644 --- a/tests/pyodide_smoke_test/package-lock.json +++ b/tests/pyodide_smoke_test/package-lock.json @@ -1,5 +1,5 @@ { - "name": "pyodide-nodejs", + "name": "pyodide_smoke_test", "lockfileVersion": 3, "requires": true, "packages": { From b8c2f5844cf46ec50b5fa6d936312d25a18478c1 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 21:35:41 -0400 Subject: [PATCH 29/44] Throw error if there are multiple wheels --- tests/pyodide_smoke_test/smoke_test.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index e52e57a886..63e130b37d 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -10,6 +10,9 @@ async function getPyodideWithRustworkx() { // Load rustworkx from local file system to Pyodide's const filePath = process.argv[2]; + if (process.argv[2] === '') { + throw new Error('Wheel path is empty, check the logs to see if there are multiple wheels in dist/.'); + } const filename = path.basename(filePath); const wheelPath = path.resolve(filePath); From 588f0d025f8b33fee473acff9066aaa3dfb2a266 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 21:53:45 -0400 Subject: [PATCH 30/44] Update CONTRIBUTING.md to explain how to run tests --- CONTRIBUTING.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c75cb9b6e..50da36f10b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -568,8 +568,25 @@ a Pyodide wheel will be available in the `dist` folder if the build is successfu #### Testing Pyodide Wheels -Currently, there are no tests for Pyodide wheels. In the future, we plan to add smoke tests -like those in the `pyodide-recipes` repository. +After running `pixi run build_pyodide`, we can run a simple smoke test with: + +```bash +pixi run smoke_test +``` + +It is also possible to run the smoke test with a specific wheel via an **absolute path**: +``` +pixi run smoke_test $ABSOLUTE_PATH_TO_WHEEL +``` + +> [!WARNING] +> If there are multiple Pyodide wheels in the dist/ folder, the test will fail +> to prevent you from testing an outdated wheel. This scenario can happen when +> we update the rustworkx version, minimum Python version, or Pyodide. To fix, +> clean the directory with `rm -r dist` and re-run `pixi run build_pyodide` + +> [!TIP] +> You can build and test with a single command with `pixi run build_pyodide_and_test`. #### Updating `pyodide-build` and dependencies @@ -581,7 +598,7 @@ of the public releases. Then, we pick a `pyodide-build` version higher than the version also specified in the cross build environments. Lastly, update `[tool.pixi.tasks.install_xbuildenv]` to install the selected version of Pyodide. -Lastly, we need to pin the Rust compiler. To find an appropriate Rust compiler version, run: +We need to pin the Rust compiler. To find an appropriate Rust compiler version, run: ```bash pixi shell @@ -595,6 +612,10 @@ maps roughly to `1.86`. If that version was not yet stable, we could try picking After updating the versions in `[tool.pixi.dependencies]`, run `pixi lock` which will update `pixi.lock`. Onwards, all builds will use the same environment. As long as `pixi run build_pyodide` passes locally or on CI it should keep compiling and building. +Lastly, remember to update the Pyodide version in `tests/pyodide_smoke_test`. Change the version in `tests/pyodide_smoke_test/package.json` +and run `pixi run install_smoke_env` to generate the new lockfile. If you forget to update the Pyodide version, the smoke test +will fail. + ### Stable Branch Policy and Backporting The stable branch is intended to be a safe source of fixes for high-impact bugs, From 233ffe6aea18b55106544df4332b172d9a3866a1 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 21:59:35 -0400 Subject: [PATCH 31/44] Minor improvements --- pyproject.toml | 4 ++-- tools/find_only_wheel.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fe1fd3857b..17c4f5d6ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -185,10 +185,10 @@ env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-mem depends-on = ["install_xbuildenv"] [tool.pixi.tasks.install_smoke_env] -cmd = ["cd", "tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "cd", ".."] +cmd = ["cd", "tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "cd", "../.."] [tool.pixi.tasks.smoke_test] -cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "cd", ".."] +cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "cd", "../.."] args = [ { "arg" = "wheel_path", "default" = "$(python ../../tools/find_only_wheel.py)" }, ] diff --git a/tools/find_only_wheel.py b/tools/find_only_wheel.py index c00c7abe19..b77b8f7908 100644 --- a/tools/find_only_wheel.py +++ b/tools/find_only_wheel.py @@ -34,4 +34,4 @@ def find_only_pyodide_wheel(directory): wheel_path = find_only_pyodide_wheel(absolute_dist_path) # Print the name of the wheel file - print(wheel_path) \ No newline at end of file + print(wheel_path) From 7d42387426f390e9ba7289979eb50d8a12e003cc Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 22:07:01 -0400 Subject: [PATCH 32/44] Even more minor fixes --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 17c4f5d6ad..5a289a904c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -188,7 +188,7 @@ depends-on = ["install_xbuildenv"] cmd = ["cd", "tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "cd", "../.."] [tool.pixi.tasks.smoke_test] -cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "cd", "../.."] +cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "&&", "cd", "../.."] args = [ { "arg" = "wheel_path", "default" = "$(python ../../tools/find_only_wheel.py)" }, ] From d81928e67aea2069619ba5a4f0450e39e6e2b8e1 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Wed, 14 May 2025 23:12:34 -0400 Subject: [PATCH 33/44] Minor grammar changes --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 50da36f10b..420d453297 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -581,8 +581,8 @@ pixi run smoke_test $ABSOLUTE_PATH_TO_WHEEL > [!WARNING] > If there are multiple Pyodide wheels in the dist/ folder, the test will fail -> to prevent you from testing an outdated wheel. This scenario can happen when -> we update the rustworkx version, minimum Python version, or Pyodide. To fix, +> to prevent anyone from testing an outdated wheel. This scenario can happen when +> we update the rustworkx version, update the minimum Python ABI, or update Pyodide. To fix, > clean the directory with `rm -r dist` and re-run `pixi run build_pyodide` > [!TIP] From fba535193d6fe28bf006b68b52cd79c50767cb71 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 15 May 2025 20:32:40 -0400 Subject: [PATCH 34/44] Complete test suite runs --- tests/pyodide_smoke_test/smoke_test.mjs | 52 +++++++++++++++------- tests/pyodide_smoke_test/test_smoke.py | 59 ++++++++++++------------- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_smoke_test/smoke_test.mjs index 63e130b37d..87d6b267f8 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_smoke_test/smoke_test.mjs @@ -1,17 +1,17 @@ import { loadPyodide } from "pyodide"; -import * as fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; +import * as fs from "fs"; +import * as path from "path"; +import { fileURLToPath } from "url"; -async function getPyodideWithRustworkx() { +async function getPyodideWithDeps() { let pyodide = await loadPyodide(); await pyodide.loadPackage("micropip"); const micropip = pyodide.pyimport("micropip"); // Load rustworkx from local file system to Pyodide's const filePath = process.argv[2]; - if (process.argv[2] === '') { - throw new Error('Wheel path is empty, check the logs to see if there are multiple wheels in dist/.'); + if (process.argv[2] === "") { + throw new Error("Wheel path is empty, check the logs to see if there are multiple wheels in dist/."); } const filename = path.basename(filePath); @@ -20,24 +20,42 @@ async function getPyodideWithRustworkx() { pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); - // Install with micropip - await micropip.install(`emfs:/tmp/${filename}`); + // Install rustworkx + networkx for testing. We ignore the optional dependencies. + await micropip.install.callKwargs({ + requirements: [`emfs:/tmp/${filename}`, "numpy", "networkx"], + deps: false, + }) + return pyodide; } -async function getUnitTest() { +async function getUnitTests(pyodide) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); let unitTestFile = `${__dirname}/test_smoke.py`; - let unitTestData = fs.readFileSync(unitTestFile); - return unitTestData.toString(); + let unitTestDir = path.dirname(__dirname); + // Mount test directory to Pyodide's file system + let mountDir = "/tmp/tests"; + pyodide.FS.mkdirTree(mountDir); + pyodide.FS.mount(pyodide.FS.filesystems.NODEFS, { root: unitTestDir }, mountDir); + // Read the test runner file + let unitTestRunner = fs.readFileSync(unitTestFile); + return unitTestRunner.toString(); +} + +async function main() { + let pyodide = await getPyodideWithDeps(); + let unitTests = await getUnitTests(pyodide); + try { + await pyodide.runPythonAsync(unitTests); + console.log("Smoke test completed successfully"); + } catch (error) { + console.error("Error during smoke test:", error); + process.exit(1); + } } -async function runSmokeTest() { - let pyodide = await getPyodideWithRustworkx(); - let unitTest = await getUnitTest(); - return pyodide.runPythonAsync(unitTest); +if (process.argv[1] === import.meta.filename){ + await main(); } -await runSmokeTest(); -console.log("Smoke test completed successfully"); diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_smoke_test/test_smoke.py index 2a8f673449..84ad7bfc6e 100644 --- a/tests/pyodide_smoke_test/test_smoke.py +++ b/tests/pyodide_smoke_test/test_smoke.py @@ -1,38 +1,35 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import pathlib import sys import unittest +def main(): + if sys.platform == "emscripten": + tests_folder = "/tmp/tests/" + else: + # Set the path to the code to test + tests_folder = pathlib.Path(__file__).parent.parent.resolve() -@unittest.skipUnless(sys.platform == "emscripten", "Smoke tests target Pyodide") -class TestPyodide(unittest.TestCase): - def test_isomorphism(self): - # Adapted from tests/graph/test_isomorphic.py - import rustworkx - - n = 15 - upper_bound_k = (n - 1) // 2 - for k in range(1, upper_bound_k + 1): - for t in range(k, upper_bound_k + 1): - result = rustworkx.is_isomorphic( - rustworkx.generators.generalized_petersen_graph(n, k), - rustworkx.generators.generalized_petersen_graph(n, t), - ) - expected = (k == t) or (k == n - t) or (k * t % n == 1) or (k * t % n == n - 1) - self.assertEqual(result, expected) - - def test_rayon_works(self): - # This essentially tests that multi-threading is set to one core and does not panic - import rustworkx - - graph = rustworkx.generators.cycle_graph(10) - path_lenghts_floyd = rustworkx.floyd_warshall(graph) - path_lenghts_no_self = rustworkx.all_pairs_dijkstra_path_lengths(graph, lambda _: 1.0) - path_lenghts_dijkstra = {k: {**v, k: 0.0} for k, v in path_lenghts_no_self.items()} - self.assertEqual(path_lenghts_floyd, path_lenghts_dijkstra) + # Discover tests in the specified folder + loader = unittest.TestLoader() + suite = loader.discover(start_dir=tests_folder) - -if sys.platform == "emscripten": - suite = unittest.TestLoader().loadTestsFromTestCase(TestPyodide) + # Run the discovered tests runner = unittest.TextTestRunner() result = runner.run(suite) - if not result.wasSuccessful(): - sys.exit(1) + assert result.wasSuccessful() + + +if __name__ == "__main__" or sys.platform == "emscripten": + main() From a66716e609f8d3c2215284df9b13edd9aeb3ac12 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 15 May 2025 20:39:13 -0400 Subject: [PATCH 35/44] Run complete test suite --- pyproject.toml | 12 ++++----- .../package-lock.json | 2 +- .../package.json | 0 .../pyodide_runner.mjs} | 27 ++++++++++++++++++- .../test_smoke.py => pyodide_tests/runner.py} | 5 ++++ 5 files changed, 38 insertions(+), 8 deletions(-) rename tests/{pyodide_smoke_test => pyodide_tests}/package-lock.json (97%) rename tests/{pyodide_smoke_test => pyodide_tests}/package.json (100%) rename tests/{pyodide_smoke_test/smoke_test.mjs => pyodide_tests/pyodide_runner.mjs} (65%) rename tests/{pyodide_smoke_test/test_smoke.py => pyodide_tests/runner.py} (86%) diff --git a/pyproject.toml b/pyproject.toml index 5a289a904c..a2427c0cf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -184,18 +184,18 @@ cmd = ["pyodide", "build"] env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-memory,+mutable-globals $(pyodide config get rustflags)" } depends-on = ["install_xbuildenv"] -[tool.pixi.tasks.install_smoke_env] -cmd = ["cd", "tests/pyodide_smoke_test", "&&", "npm", "install", "&&", "cd", "../.."] +[tool.pixi.tasks.install_pyodide_test_env] +cmd = ["cd", "tests/pyodide_tests", "&&", "npm", "install", "&&", "cd", "../.."] -[tool.pixi.tasks.smoke_test] -cmd = ["cd", "tests/pyodide_smoke_test", "&&", "node", "smoke_test.mjs", "{{ wheel_path }}", "&&", "cd", "../.."] +[tool.pixi.tasks.pyodide_test] +cmd = ["cd", "tests/pyodide_tests", "&&", "node", "pyodide_runner.mjs", "{{ wheel_path }}", "&&", "cd", "../.."] args = [ { "arg" = "wheel_path", "default" = "$(python ../../tools/find_only_wheel.py)" }, ] -depends-on = ["install_smoke_env"] +depends-on = ["install_pyodide_test_env"] [tool.pixi.tasks.build_pyodide_and_test] -depends-on = ["build_pyodide", "smoke_test"] +depends-on = ["build_pyodide", "pyodide_test"] [tool.pyodide.build] xbuildenv_path = ".xbuildenv" diff --git a/tests/pyodide_smoke_test/package-lock.json b/tests/pyodide_tests/package-lock.json similarity index 97% rename from tests/pyodide_smoke_test/package-lock.json rename to tests/pyodide_tests/package-lock.json index f8fb54afb9..c982b94203 100644 --- a/tests/pyodide_smoke_test/package-lock.json +++ b/tests/pyodide_tests/package-lock.json @@ -1,5 +1,5 @@ { - "name": "pyodide_smoke_test", + "name": "pyodide_tests", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/tests/pyodide_smoke_test/package.json b/tests/pyodide_tests/package.json similarity index 100% rename from tests/pyodide_smoke_test/package.json rename to tests/pyodide_tests/package.json diff --git a/tests/pyodide_smoke_test/smoke_test.mjs b/tests/pyodide_tests/pyodide_runner.mjs similarity index 65% rename from tests/pyodide_smoke_test/smoke_test.mjs rename to tests/pyodide_tests/pyodide_runner.mjs index 87d6b267f8..6d6ad9a377 100644 --- a/tests/pyodide_smoke_test/smoke_test.mjs +++ b/tests/pyodide_tests/pyodide_runner.mjs @@ -1,3 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Running this file is the equivalent of running: + * python -m unittest discover . + * at the root of the tests folder. It works both with Pyodide and + * in a normal Python environment. + */ + +/* + Usage: node pyodide_runner.mjs + This loads Pyodide, rustworkx, networkx, numpy, and the test files + and runs the tests in memory with unittest. +*/ + import { loadPyodide } from "pyodide"; import * as fs from "fs"; import * as path from "path"; @@ -32,7 +57,7 @@ async function getPyodideWithDeps() { async function getUnitTests(pyodide) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - let unitTestFile = `${__dirname}/test_smoke.py`; + let unitTestFile = `${__dirname}/runner.py`; let unitTestDir = path.dirname(__dirname); // Mount test directory to Pyodide's file system let mountDir = "/tmp/tests"; diff --git a/tests/pyodide_smoke_test/test_smoke.py b/tests/pyodide_tests/runner.py similarity index 86% rename from tests/pyodide_smoke_test/test_smoke.py rename to tests/pyodide_tests/runner.py index 84ad7bfc6e..ba5c4b0aee 100644 --- a/tests/pyodide_smoke_test/test_smoke.py +++ b/tests/pyodide_tests/runner.py @@ -10,6 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. +# Running this file is the equivalent of running: +# python -m unittest discover . +# At the root of the tests folder. It works both with Pyodide and +# in a normal Python environment. + import pathlib import sys import unittest From 1599f18e805f70a1ee85f5ebaffebdf930e12e28 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 15 May 2025 20:41:20 -0400 Subject: [PATCH 36/44] Update CONTRIBUTING.md to reflect full test suite --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 420d453297..9ba730f0f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -568,15 +568,15 @@ a Pyodide wheel will be available in the `dist` folder if the build is successfu #### Testing Pyodide Wheels -After running `pixi run build_pyodide`, we can run a simple smoke test with: +After running `pixi run build_pyodide`, we can run the test suite with: ```bash -pixi run smoke_test +pixi run pyodide_test ``` It is also possible to run the smoke test with a specific wheel via an **absolute path**: ``` -pixi run smoke_test $ABSOLUTE_PATH_TO_WHEEL +pixi run pyodide_test $ABSOLUTE_PATH_TO_WHEEL ``` > [!WARNING] @@ -612,8 +612,8 @@ maps roughly to `1.86`. If that version was not yet stable, we could try picking After updating the versions in `[tool.pixi.dependencies]`, run `pixi lock` which will update `pixi.lock`. Onwards, all builds will use the same environment. As long as `pixi run build_pyodide` passes locally or on CI it should keep compiling and building. -Lastly, remember to update the Pyodide version in `tests/pyodide_smoke_test`. Change the version in `tests/pyodide_smoke_test/package.json` -and run `pixi run install_smoke_env` to generate the new lockfile. If you forget to update the Pyodide version, the smoke test +Lastly, remember to update the Pyodide version in `tests/pyodide_tests`. Change the version in `tests/pyodide_tests/package.json` +and run `pixi run install_pyodide_test_env` to generate the new lockfile. If you forget to update the Pyodide version, the smoke test will fail. ### Stable Branch Policy and Backporting From ec111ad5ae634dbdff34de7210851d00923cdf44 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 15 May 2025 20:47:08 -0400 Subject: [PATCH 37/44] Black --- tests/pyodide_tests/pyodide_runner.mjs | 2 +- tests/pyodide_tests/runner.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pyodide_tests/pyodide_runner.mjs b/tests/pyodide_tests/pyodide_runner.mjs index 6d6ad9a377..f70b665747 100644 --- a/tests/pyodide_tests/pyodide_runner.mjs +++ b/tests/pyodide_tests/pyodide_runner.mjs @@ -19,7 +19,7 @@ /* Usage: node pyodide_runner.mjs - This loads Pyodide, rustworkx, networkx, numpy, and the test files + This loads Pyodide, rustworkx, networkx, numpy. Then, it mounts the test folder and runs the tests in memory with unittest. */ diff --git a/tests/pyodide_tests/runner.py b/tests/pyodide_tests/runner.py index ba5c4b0aee..1d431ddd73 100644 --- a/tests/pyodide_tests/runner.py +++ b/tests/pyodide_tests/runner.py @@ -19,6 +19,7 @@ import sys import unittest + def main(): if sys.platform == "emscripten": tests_folder = "/tmp/tests/" From 289329c63838e33639d1de0cbcc5a0572f7907a7 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 15 May 2025 20:50:00 -0400 Subject: [PATCH 38/44] Minor correction --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ba730f0f8..942959ce51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -574,7 +574,7 @@ After running `pixi run build_pyodide`, we can run the test suite with: pixi run pyodide_test ``` -It is also possible to run the smoke test with a specific wheel via an **absolute path**: +It is also possible to run the tests with a specific wheel via an **absolute path**: ``` pixi run pyodide_test $ABSOLUTE_PATH_TO_WHEEL ``` From 65d8a5e05aa3072d281186ee98b38d4423015664 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sat, 17 May 2025 00:02:02 -0400 Subject: [PATCH 39/44] smoke test -> unit test --- CONTRIBUTING.md | 2 +- tests/pyodide_tests/pyodide_runner.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 942959ce51..b0fd311754 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -613,7 +613,7 @@ After updating the versions in `[tool.pixi.dependencies]`, run `pixi lock` which will use the same environment. As long as `pixi run build_pyodide` passes locally or on CI it should keep compiling and building. Lastly, remember to update the Pyodide version in `tests/pyodide_tests`. Change the version in `tests/pyodide_tests/package.json` -and run `pixi run install_pyodide_test_env` to generate the new lockfile. If you forget to update the Pyodide version, the smoke test +and run `pixi run install_pyodide_test_env` to generate the new lockfile. If you forget to update the Pyodide version, the tests will fail. ### Stable Branch Policy and Backporting diff --git a/tests/pyodide_tests/pyodide_runner.mjs b/tests/pyodide_tests/pyodide_runner.mjs index f70b665747..66aef9fe25 100644 --- a/tests/pyodide_tests/pyodide_runner.mjs +++ b/tests/pyodide_tests/pyodide_runner.mjs @@ -73,9 +73,9 @@ async function main() { let unitTests = await getUnitTests(pyodide); try { await pyodide.runPythonAsync(unitTests); - console.log("Smoke test completed successfully"); + console.log("Unit tests completed successfully"); } catch (error) { - console.error("Error during smoke test:", error); + console.error("Error during unit tests:", error); process.exit(1); } } From aa3451224cad7d95e41134bac7c5aac6f27ff071 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 29 May 2025 08:38:37 -0400 Subject: [PATCH 40/44] Solve nit --- releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml index 7e81fdb3d0..543443cb81 100644 --- a/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml +++ b/releasenotes/notes/pyodide-support-2172f2313b06f10d.yaml @@ -1,5 +1,5 @@ --- -upgrade: +features: - | `rustworkx` now has experimental support for `Pyodide `__. Pyodide is a Python distribution for WebAssembly that runs in the browser. From d7bb2159fb71434024e1db82d985403f04ead8d5 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 29 May 2025 21:43:41 -0400 Subject: [PATCH 41/44] Bump pyodide to 0.27.6 and pyodide-build to 0.30.4 --- pixi.lock | 12 ++++++------ pyproject.toml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pixi.lock b/pixi.lock index 48cebde8bf..5e34702185 100644 --- a/pixi.lock +++ b/pixi.lock @@ -81,7 +81,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.2-pyhac95a24_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.4-pyhac95a24_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-lock-0.1.0a7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda @@ -1052,9 +1052,9 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 888600 timestamp: 1736243563082 -- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.2-pyhac95a24_0.conda - sha256: 3b07fb5ce926ebaeb018df47a5a64b3238f07598009b223aae346c2e664cd47d - md5: 903583e853d3cc2c82fcdcc698b66940 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.4-pyhac95a24_0.conda + sha256: 97ca682e4bb732b3eb8516dba6863f20ab7cd2cbf1a1f7f0c437678dbee11373 + md5: 262efcb48d7313f522e72da0c11c027c depends: - auditwheel-emscripten ==0.1.0 - packaging @@ -1077,8 +1077,8 @@ packages: license_family: MOZILLA purls: - pkg:pypi/pyodide-build?source=hash-mapping - size: 101351 - timestamp: 1746476227410 + size: 101429 + timestamp: 1747737109119 - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda sha256: 30e4a37a40d7ee47b8be64015674ad7323c6f41e935d080fb6bcfb8ec1f02b3d md5: 0947e2092ff75a34f5e58bc5f689e247 diff --git a/pyproject.toml b/pyproject.toml index 772f29a040..0e73bde703 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,7 @@ default = {features = [], solve-group = "default"} [tool.pixi.dependencies] python = "==3.12.7" pip="==25.1" -pyodide-build = "==0.30.2" +pyodide-build = "==0.30.4" emscripten = "==3.1.58" nodejs = ">=22.13.0,<22.14" rust = "==1.86" @@ -178,11 +178,11 @@ rust-std-wasm32-unknown-emscripten = "==1.86" rust-src = "==1.86" [tool.pixi.tasks.install_xbuildenv] -cmd = ["pyodide", "xbuildenv", "install", "0.27.5"] +cmd = ["pyodide", "xbuildenv", "install", "0.27.6"] [tool.pixi.tasks.build_pyodide] cmd = ["pyodide", "build"] -env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-memory,+mutable-globals $(pyodide config get rustflags)" } +env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "$(pyodide config get rustflags) -C target-feature=+atomics,+bulk-memory,+mutable-globals" } depends-on = ["install_xbuildenv"] [tool.pyodide.build] From d6b1faa9ed03e4ae097124d1278697a26898a313 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Thu, 29 May 2025 21:54:40 -0400 Subject: [PATCH 42/44] Update to 0.27.6 and simplify setup --- .../package-lock.json => package-lock.json | 11 ++++++----- package.json | 6 ++++++ pyproject.toml | 6 +++--- tests/pyodide_tests/package.json | 5 ----- 4 files changed, 15 insertions(+), 13 deletions(-) rename tests/pyodide_tests/package-lock.json => package-lock.json (79%) create mode 100644 package.json delete mode 100644 tests/pyodide_tests/package.json diff --git a/tests/pyodide_tests/package-lock.json b/package-lock.json similarity index 79% rename from tests/pyodide_tests/package-lock.json rename to package-lock.json index c982b94203..fa96b31aa7 100644 --- a/tests/pyodide_tests/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { - "name": "pyodide_tests", + "name": "rustworkx-pyodide", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "rustworkx-pyodide", "dependencies": { - "pyodide": "^0.27.5" + "pyodide": "^0.27.6" } }, "node_modules/pyodide": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.5.tgz", - "integrity": "sha512-nXErpLzEdtQolt+sNQ/5mKuN9XTUwhxR2MRhRhZ6oDRGpYLXrOp5+kkTPGEwK+wn1ZA8+poNmoxKTj2sq/p9og==", + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.6.tgz", + "integrity": "sha512-ahiSHHs6iFKl2f8aO1wALINAlMNDLAtb44xCI87GQyH2tLDk8F8VWip3u1ZNIyglGSCYAOSFzWKwS1f9gBFVdg==", "license": "Apache-2.0", "dependencies": { "ws": "^8.5.0" diff --git a/package.json b/package.json new file mode 100644 index 0000000000..83e8fab2f6 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "name": "rustworkx-pyodide", + "dependencies": { + "pyodide": "^0.27.6" + } +} diff --git a/pyproject.toml b/pyproject.toml index 3d7dfe78e0..d7c45ec8c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -186,12 +186,12 @@ env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "-C target-feature=+atomics,+bulk-mem depends-on = ["install_xbuildenv"] [tool.pixi.tasks.install_pyodide_test_env] -cmd = ["cd", "tests/pyodide_tests", "&&", "npm", "install", "&&", "cd", "../.."] +cmd = ["npm", "install"] [tool.pixi.tasks.pyodide_test] -cmd = ["cd", "tests/pyodide_tests", "&&", "node", "pyodide_runner.mjs", "{{ wheel_path }}", "&&", "cd", "../.."] +cmd = ["node", "tests/pyodide_tests/pyodide_runner.mjs", "{{ wheel_path }}"] args = [ - { "arg" = "wheel_path", "default" = "$(python ../../tools/find_only_wheel.py)" }, + { "arg" = "wheel_path", "default" = "$(python tools/find_only_wheel.py)" }, ] depends-on = ["install_pyodide_test_env"] diff --git a/tests/pyodide_tests/package.json b/tests/pyodide_tests/package.json deleted file mode 100644 index 0876dd9cf6..0000000000 --- a/tests/pyodide_tests/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "pyodide": "^0.27.5" - } -} From 24f000ccbb4d4bb9c42807d2a5ec8f910434d355 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sat, 28 Jun 2025 22:49:08 -0400 Subject: [PATCH 43/44] Update pyodide build & pyodide --- pixi.lock | 12 ++++++------ pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index 5e34702185..f941b4dc29 100644 --- a/pixi.lock +++ b/pixi.lock @@ -81,7 +81,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.4-pyhac95a24_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.5-pyhac95a24_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-lock-0.1.0a7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda @@ -1052,9 +1052,9 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 888600 timestamp: 1736243563082 -- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.4-pyhac95a24_0.conda - sha256: 97ca682e4bb732b3eb8516dba6863f20ab7cd2cbf1a1f7f0c437678dbee11373 - md5: 262efcb48d7313f522e72da0c11c027c +- conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-build-0.30.5-pyhac95a24_0.conda + sha256: e05d69ad90d17d67cd3194b8acb02d7b09293fee01405989f407f0394ba97ec1 + md5: 8f8cfa1e405562340cfabd750f445192 depends: - auditwheel-emscripten ==0.1.0 - packaging @@ -1077,8 +1077,8 @@ packages: license_family: MOZILLA purls: - pkg:pypi/pyodide-build?source=hash-mapping - size: 101429 - timestamp: 1747737109119 + size: 101414 + timestamp: 1749078596512 - conda: https://conda.anaconda.org/conda-forge/noarch/pyodide-cli-0.3.0-pyh5d45ec7_0.conda sha256: 30e4a37a40d7ee47b8be64015674ad7323c6f41e935d080fb6bcfb8ec1f02b3d md5: 0947e2092ff75a34f5e58bc5f689e247 diff --git a/pyproject.toml b/pyproject.toml index 0e73bde703..8edbdca7b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,7 @@ default = {features = [], solve-group = "default"} [tool.pixi.dependencies] python = "==3.12.7" pip="==25.1" -pyodide-build = "==0.30.4" +pyodide-build = "==0.30.5" emscripten = "==3.1.58" nodejs = ">=22.13.0,<22.14" rust = "==1.86" @@ -178,7 +178,7 @@ rust-std-wasm32-unknown-emscripten = "==1.86" rust-src = "==1.86" [tool.pixi.tasks.install_xbuildenv] -cmd = ["pyodide", "xbuildenv", "install", "0.27.6"] +cmd = ["pyodide", "xbuildenv", "install", "0.27.7"] [tool.pixi.tasks.build_pyodide] cmd = ["pyodide", "build"] From 98c824cc84913d78a1d913b2c054422c729d84d5 Mon Sep 17 00:00:00 2001 From: Ivan Carvalho Date: Sat, 28 Jun 2025 22:53:02 -0400 Subject: [PATCH 44/44] Bump pyodide --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa96b31aa7..93f7378b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,14 +6,14 @@ "": { "name": "rustworkx-pyodide", "dependencies": { - "pyodide": "^0.27.6" + "pyodide": "^0.27.7" } }, "node_modules/pyodide": { - "version": "0.27.6", - "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.6.tgz", - "integrity": "sha512-ahiSHHs6iFKl2f8aO1wALINAlMNDLAtb44xCI87GQyH2tLDk8F8VWip3u1ZNIyglGSCYAOSFzWKwS1f9gBFVdg==", - "license": "Apache-2.0", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.7.tgz", + "integrity": "sha512-RUSVJlhQdfWfgO9hVHCiXoG+nVZQRS5D9FzgpLJ/VcgGBLSAKoPL8kTiOikxbHQm1kRISeWUBdulEgO26qpSRA==", + "license": "MPL-2.0", "dependencies": { "ws": "^8.5.0" }, diff --git a/package.json b/package.json index 83e8fab2f6..30fd79263f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rustworkx-pyodide", "dependencies": { - "pyodide": "^0.27.6" + "pyodide": "^0.27.7" } }