{"id":21891241,"url":"https://github.com/deathbeds/jupyterlite-pyodide-lock","last_synced_at":"2026-04-13T23:01:51.925Z","repository":{"id":231207506,"uuid":"781217408","full_name":"deathbeds/jupyterlite-pyodide-lock","owner":"deathbeds","description":"Create pre-solved environments for jupyterlite-pyodide-kernel with pyodide-lock","archived":false,"fork":false,"pushed_at":"2025-12-08T14:06:03.000Z","size":620,"stargazers_count":0,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-09T01:18:00.190Z","etag":null,"topics":["jupyterlite","jupyterlite-extension","pyodide"],"latest_commit_sha":null,"homepage":"https://jupyterlite-pyodide-lock.rtfd.org","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deathbeds.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-04-03T00:54:23.000Z","updated_at":"2025-12-08T14:06:09.000Z","dependencies_parsed_at":"2024-04-15T13:58:10.226Z","dependency_job_id":"7fc38a6b-36df-46d2-bf68-d818d5a0b9c0","html_url":"https://github.com/deathbeds/jupyterlite-pyodide-lock","commit_stats":null,"previous_names":["bollwyvl/jupyterlite-pyodide-lock","deathbeds/jupyterlite-pyodide-lock"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/deathbeds/jupyterlite-pyodide-lock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deathbeds%2Fjupyterlite-pyodide-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deathbeds%2Fjupyterlite-pyodide-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deathbeds%2Fjupyterlite-pyodide-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deathbeds%2Fjupyterlite-pyodide-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deathbeds","download_url":"https://codeload.github.com/deathbeds/jupyterlite-pyodide-lock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deathbeds%2Fjupyterlite-pyodide-lock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31774547,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T20:17:16.280Z","status":"ssl_error","status_checked_at":"2026-04-13T20:17:08.216Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jupyterlite","jupyterlite-extension","pyodide"],"created_at":"2024-11-28T12:23:42.169Z","updated_at":"2026-04-13T23:01:51.919Z","avatar_url":"https://github.com/deathbeds.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jupyterlite-pyodide-lock\n\n\u003e Build reproducible Jupyter Lite sites with [jupyterlite-pyodide-kernel][jlpk] and\n\u003e [pyodide-lock][pl].\n\n|            docs             |                                          install                                           |                build                 |\n| :-------------------------: | :----------------------------------------------------------------------------------------: | :----------------------------------: |\n| [![docs][docs-badge]][docs] | [![install from pypi][pypi-badge]][pypi] [![install from conda-forge][conda-badge]][conda] | [![build][workflow-badge]][workflow] |\n\n[docs]: https://jupyterlite-pyodide-lock.rtfd.org\n[docs-badge]:\n  https://readthedocs.org/projects/jupyterlite-pyodide-lock/badge/?version=stable\n[conda-badge]: https://img.shields.io/conda/vn/conda-forge/jupyterlite-pyodide-lock\n[conda]: https://anaconda.org/conda-forge/jupyterlite-pyodide-lock\n[pypi-badge]: https://img.shields.io/pypi/v/jupyterlite-pyodide-lock\n[pypi]: https://pypi.org/project/jupyterlite-pyodide-lock\n[workflow-badge]:\n  https://github.com/deathbeds/jupyterlite-pyodide-lock/actions/workflows/test.yml/badge.svg?branch=main\n[workflow]:\n  https://github.com/deathbeds/jupyterlite-pyodide-lock/actions/workflows/test.yml?query=branch%3Amain\n\nView the full documentation on [ReadTheDocs][rtfd].\n\n[jlpk]: https://github.com/jupyterlite/pyodide-kernel\n[pl]: https://github.com/pyodide/pyodide-lock\n[rtfd]: https://jupyterlite-pyodide-lock.rtfd.org/en/stable\n[conda-forge]: https://conda-forge.org/\n\n## Overview\n\n`jupyterlite-pyodide-lock` avoids **run time** [`jupyterlite-pyodide-kernel`][jlpk]\npackage management ambiguity with a **build time**\n[`pyodide-lock.json`](https://github.com/pyodide/pyodide-lock).\n\n## Install\n\n`jupyterlite-pyodide-lock` is distributed on [PyPI], for use with `pip` or `uv`, and\n[conda-forge], for use with `conda`, `mamba`, `micromamba`, or `pixi`.\n\n```bash\npip install jupyterlite-pyodide-lock\n# or\nconda install jupyterlite-pyodide-lock\n```\n\n### Addons\n\nSeveral JupyterLite addons extend a build, and are configured in\n`jupyter_lite_config.json`.\n\n| addon                     | features                                                                    |\n| ------------------------- | --------------------------------------------------------------------------- |\n| `PyodideLockAddon`        | re-configure `jupyterlite-pyodide-kernel` with a custom `pyodide-lock.json` |\n| `PyodideLockOfflineAddon` | replace remote CDN URLs with local packages to be served with the site      |\n| `FixHtmlAddon`            | fix paths in HTML generated by third-pary JupyterLite addons                |\n\n### Lockers\n\nSeveral additional installable packages provide configurable lockers, or approaches for\nresolving `pyodide-lock.json`; see the [documentation][rtfd] for more information.\n\n| package                              | lockers           | key dependencies |\n| ------------------------------------ | ----------------- | ---------------- |\n| `jupyterlite-pyodide-lock`           | `BrowserLocker`   | `tornado`        |\n| `jupyterlite-pyodide-lock-webdriver` | `WebDriverLocker` | `selenium`       |\n| `jupyterlite-pyodide-lock-uv`        | `UvLocker`        | `uv`             |\n\n## How it works\n\n`jupyterlite-pyodide-lock` works by:\n\n- downloading a `pyodide` distribution\n- using a `pyodide`-aware solver, either\n  - the build environment with `uv`\n  - in a browser, with `micropip` and a minimal `pyodide` runtime, managed by Python's\n    `webbrowser` or `selenium`\n- compiling a set of packages from the build environment, PyPI, or elsewhere\n  - _(optional) limiting to packages to ones built before a given date for\n    reproducibility_\n- returning the list to build a new `pyodide-lock.json`\n  - _(optional) copying all downloaded wheels to be served along with the site_\n- configuring [`jupyterlite-pyodide-kernel`][jlpk] to use the new `pyodide-lock.json`\n  - _(optional) rewriting other generated `jupyter-config-data` in JupyterLite app\n    pages_\n\n## Examples\n\nUse `jupyterlite-pyodide-lock` to [minimally](#minimal-example) provide a more\npredictable baseline `pyodide` runtime environment, or ensure complex dependencies like\n[widgets](#widgets-example) are consistent over time.\n\n### Minimal Example\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n  \u003ci\u003eEnsure \u003ccode\u003epyodide-kernel\u003c/code\u003e's dependencies are locked, assuming\n  \u003ccode\u003epip\u003c/code\u003e.\u003c/i\u003e\n\u003c/summary\u003e\n\n#### Create the Minimal Build Environment\n\n- make a `requirements.txt`\n\n  ```text\n  jupyterlite-core ==0.7.0\n  jupyterlite-pyodide-kernel ==0.7.0\n  jupyterlite-pyodide-lock ==0.2.0\n  jupyterlite-pyodide-lock-uv ==0.2.0\n  ```\n\n- Run:\n\n  ```bash\n  pip install -r requirements.txt\n  ```\n\n#### Configure the Minimal Site\n\n- build a `jupyter_lite_config.json`:\n\n  ```json\n  {\n    \"PyodideLockAddon\": {\n      \"enabled\": true,\n      \"locker\": \"UvLocker\"\n    }\n  }\n  ```\n\n#### Build the Minimal Site\n\n- build a `jupyter_lite_config.json`:\n\n  ```bash\n  jupyter lite build\n  ```\n\n#### Check the Minimal Site Works\n\n- start a simple, local development server\n\n  ```bash\n  cd _output\n  python -m http.server -b 127.0.0.1\n  ```\n\n- visit the site at `http://127.0.0.1:8000/`\n- make a new Notebook\n  - use basic `python` features\n\n\u003c/details\u003e\n\n### Widgets Example\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n  \u003ci\u003eBuild a JupyterLite site with all the packages needed to run\n  \u003ccode\u003eipywidgets\u003c/code\u003e in a Notebook, assuming \u003ccode\u003emamba\u003c/code\u003e.\u003c/i\u003e\n\u003c/summary\u003e\n\n#### Create the Widget Build Environment\n\n- make an `environment.yml`\n\n  ```yaml\n  channels:\n    - conda-forge\n    - nodefaults\n  dependencies:\n    - ipywidgets ==8.1.8\n    - jupyterlite-core ==0.7.0\n    - jupyterlite-pyodide-kernel ==0.7.0\n    - jupyterlite-pyodide-lock-recommended ==0.2.0\n  ```\n\n  - _the `-recommended` package includes `firefox` and `geckodriver`_\n  - _optionally use a tool like [`conda-lock`][conda-lock] or [`pixi`][pixi] to create a\n    lockfile for the build environment_\n\n[conda-lock]: https://github.com/conda-incubator/conda-lock\n[pixi]: https://github.com/prefix-dev/pixi\n\n- Run:\n\n  ```bash\n  mamba env update --file environment.yml --prefix .venv\n  source activate .venv # or just `activate .venv` on windows\n  ```\n\n#### Configure the Widgets Site\n\n- build a `jupyter_lite_config.json`:\n\n  ```json\n  {\n    \"PyodideLockAddon\": {\n      \"enabled\": true,\n      \"constraints\": [\"traitlets ==5.14.3\"],\n      \"specs\": [\"ipywidgets ==8.1.8\"],\n      \"extra_preload_packages\": [\"ipywidgets\"]\n    },\n    \"PyodideLockOfflineAddon\": {\n      \"enabled\": true\n    }\n  }\n  ```\n\n  - _note the tight `ipywidgets` pin, ensuring compatibility with the build environment_\n  - _while not required, the `constraints` option allows for controlling transitive\n    dependencies_\n    - _this feature requires `micropip \u003e=0.9.0`, which is only compatible with\n      `pyodide \u003e=0.27`_\n\n#### Build the Site with Widgets\n\n- build a `jupyter_lite_config.json`:\n\n  ```bash\n  jupyter lite build\n  ```\n\n#### Check Widgets Works Offline\n\n- disconnect from the internet ✈️\n  - _this step is optional, but is the most reliable way to validate a reproducible\n    site_\n- start a simple, local development server\n\n  ```bash\n  cd _output\n  python -m http.server -b 127.0.0.1\n  ```\n\n- visit the site at `http://127.0.0.1:8000/`\n- make a new Notebook\n  - see that `ipywidgets` can be imported, and widgets work:\n\n    ```python\n    import ipywidgets\n    ipywidgets.FloatSlider()\n    ```\n\n\u003c/details\u003e\n\n## Motivation\n\n- By default, a `pyodide` distribution provides a precise set of hundreds of package\n  versions known to work together in the browser, described in its `pyodide-lock.json`.\n\n- Among these packages is `micropip`, which gives site users the ability to install\n  packages _not_ included in `pyodide-lock.json`. These may be served along with an HTML\n  page, downloaded from PyPI, or anywhere on the internet. `jupyterlite-pyodide-kernel`\n  uses this capability to install itself, and its dependencies.\n  - At run time, `piplite` provides a `micropip`-based shim for the IPython `%pip`\n    magic, the most portable approach for interactive package management in Notebook\n    documents.\n\n- `micropip` (and `%pip`) are powerful for interactive usage, but can cause headaches\n  when upstream versions (or their dependencies) change in ways that either no longer\n  provide the same API expected by the exact versions of `pyodide`, `pyodide-kernel`,\n  and JupyterLab extensions in a deployed JupyterLite site.\n\n`jupyterlite-pyodide-lock` gives content authors tools to manage their effective\n`pyodide` distribution, making it easier to build, verify, and maintain predictable,\ninteractive computing environments for future site visitors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeathbeds%2Fjupyterlite-pyodide-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeathbeds%2Fjupyterlite-pyodide-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeathbeds%2Fjupyterlite-pyodide-lock/lists"}