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/.github/workflows/main.yml b/.github/workflows/main.yml index 30e7596ad6..60b7ae61cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -199,3 +199,16 @@ jobs: with: name: html_docs path: docs/build/html + pyodide-build: + if: github.repository_owner == 'Qiskit' + needs: [tests] + name: Pyodide Build + Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - 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_and_test diff --git a/.gitignore b/.gitignore index ee3e0af630..bf64c44dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,15 @@ rustworkx-core/Cargo.lock **/.DS_Store venv/ .python-version + +# pixi environments +.pixi +*.egg-info + +# pyodide xbuild +.xbuildenv + +# Node.js +node_modules/ +jspm_packages/ +.npm diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2541287320..d9ad401e28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -560,6 +560,80 @@ 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 + +After running `pixi run build_pyodide`, we can run the test suite with: + +```bash +pixi run pyodide_test +``` + +It is also possible to run the tests with a specific wheel via an **absolute path**: +``` +pixi run pyodide_test $ABSOLUTE_PATH_TO_WHEEL +``` + +> [!WARNING] +> If there are multiple Pyodide wheels in the dist/ folder, the test will fail +> 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] +> You can build and test with a single command with `pixi run build_pyodide_and_test`. + +#### 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, update `[tool.pixi.tasks.install_xbuildenv]` to install +the selected version of Pyodide. + +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 [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 +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 tests +will fail. + ### Stable Branch Policy and Backporting The stable branch is intended to be a safe source of fixes for high-impact bugs, diff --git a/docs/source/install.rst b/docs/source/install.rst index 071f25e347..f87413e3de 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -117,6 +117,10 @@ source. - WASM (Emscripten) - :ref:`tier-experimental` - + * - Pyodide + - WASM (Emscripten) + - :ref:`tier-experimental` + - .. _manylinux 2014: https://peps.python.org/pep-0599/> diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..93f7378b14 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "rustworkx-pyodide", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rustworkx-pyodide", + "dependencies": { + "pyodide": "^0.27.7" + } + }, + "node_modules/pyodide": { + "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" + }, + "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/package.json b/package.json new file mode 100644 index 0000000000..30fd79263f --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "name": "rustworkx-pyodide", + "dependencies": { + "pyodide": "^0.27.7" + } +} diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000000..f941b4dc29 --- /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.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 + - 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.5-pyhac95a24_0.conda + sha256: e05d69ad90d17d67cd3194b8acb02d7b09293fee01405989f407f0394ba97ec1 + md5: 8f8cfa1e405562340cfabd750f445192 + 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: 101414 + timestamp: 1749078596512 +- 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 f0a576d716..8094a9659f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,19 +110,19 @@ target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] [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 = [ @@ -152,4 +152,45 @@ 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.5" +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.7"] + +[tool.pixi.tasks.build_pyodide] +cmd = ["pyodide", "build"] +env = { RUSTC_BOOTSTRAP = "1", RUSTFLAGS = "$(pyodide config get rustflags) -C target-feature=+atomics,+bulk-memory,+mutable-globals" } +depends-on = ["install_xbuildenv"] + +[tool.pixi.tasks.install_pyodide_test_env] +cmd = ["npm", "install"] + +[tool.pixi.tasks.pyodide_test] +cmd = ["node", "tests/pyodide_tests/pyodide_runner.mjs", "{{ wheel_path }}"] +args = [ + { "arg" = "wheel_path", "default" = "$(python tools/find_only_wheel.py)" }, +] +depends-on = ["install_pyodide_test_env"] + +[tool.pixi.tasks.build_pyodide_and_test] +depends-on = ["build_pyodide", "pyodide_test"] + +[tool.pyodide.build] +xbuildenv_path = ".xbuildenv" diff --git a/tests/pyodide_tests/pyodide_runner.mjs b/tests/pyodide_tests/pyodide_runner.mjs new file mode 100644 index 0000000000..66aef9fe25 --- /dev/null +++ b/tests/pyodide_tests/pyodide_runner.mjs @@ -0,0 +1,86 @@ +/* + * 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. Then, it mounts the test folder + and runs the tests in memory with unittest. +*/ + +import { loadPyodide } from "pyodide"; +import * as fs from "fs"; +import * as path from "path"; +import { fileURLToPath } from "url"; + +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/."); + } + const filename = path.basename(filePath); + + const wheelPath = path.resolve(filePath); + const wheelData = fs.readFileSync(wheelPath); + + pyodide.FS.writeFile(`/tmp/${filename}`, new Uint8Array(wheelData)); + + // 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 getUnitTests(pyodide) { + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + let unitTestFile = `${__dirname}/runner.py`; + 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("Unit tests completed successfully"); + } catch (error) { + console.error("Error during unit tests:", error); + process.exit(1); + } +} + +if (process.argv[1] === import.meta.filename){ + await main(); +} + diff --git a/tests/pyodide_tests/runner.py b/tests/pyodide_tests/runner.py new file mode 100644 index 0000000000..1d431ddd73 --- /dev/null +++ b/tests/pyodide_tests/runner.py @@ -0,0 +1,41 @@ +# 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. + +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() + + # Discover tests in the specified folder + loader = unittest.TestLoader() + suite = loader.discover(start_dir=tests_folder) + + # Run the discovered tests + runner = unittest.TextTestRunner() + result = runner.run(suite) + assert result.wasSuccessful() + + +if __name__ == "__main__" or sys.platform == "emscripten": + main() diff --git a/tools/find_only_wheel.py b/tools/find_only_wheel.py new file mode 100644 index 0000000000..b77b8f7908 --- /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)