From c593de234174f0d0d0fa6683e2c6e1e5a04db44e Mon Sep 17 00:00:00 2001 From: Andre Manoel Date: Fri, 30 Oct 2026 17:07:45 -0300 Subject: [PATCH 1/3] chore: reduce release tooling dependencies Restore the previous Graphify version and use nbclient for notebook execution. Remove the Jupyter frontend and nbconvert dependency stack while retaining security constraints, audits and isolated tooling setup. Signed-off-by: Andre Manoel --- .agents/requirements-graphify.in | 2 +- .agents/requirements-graphify.txt | 9 +- .agents/tests/test_structural_impact.py | 6 +- .agents/tools/structural_impact.py | 13 +- .github/workflows/build-notebooks.yml | 2 +- DEVELOPMENT.md | 7 +- Makefile | 25 +- constraints.txt | 1 - docs/scripts/build_notebooks_cached.sh | 15 +- docs/scripts/prepare_nbconvert_css.py | 76 --- .../tests/docs/test_build_notebooks_cached.py | 28 +- .../tests/docs/test_prepare_nbconvert_css.py | 123 ---- pyproject.toml | 9 +- uv.lock | 616 +----------------- 14 files changed, 58 insertions(+), 874 deletions(-) delete mode 100644 docs/scripts/prepare_nbconvert_css.py delete mode 100644 packages/data-designer/tests/docs/test_prepare_nbconvert_css.py diff --git a/.agents/requirements-graphify.in b/.agents/requirements-graphify.in index 01b50857d..cdc21b1fa 100644 --- a/.agents/requirements-graphify.in +++ b/.agents/requirements-graphify.in @@ -1 +1 @@ -graphifyy==0.9.96 +graphifyy==0.4.23 diff --git a/.agents/requirements-graphify.txt b/.agents/requirements-graphify.txt index 3bf7cd63d..468c38108 100644 --- a/.agents/requirements-graphify.txt +++ b/.agents/requirements-graphify.txt @@ -1,20 +1,13 @@ -graphifyy==0.9.96 +graphifyy==0.4.23 networkx==3.6.1 -numpy==2.4.6 -pyyaml==6.0.3 -rapidfuzz==3.14.6 tree-sitter==0.25.2 -tree-sitter-bash==0.25.1 tree-sitter-c==0.24.2 tree-sitter-c-sharp==0.23.5 tree-sitter-cpp==0.23.4 tree-sitter-elixir==0.3.5 -tree-sitter-fortran==0.6.0 tree-sitter-go==0.25.0 -tree-sitter-groovy==0.1.2 tree-sitter-java==0.23.5 tree-sitter-javascript==0.25.0 -tree-sitter-json==0.24.8 tree-sitter-julia==0.23.1 tree-sitter-kotlin==1.1.0 tree-sitter-lua==0.5.0 diff --git a/.agents/tests/test_structural_impact.py b/.agents/tests/test_structural_impact.py index e5cc37bbc..eaaca6c86 100644 --- a/.agents/tests/test_structural_impact.py +++ b/.agents/tests/test_structural_impact.py @@ -22,7 +22,7 @@ from unittest.mock import patch sys.argv = sys.argv[1:] -with patch("graphify.export.to_json", return_value=False): +with patch("graphify.export.to_json", side_effect=OSError("graph export failed")): runpy.run_path(sys.argv[0], run_name="__main__") """ @@ -99,7 +99,7 @@ def test_full_graph_round_trip(repo_root: Path, run_tool: Callable[..., subproce assert "**no changes**" in run_tool("--full", "--previous-graph", str(graph_path)).stdout -@pytest.mark.parametrize("metadata", [{}, {"graphify_version": "0.4.23"}, {"schema_version": -1}]) +@pytest.mark.parametrize("metadata", [{}, {"graphify_version": "0.9.96"}, {"schema_version": -1}]) def test_incompatible_baseline_is_reset( repo_root: Path, run_tool: Callable[..., subprocess.CompletedProcess[str]], metadata: dict[str, object] ) -> None: @@ -144,7 +144,7 @@ def test_rejected_export_preserves_baselines( result = run_tool("--full", reject_export=True, check=False) assert result.returncode != 0 - assert "baselines were not updated" in result.stderr + assert "graph export failed" in result.stderr assert baselines.read_bytes() == previous diff --git a/.agents/tools/structural_impact.py b/.agents/tools/structural_impact.py index c351fbb49..a60bf32cf 100644 --- a/.agents/tools/structural_impact.py +++ b/.agents/tools/structural_impact.py @@ -22,13 +22,15 @@ from graphify.analyze import god_nodes, graph_diff from graphify.build import build_from_json from graphify.cluster import cluster, score_all -from graphify.export import GRAPH_SCHEMA_VERSION, to_json +from graphify.export import to_json from graphify.extract import extract from networkx.readwrite import json_graph _REPO_ROOT_DEFAULT = Path(__file__).resolve().parent.parent.parent _REPO_ROOT = _REPO_ROOT_DEFAULT _GRAPHIFY_VERSION = version("graphifyy") +# Graphify 0.4.x does not stamp its exports, so version our saved report format. +GRAPH_SCHEMA_VERSION = 1 # DD layering: interface -> engine -> config (left depends on right). _LEGAL_DIRECTIONS = {("interface", "engine"), ("interface", "config"), ("engine", "config")} @@ -111,8 +113,12 @@ def _build_graph(files: list[Path]) -> _Analysis: """Extract, build directed graph, cluster, and find god nodes.""" if not files: raise ValueError(f"No Python source files found under {_REPO_ROOT}") - ext = extract(files, root=_REPO_ROOT, cache_root=_REPO_ROOT) + ext = extract(files, cache_root=_REPO_ROOT) G = build_from_json(ext, directed=True) + G.graph.update(graphify_version=_GRAPHIFY_VERSION, schema_version=GRAPH_SCHEMA_VERSION) + for _, node in G.nodes(data=True): + if node.get("source_file"): + node["source_file"] = _rel(node["source_file"]) comms = cluster(G) return _Analysis(graph=G, communities=comms, cohesion=score_all(G, comms), god_nodes=god_nodes(G, top_n=15)) @@ -370,8 +376,7 @@ def _full_mode(previous_graph_path: str | None = None) -> str: out_dir = _REPO_ROOT / "graphify-out" out_dir.mkdir(exist_ok=True) # This is a complete source snapshot, so deletions must replace the old graph. - if not to_json(G, communities, str(out_dir / "graph.json"), force=True): - raise RuntimeError("Could not save the structural graph; baselines were not updated") + to_json(G, communities, str(out_dir / "graph.json")) (out_dir / "baselines.json").write_text( json.dumps( { diff --git a/.github/workflows/build-notebooks.yml b/.github/workflows/build-notebooks.yml index 80b8af897..7c2d96578 100644 --- a/.github/workflows/build-notebooks.yml +++ b/.github/workflows/build-notebooks.yml @@ -50,7 +50,7 @@ jobs: - name: Set up Python run: uv python install 3.11 - name: Install notebook dependencies - run: make install-docs-deps DOCS_PYTHON_VERSION=3.11 NOTEBOOK_CSS=0 + run: make install-docs-deps DOCS_PYTHON_VERSION=3.11 - name: Restore notebook cache if: inputs.use_cache id: cache diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 94a34bde9..cb23a717a 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -25,13 +25,12 @@ cd DataDesigner # Install with dev dependencies make install-dev -# Or, if you use Jupyter / IPython for development +# Or, to execute the tutorial and documentation notebooks make install-dev-notebooks ``` -Notebook setup uses the patched nbconvert PyPI release, including its HTML stylesheets. -The `install-notebook-css` target reuses existing stylesheets and can restore missing assets from a -checksum-pinned wheel. Use `NOTEBOOK_CSS=0` to skip that restoration check in execution-only environments. +Notebook setup installs the kernel and execution tools used by Jupytext and the Fern notebook pipeline. +Install a Jupyter frontend separately if you want to edit notebooks in its browser interface. ### Verify Your Setup diff --git a/Makefile b/Makefile index 01ea3a357..b07fe207f 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,6 @@ help: @echo " generate-fern-notebooks - Convert docs/notebook_source/*.py → fern/components/notebooks/{json,ts}" @echo " generate-fern-notebooks-with-outputs - Full pipeline: execute notebooks (needs API key), colabify, convert to Fern" @echo " install-docs-deps - Install docs and notebook dependencies" - @echo " install-notebook-css - Restore nbconvert HTML export stylesheets" @echo " prepare-fern-release VERSION=X.Y.Z - Add or refresh Fern version files for release preview" @echo " check-fern-release-version VERSION=X.Y.Z - Verify Fern has a version entry for release publishing" @echo " prepare-fern-docs - Generate local Fern artifacts" @@ -179,9 +178,8 @@ test-graphify: install-graphify install-dev-notebooks: @echo "📦 Installing DataDesigner workspace with notebook dependencies..." @echo " Packages: data-designer-config → data-designer-engine → data-designer" - @echo " Groups: dev + docs + notebooks (Jupyter, jupytext, etc.)" + @echo " Groups: dev + docs + notebooks (Jupytext, kernel, execution tools)" uv sync --all-packages --group dev --group docs --group notebooks - @$(MAKE) install-notebook-css $(call install-pre-commit-hooks) @echo "✅ Dev + docs + notebooks installation complete!" @echo "" @@ -511,7 +509,7 @@ update-license-headers: DOCS_PYTHON_VERSION ?= 3.13 DOCS_PYTHON ?= .venv/bin/python DOCS_JUPYTEXT ?= .venv/bin/jupytext -NOTEBOOK_CSS ?= 1 +DOCS_NBCLIENT ?= $(dir $(DOCS_JUPYTEXT))jupyter-execute FERN_VERSION ?= $(shell jq -r .version fern/fern.config.json) FERN ?= npx -y fern-api@$(FERN_VERSION) FERN_ORG ?= $(shell jq -r .organization fern/fern.config.json) @@ -526,16 +524,6 @@ DOCS_CERTS = SSL_CERT_FILE=$$($(DOCS_PYTHON) -c "import certifi; print(certifi.w install-docs-deps: @echo "📦 Installing docs dependencies (Python $(DOCS_PYTHON_VERSION))..." uv sync --python $(DOCS_PYTHON_VERSION) --all-packages --group docs --group notebooks - @$(MAKE) install-notebook-css - -install-notebook-css: -ifeq ($(NOTEBOOK_CSS),1) - $(DOCS_CERTS) $(DOCS_PYTHON) docs/scripts/prepare_nbconvert_css.py -else ifeq ($(NOTEBOOK_CSS),0) - @echo "Skipping nbconvert CSS (notebook execution only)." -else - $(error NOTEBOOK_CSS must be 0 or 1) -endif prepare-fern-release: ifndef VERSION @@ -599,7 +587,7 @@ serve-fern-docs-local-theme: convert-execute-notebooks: ifeq ($(USE_CACHE),1) @echo "📓 Converting Python tutorials to notebooks (with caching)..." - @$(DOCS_CERTS) DOCS_JUPYTEXT=$(DOCS_JUPYTEXT) bash docs/scripts/build_notebooks_cached.sh + @$(DOCS_CERTS) DOCS_JUPYTEXT=$(DOCS_JUPYTEXT) DOCS_NBCLIENT=$(DOCS_NBCLIENT) bash docs/scripts/build_notebooks_cached.sh else @echo "📓 Converting Python tutorials to notebooks and executing ($(DOCS_PYTHON))..." @rm -rf docs/notebooks @@ -611,7 +599,10 @@ else for f in docs/notebook_source/*.py; do \ [ -f "$$f" ] || continue; \ echo "▶ executing $$f"; \ - $(DOCS_JUPYTEXT) --to ipynb --execute "$$f" || failed="$$failed\n • $$f"; \ + if ! { $(DOCS_JUPYTEXT) --to ipynb "$$f" && $(DOCS_NBCLIENT) --inplace "$${f%.py}.ipynb"; }; then \ + rm -f "$${f%.py}.ipynb"; \ + failed="$$failed\n • $$f"; \ + fi; \ done; \ for f in docs/notebook_source/*.ipynb; do [ -f "$$f" ] && mv "$$f" docs/notebooks/; done; \ rm -rf docs/notebook_source/artifacts; \ @@ -818,7 +809,7 @@ clean-test-coverage: format format-check format-check-config format-check-engine format-check-interface \ format-config format-engine format-interface \ generate-colab-notebooks generate-fern-notebooks generate-fern-notebooks-with-outputs help \ - install install-dev install-dev-notebooks install-dev-recipes install-docs-deps install-graphify install-notebook-css \ + install install-dev install-dev-notebooks install-dev-recipes install-docs-deps install-graphify \ lint lint-config lint-engine lint-fix lint-fix-config lint-fix-engine lint-fix-interface lint-interface lock-graphify \ perf-import perf-import-runtime prepare-fern-docs prepare-fern-release publish serve-fern-docs-dev serve-fern-docs-local-theme serve-fern-docs-locally show-versions \ health-checks \ diff --git a/constraints.txt b/constraints.txt index 7144c4482..761159157 100644 --- a/constraints.txt +++ b/constraints.txt @@ -4,7 +4,6 @@ # uv pip install data-designer -c constraints.txt # uv add data-designer -c constraints.txt anyio>=4.14.2 -bleach>=6.4.0 cryptography>=50.0.0 filelock>=4.2.1 idna>=3.15 diff --git a/docs/scripts/build_notebooks_cached.sh b/docs/scripts/build_notebooks_cached.sh index 74164ca6d..fc43ad41f 100755 --- a/docs/scripts/build_notebooks_cached.sh +++ b/docs/scripts/build_notebooks_cached.sh @@ -25,6 +25,7 @@ SOURCE_DIR="$REPO_ROOT/docs/notebook_source" OUTPUT_DIR="$REPO_ROOT/docs/notebooks" CACHE_DIR="${1:-$REPO_ROOT/.notebook-cache}" DOCS_JUPYTEXT="${DOCS_JUPYTEXT:-$REPO_ROOT/.venv/bin/jupytext}" +DOCS_NBCLIENT="${DOCS_NBCLIENT:-$(dirname "$DOCS_JUPYTEXT")/jupyter-execute}" NOTEBOOK_EXECUTION_ATTEMPTS="${NOTEBOOK_EXECUTION_ATTEMPTS:-1}" NOTEBOOK_RETRY_DELAY_SECONDS="${NOTEBOOK_RETRY_DELAY_SECONDS:-15}" @@ -37,11 +38,13 @@ if [[ ! "$NOTEBOOK_RETRY_DELAY_SECONDS" =~ ^[0-9]+$ ]]; then exit 1 fi -if [ ! -x "$DOCS_JUPYTEXT" ]; then - echo "❌ Missing jupytext executable: $DOCS_JUPYTEXT" - echo "Run 'make install-dev-notebooks' first." - exit 1 -fi +for notebook_tool in "$DOCS_JUPYTEXT" "$DOCS_NBCLIENT"; do + if [ ! -x "$notebook_tool" ]; then + echo "❌ Missing notebook executable: $notebook_tool" + echo "Run 'make install-dev-notebooks' first." + exit 1 + fi +done execute_notebook() { local src="$1" @@ -51,7 +54,7 @@ execute_notebook() { for ((attempt = 1; attempt <= NOTEBOOK_EXECUTION_ATTEMPTS; attempt++)); do rm -f "$output" - if "$DOCS_JUPYTEXT" --to ipynb --execute "$src"; then + if "$DOCS_JUPYTEXT" --to ipynb "$src" && "$DOCS_NBCLIENT" --inplace "$output"; then return 0 fi rm -f "$output" diff --git a/docs/scripts/prepare_nbconvert_css.py b/docs/scripts/prepare_nbconvert_css.py deleted file mode 100644 index f5d877aa8..000000000 --- a/docs/scripts/prepare_nbconvert_css.py +++ /dev/null @@ -1,76 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -from __future__ import annotations - -import base64 -import csv -import hashlib -import io -import os -import sys -from importlib.metadata import distribution -from pathlib import Path -from urllib.request import urlopen -from zipfile import ZipFile - -WHEEL_URL = ( - "https://files.pythonhosted.org/packages/81/fd/a8d457af68848a8e5c12dd1d84ff35c415db471af78280e0703db4efbb12/" - "nbconvert-7.17.2-py3-none-any.whl" -) -WHEEL_SHA256 = "6a143c11c00bf831604da4b1be4347d36ea52367057eb920135d69bc2862fed2" -WHEEL_TEMPLATES = "nbconvert-7.17.2.data/data/share/jupyter/nbconvert/templates" -CSS_SHA256 = { - "classic/static/style.css": "5865a609f4437b0464bc121cd567b619074e540a0515a3b82f222f764eb51e01", - "lab/static/index.css": "917ff47850a7cc08fd0658026fda7672a85220aaab258e8849e891b37426f947", - "lab/static/theme-dark.css": "795f2d5069737cbeb5cba01e6b5c7cadbde227c909e43004c5a60f58d5160aec", - "lab/static/theme-light.css": "11bf3558fd3ed353a4c1401ac0c1730d01df073f6436d357c5bbf02a03bd6962", -} - - -def prepare_nbconvert_css() -> None: - """Restore the pinned nbconvert build's HTML assets without replacing its Python code.""" - installed = distribution("nbconvert") - templates = Path(sys.prefix) / "share" / "jupyter" / "nbconvert" / "templates" - if all( - (templates / name).is_file() and hashlib.sha256((templates / name).read_bytes()).hexdigest() == digest - for name, digest in CSS_SHA256.items() - ): - print("nbconvert HTML stylesheets already present.") - return - - record_entry = next( - entry for entry in installed.files or () if entry.name == "RECORD" and entry.parent.name.endswith(".dist-info") - ) - record_path = Path(installed.locate_file(record_entry)) - print("Downloading nbconvert HTML stylesheets from PyPI (256 KiB wheel)...") - with urlopen(WHEEL_URL, timeout=30) as response: - wheel = response.read() - if hashlib.sha256(wheel).hexdigest() != WHEEL_SHA256: - raise ValueError("nbconvert CSS wheel checksum mismatch") - with ZipFile(io.BytesIO(wheel)) as archive: - stylesheets = {name: archive.read(f"{WHEEL_TEMPLATES}/{name}") for name in CSS_SHA256} - for name, content in stylesheets.items(): - if hashlib.sha256(content).hexdigest() != CSS_SHA256[name]: - raise ValueError(f"nbconvert stylesheet checksum mismatch: {name}") - - paths = { - name: os.path.relpath(templates / name, record_path.parent.parent).replace(os.sep, "/") for name in stylesheets - } - records = [ - row for row in csv.reader(io.StringIO(record_path.read_text(encoding="utf-8"))) if row[0] not in paths.values() - ] - for name, content in stylesheets.items(): - destination = templates / name - destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_bytes(content) - digest = base64.urlsafe_b64encode(hashlib.sha256(content).digest()).rstrip(b"=").decode() - records.append([paths[name], f"sha256={digest}", str(len(content))]) - # Track restored assets so reinstalling or uninstalling nbconvert also removes them. - with record_path.open("w", newline="", encoding="utf-8") as record: - csv.writer(record).writerows(records) - print("Restored nbconvert HTML stylesheets; installed Python code is unchanged.") - - -if __name__ == "__main__": - prepare_nbconvert_css() diff --git a/packages/data-designer/tests/docs/test_build_notebooks_cached.py b/packages/data-designer/tests/docs/test_build_notebooks_cached.py index 9b9352559..4e21a8253 100644 --- a/packages/data-designer/tests/docs/test_build_notebooks_cached.py +++ b/packages/data-designer/tests/docs/test_build_notebooks_cached.py @@ -29,7 +29,8 @@ def test_rejects_invalid_retry_settings(tmp_path: Path, setting: str, value: str assert message in result.stdout -def test_retries_failed_notebook_execution(tmp_path: Path) -> None: +@pytest.mark.parametrize("failure_count", [1, 2]) +def test_retries_failed_notebook_execution(tmp_path: Path, failure_count: int) -> None: repo_root = tmp_path / "repo" script = repo_root / "docs" / "scripts" / SCRIPT_PATH.name script.parent.mkdir(parents=True) @@ -45,6 +46,14 @@ def test_retries_failed_notebook_execution(tmp_path: Path) -> None: jupytext = tmp_path / "jupytext" jupytext.write_text( """#!/usr/bin/env bash +src="${!#}" +echo '{"cells": []}' > "${src%.py}.ipynb" +""" + ) + jupytext.chmod(0o755) + nbclient = tmp_path / "jupyter-execute" + nbclient.write_text( + """#!/usr/bin/env bash set -euo pipefail attempt=0 if [ -f "$ATTEMPTS_FILE" ]; then @@ -52,26 +61,29 @@ def test_retries_failed_notebook_execution(tmp_path: Path) -> None: fi attempt=$((attempt + 1)) printf '%s' "$attempt" > "$ATTEMPTS_FILE" -if [ "$attempt" -lt 2 ]; then +if [ "$attempt" -le "$FAILURE_COUNT" ]; then exit 1 fi -src="${!#}" -printf '%s\n' '{"cells": []}' > "${src%.py}.ipynb" """ ) - jupytext.chmod(0o755) + nbclient.chmod(0o755) env = { **os.environ, "ATTEMPTS_FILE": str(attempts_file), + "FAILURE_COUNT": str(failure_count), "DOCS_JUPYTEXT": str(jupytext), + "DOCS_NBCLIENT": str(nbclient), "NOTEBOOK_CACHE_CONTEXT": "test-context", "NOTEBOOK_EXECUTION_ATTEMPTS": "2", "NOTEBOOK_RETRY_DELAY_SECONDS": "0", } - result = subprocess.run([script], check=True, capture_output=True, text=True, env=env) + result = subprocess.run([script], check=False, capture_output=True, text=True, env=env) + succeeded = failure_count == 1 + assert result.returncode == (0 if succeeded else 1) assert attempts_file.read_text() == "2" assert "Attempt 1 failed; retrying" in result.stdout - assert (repo_root / "docs" / "notebooks" / "example.ipynb").exists() - assert (repo_root / ".notebook-cache" / "example.sha256").exists() + assert (repo_root / "docs" / "notebooks" / "example.ipynb").exists() == succeeded + assert (repo_root / ".notebook-cache" / "example.sha256").exists() == succeeded + assert not (source_dir / "example.ipynb").exists() diff --git a/packages/data-designer/tests/docs/test_prepare_nbconvert_css.py b/packages/data-designer/tests/docs/test_prepare_nbconvert_css.py deleted file mode 100644 index 035e1df5c..000000000 --- a/packages/data-designer/tests/docs/test_prepare_nbconvert_css.py +++ /dev/null @@ -1,123 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -from __future__ import annotations - -import csv -import hashlib -import importlib.util -import io -from importlib.metadata import PathDistribution -from pathlib import Path -from types import ModuleType -from zipfile import ZipFile - -import pytest - -SCRIPT_PATH = Path(__file__).resolve().parents[4] / "docs" / "scripts" / "prepare_nbconvert_css.py" - - -@pytest.fixture -def css_environment(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> tuple[ModuleType, Path, Path]: - spec = importlib.util.spec_from_file_location("prepare_nbconvert_css", SCRIPT_PATH) - assert spec is not None and spec.loader is not None - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - prefix = tmp_path / "venv" - info = prefix / "lib" / "site-packages" / "nbconvert-7.17.2.dist-info" - info.mkdir(parents=True) - (info / "METADATA").write_text("Name: nbconvert\nVersion: 7.17.2\n") - record = info / "RECORD" - record.write_text("nbconvert-7.17.2.dist-info/RECORD,,\n") - installed = PathDistribution(info) - monkeypatch.setattr(module, "distribution", lambda name: installed) - monkeypatch.setattr(module.sys, "prefix", str(prefix)) - - stylesheets = {name: f"/* {name} */".encode() for name in module.CSS_SHA256} - payload = io.BytesIO() - with ZipFile(payload, "w") as wheel: - for name, content in stylesheets.items(): - wheel.writestr(f"{module.WHEEL_TEMPLATES}/{name}", content) - wheel.writestr("nbconvert/__init__.py", "raise RuntimeError('must not install wheel code')") - wheel_bytes = payload.getvalue() - monkeypatch.setattr(module, "WHEEL_SHA256", hashlib.sha256(wheel_bytes).hexdigest()) - monkeypatch.setattr( - module, "CSS_SHA256", {name: hashlib.sha256(data).hexdigest() for name, data in stylesheets.items()} - ) - monkeypatch.setattr(module, "urlopen", lambda *args, **kwargs: io.BytesIO(wheel_bytes)) - return module, prefix, record - - -def test_restores_only_css_and_tracks_assets_for_uninstall(css_environment: tuple[ModuleType, Path, Path]) -> None: - module, prefix, record = css_environment - - module.prepare_nbconvert_css() - - templates = prefix / "share" / "jupyter" / "nbconvert" / "templates" - assert {str(path.relative_to(templates)) for path in templates.rglob("*.css")} == module.CSS_SHA256.keys() - assert not (record.parent.parent / "nbconvert").exists() - with record.open(newline="") as stream: - records = list(csv.reader(stream)) - assert len(records) == 5 - for name, digest, size in records[1:]: - restored = record.parent.parent / name - assert restored.is_file() - assert digest.startswith("sha256=") - assert len(restored.read_bytes()) == int(size) - - -def test_complete_installation_needs_no_network( - css_environment: tuple[ModuleType, Path, Path], monkeypatch: pytest.MonkeyPatch -) -> None: - module, _, record = css_environment - module.prepare_nbconvert_css() - previous = record.read_bytes() - - def offline(*args: object, **kwargs: object) -> None: - raise AssertionError("must not download existing assets") - - monkeypatch.setattr(module, "urlopen", offline) - module.prepare_nbconvert_css() - - assert record.read_bytes() == previous - - -@pytest.mark.parametrize("corruption", ["wheel", "stylesheet", "missing-member"]) -def test_rejects_invalid_assets_before_writing( - css_environment: tuple[ModuleType, Path, Path], monkeypatch: pytest.MonkeyPatch, corruption: str -) -> None: - module, prefix, record = css_environment - previous = record.read_bytes() - if corruption == "wheel": - monkeypatch.setattr(module, "WHEEL_SHA256", "0" * 64) - elif corruption == "stylesheet": - monkeypatch.setitem(module.CSS_SHA256, "classic/static/style.css", "0" * 64) - else: - monkeypatch.setitem(module.CSS_SHA256, "classic/static/missing.css", "0" * 64) - - with pytest.raises((ValueError, KeyError), match="checksum mismatch|missing.css"): - module.prepare_nbconvert_css() - - assert not (prefix / "share").exists() - assert record.read_bytes() == previous - - -@pytest.mark.parametrize("corruption", ["missing", "modified"]) -def test_repairs_incomplete_installation_without_duplicate_records( - css_environment: tuple[ModuleType, Path, Path], corruption: str -) -> None: - module, prefix, record = css_environment - module.prepare_nbconvert_css() - stylesheet = prefix / "share" / "jupyter" / "nbconvert" / "templates" / "classic" / "static" / "style.css" - expected = stylesheet.read_bytes() - if corruption == "missing": - stylesheet.unlink() - else: - stylesheet.write_bytes(b"modified") - - module.prepare_nbconvert_css() - - assert stylesheet.read_bytes() == expected - with record.open(newline="") as stream: - assert len(list(csv.reader(stream))) == 5 diff --git a/pyproject.toml b/pyproject.toml index aa5f3a3db..963945fae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,6 @@ required-version = ">=0.13.4" # Minimum versions for transitive dependencies with known security vulnerabilities. constraint-dependencies = [ "anyio>=4.14.2", - "bleach>=6.4.0", "cryptography>=50.0.0", "filelock>=4.2.1", "idna>=3.15", @@ -65,14 +64,8 @@ notebooks = [ "aiohttp>=3.14.4,<4", # Keep aligned with the security constraint above "datasets>=5.0.1,<6", # 5.0.1 fixes CVE-2026-66007 "ipykernel>=6.29.0,<7", - "jupyter>=1.0.0,<2", - "jupyter-server>=2.21.0,<3", # Keep aligned with the security constraint above - "jupyterlab>=4.6.4,<5", # 4.6.4 fixes CVE-2026-102831 - "mistune>=3.3.3,<4", # 3.3.3 fixes security advisory pulled in by nbconvert - "nbconvert>=7.17.2,<8", # 7.17.2 fixes export filename path traversal - "notebook>=7.6.3,<8", # 7.6.3 fixes CVE-2026-102831 + "nbclient>=0.10.4,<1", "setuptools>=84.1.0,<85", # 84.1.0 fixes the namespace_packages.txt path escape - "soupsieve>=2.9.1,<3", # 2.9.1 fixes soupsieve security advisories pulled in by nbconvert "tornado>=6.6,<7", # 6.6 fixes redirect and HTTP parsing advisories ] recipes = [ diff --git a/uv.lock b/uv.lock index 99846d7ac..f88975e3e 100644 --- a/uv.lock +++ b/uv.lock @@ -22,7 +22,6 @@ members = [ ] constraints = [ { name = "anyio", specifier = ">=4.14.2" }, - { name = "bleach", specifier = ">=6.4.0" }, { name = "cryptography", specifier = ">=50.0.0" }, { name = "filelock", specifier = ">=4.2.1" }, { name = "idna", specifier = ">=3.15" }, @@ -266,62 +265,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, ] -[[package]] -name = "argon2-cffi" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argon2-cffi-bindings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, -] - -[[package]] -name = "argon2-cffi-bindings" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, -] - -[[package]] -name = "arrow" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, -] - [[package]] name = "asciichartpy" version = "1.5.25" @@ -343,15 +286,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] -[[package]] -name = "async-lru" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8a/ca724066c32a53fa75f59e0f21aa822fdaa8a0dffa112d223634e3caabf9/async_lru-2.2.0.tar.gz", hash = "sha256:80abae2a237dbc6c60861d621619af39f0d920aea306de34cb992c879e01370c", size = 14654, upload-time = "2026-02-20T19:11:43.848Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl", hash = "sha256:e2c1cf731eba202b59c5feedaef14ffd9d02ad0037fcda64938699f2c380eafe", size = 7890, upload-time = "2026-02-20T19:11:42.273Z" }, -] - [[package]] name = "attrs" version = "25.4.0" @@ -361,45 +295,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, -] - -[[package]] -name = "bleach" -version = "6.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/3c/e12ac860709702bd5ebeb9b56a4fe334f1001246ee1b8f2b7ee28912df7d/bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452", size = 204857, upload-time = "2026-06-05T13:01:13.734Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/9d/40b6267367182187139a4000b82a3b287d84d745bccd808e75d916920e9d/bleach-6.4.0-py3-none-any.whl", hash = "sha256:4b6b6a54fff2e69a3dde9d21cc6301220bee3c3cb792187d11403fd795031081", size = 165109, upload-time = "2026-06-05T13:01:12.504Z" }, -] - -[package.optional-dependencies] -css = [ - { name = "tinycss2" }, -] - [[package]] name = "bm25s" version = "0.3.2" @@ -935,14 +830,8 @@ notebooks = [ { name = "aiohttp" }, { name = "datasets" }, { name = "ipykernel" }, - { name = "jupyter" }, - { name = "jupyter-server" }, - { name = "jupyterlab" }, - { name = "mistune" }, - { name = "nbconvert" }, - { name = "notebook" }, + { name = "nbclient" }, { name = "setuptools" }, - { name = "soupsieve" }, { name = "tornado" }, ] recipes = [ @@ -968,14 +857,8 @@ notebooks = [ { name = "aiohttp", specifier = ">=3.14.4,<4" }, { name = "datasets", specifier = ">=5.0.1,<6" }, { name = "ipykernel", specifier = ">=6.29.0,<7" }, - { name = "jupyter", specifier = ">=1.0.0,<2" }, - { name = "jupyter-server", specifier = ">=2.21.0,<3" }, - { name = "jupyterlab", specifier = ">=4.6.4,<5" }, - { name = "mistune", specifier = ">=3.3.3,<4" }, - { name = "nbconvert", specifier = ">=7.17.2,<8" }, - { name = "notebook", specifier = ">=7.6.3,<8" }, + { name = "nbclient", specifier = ">=0.10.4,<1" }, { name = "setuptools", specifier = ">=84.1.0,<85" }, - { name = "soupsieve", specifier = ">=2.9.1,<3" }, { name = "tornado", specifier = ">=6.6,<7" }, ] recipes = [ @@ -1042,15 +925,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, -] - [[package]] name = "diff-cover" version = "10.2.0" @@ -1159,15 +1033,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/1b/5e74289a1878e666bb4b65fe5805a04cafcac2aa5f519ef1ead67ef91c56/filelock-4.2.4-py3-none-any.whl", hash = "sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e", size = 162475, upload-time = "2026-10-19T01:57:49.419Z" }, ] -[[package]] -name = "fqdn" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, -] - [[package]] name = "frozenlist" version = "1.8.0" @@ -1517,35 +1382,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, ] -[[package]] -name = "ipywidgets" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "comm" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "jupyterlab-widgets" }, - { name = "traitlets" }, - { name = "widgetsnbextension" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, -] - -[[package]] -name = "isoduration" -version = "20.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "arrow" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, -] - [[package]] name = "jedi" version = "0.19.2" @@ -1579,15 +1415,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/79/91b83df91d03b9647ee759f286900957f0d113f49a606e28c8a43d39365b/json_repair-0.64.2-py3-none-any.whl", hash = "sha256:1dec7868c05abff0c52e91dc896e01f28fda6ba1c9f2a9ede18ab520dde637e1", size = 52688, upload-time = "2026-10-23T05:35:21.978Z" }, ] -[[package]] -name = "json5" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" }, -] - [[package]] name = "jsonpath-ng" version = "1.8.0" @@ -1678,15 +1505,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/e1/12b8a4e1ead50749bf2997f004b99a4b4458144bb31ac6be9d4fadd64103/jsonpath_rust_bindings-1.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c24aa6724dd46f79b3b1ef178eae6412aba9a2b51e0fbc283e04d89f11af1444", size = 808532, upload-time = "2025-11-16T19:03:57.096Z" }, ] -[[package]] -name = "jsonpointer" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, -] - [[package]] name = "jsonschema" version = "4.26.0" @@ -1702,19 +1520,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] -[package.optional-dependencies] -format-nongpl = [ - { name = "fqdn" }, - { name = "idna" }, - { name = "isoduration" }, - { name = "jsonpointer" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "rfc3987-syntax" }, - { name = "uri-template" }, - { name = "webcolors" }, -] - [[package]] name = "jsonschema-specifications" version = "2025.9.1" @@ -1727,36 +1532,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] -[[package]] -name = "jupyter" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipywidgets" }, - { name = "jupyter-console" }, - { name = "jupyterlab" }, - { name = "nbconvert" }, - { name = "notebook" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, -] - -[[package]] -name = "jupyter-builder" -version = "1.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/3e/56f593e6a664cd14d441724654ce8243f3cac7d3e45867cf084749c8bc75/jupyter_builder-1.2.3.tar.gz", hash = "sha256:01aba6794eb9b19e0e29ae21137ca60ba4135c70347d4b9f664a586822b8c809", size = 1024218, upload-time = "2026-09-04T18:54:09.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/aa/be79e87c50698673f196d0633fc1664607da2289f9688d1de7a1c9db9e71/jupyter_builder-1.2.3-py3-none-any.whl", hash = "sha256:c5ea5a7190c2a7b082494abade98eece1b2b5bd5dbd7d610606cbcd10a1d08b3", size = 947541, upload-time = "2026-09-04T18:54:07.644Z" }, -] - [[package]] name = "jupyter-client" version = "8.8.0" @@ -1773,26 +1548,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, ] -[[package]] -name = "jupyter-console" -version = "6.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "pyzmq" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, -] - [[package]] name = "jupyter-core" version = "5.9.1" @@ -1806,141 +1561,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] -[[package]] -name = "jupyter-events" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema", extra = ["format-nongpl"] }, - { name = "packaging" }, - { name = "python-json-logger" }, - { name = "pyyaml" }, - { name = "referencing" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, -] - -[[package]] -name = "jupyter-lsp" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5a/9066c9f8e94ee517133cd98dba393459a16cd48bba71a82f16a65415206c/jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245", size = 54823, upload-time = "2025-08-27T17:47:34.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f", size = 76687, upload-time = "2025-08-27T17:47:33.15Z" }, -] - -[[package]] -name = "jupyter-server" -version = "2.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "argon2-cffi" }, - { name = "jinja2" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "jupyter-events" }, - { name = "jupyter-server-terminals" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "overrides", marker = "python_full_version < '3.12'" }, - { name = "packaging" }, - { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "pyzmq" }, - { name = "send2trash" }, - { name = "terminado" }, - { name = "tornado" }, - { name = "traitlets" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/3d/3c9f8bce5d5448107bd285cd80185e76c9c871d076b580dfd90997e52904/jupyter_server-2.21.1.tar.gz", hash = "sha256:a8960aa29263f6041283e97d4756b099fb49b767baab6371891ecb1bd40a63df", size = 761651, upload-time = "2026-09-15T16:00:07.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/39/91bc08650cc8e3efeb7a83c25e20ebddcc9b70d28b4eb6f51a4fb55da7a4/jupyter_server-2.21.1-py3-none-any.whl", hash = "sha256:2a6467606af7dbae2e7e31640030025969e15db6a649eae334af90415dc71dca", size = 394641, upload-time = "2026-09-15T16:00:05.526Z" }, -] - -[[package]] -name = "jupyter-server-terminals" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "terminado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770, upload-time = "2026-01-14T16:53:20.213Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704, upload-time = "2026-01-14T16:53:18.738Z" }, -] - -[[package]] -name = "jupyterlab" -version = "4.6.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-lru" }, - { name = "httpx" }, - { name = "ipykernel" }, - { name = "jinja2" }, - { name = "jupyter-builder" }, - { name = "jupyter-core" }, - { name = "jupyter-lsp" }, - { name = "jupyter-server" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "packaging" }, - { name = "tornado" }, - { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/8d/995cc142f6083346b35e7d3eadc5a6717ee89ce64ea53577010ac493bc3c/jupyterlab-4.6.4.tar.gz", hash = "sha256:404f49b081819378524886c9db66dba57a5565981eff885830df1baba3a17df5", size = 28335647, upload-time = "2026-09-21T15:25:18.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/e4/072bc0d3c6d45d414062a06af7ee81c02a2c12d769c358ec9919f9995ffd/jupyterlab-4.6.4-py3-none-any.whl", hash = "sha256:15b13f991d3985129c797eb84d9949eeb8b6615e14b444868e642411f2c418b2", size = 17171596, upload-time = "2026-09-21T15:25:14.123Z" }, -] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, -] - -[[package]] -name = "jupyterlab-server" -version = "2.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "jinja2" }, - { name = "json5" }, - { name = "jsonschema" }, - { name = "jupyter-server" }, - { name = "packaging" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, -] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.16" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, -] - [[package]] name = "jupytext" version = "1.19.1" @@ -1957,15 +1577,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl", hash = "sha256:d8975035155d034bdfde5c0c37891425314b7ea8d3a6c4b5d18c294348714cd9", size = 170478, upload-time = "2026-01-25T21:35:11.17Z" }, ] -[[package]] -name = "lark" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, -] - [[package]] name = "lxml" version = "6.1.3" @@ -2251,15 +1862,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] -[[package]] -name = "mistune" -version = "3.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/92/328a294a6de83bacb95bed01f04e0eaff4e3616ee359fc821a5dfc539b02/mistune-3.3.4.tar.gz", hash = "sha256:58b5c96d6fcb61190dfe5fae498d2b2065f99cf61e9649418fd54cf1ada86dfe", size = 121426, upload-time = "2026-07-22T05:22:30.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/e4/288365afae98953bc01de09f686f40d8ee84578135aa7767d5d4e60b5278/mistune-3.3.4-py3-none-any.whl", hash = "sha256:ee015381e955e370962968befe1d729ab60fafb6a715ac6751763fbce38c8d4a", size = 66862, upload-time = "2026-07-22T05:22:29.419Z" }, -] - [[package]] name = "multidict" version = "6.9.1" @@ -2449,31 +2051,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, ] -[[package]] -name = "nbconvert" -version = "7.17.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "bleach", extra = ["css"] }, - { name = "defusedxml" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyterlab-pygments" }, - { name = "markupsafe" }, - { name = "mistune" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "pandocfilters" }, - { name = "pygments" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/6a/252c7620305a9661205aed7a2a4b3592f5207bfca56ad11b7dea2c5ec2f6/nbconvert-7.17.2.tar.gz", hash = "sha256:344a218e8595117ce37396ed025d994f146d076226ebac59255a2fa085ab24d8", size = 865989, upload-time = "2026-10-12T09:11:19.583Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/fd/a8d457af68848a8e5c12dd1d84ff35c415db471af78280e0703db4efbb12/nbconvert-7.17.2-py3-none-any.whl", hash = "sha256:6a143c11c00bf831604da4b1be4347d36ea52367057eb920135d69bc2862fed2", size = 262094, upload-time = "2026-10-12T09:11:17.215Z" }, -] - [[package]] name = "nbformat" version = "5.10.4" @@ -2516,35 +2093,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] -[[package]] -name = "notebook" -version = "7.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-builder" }, - { name = "jupyter-server" }, - { name = "jupyterlab" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/ac/aedb759dc683dcd129f6c75271bab5eb23020b0f7969163c726343718e1e/notebook-7.6.3.tar.gz", hash = "sha256:e2c08e469c0ae20bb0b3214f0ab77e79653317a2f8e5b34c10361c66874a5b50", size = 5501618, upload-time = "2026-09-21T18:07:24.261Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/f7/907b98438cf00bdc4e296570058f29a347214bb42338a48e3b7eae58ae2a/notebook-7.6.3-py3-none-any.whl", hash = "sha256:ad7e0eb765fba836cd4a2ab0c7a3a26cde1d91665fbf6f533b6ae7b2de6d88d2", size = 5548452, upload-time = "2026-09-21T18:07:21.563Z" }, -] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, -] - [[package]] name = "numpy" version = "2.4.3" @@ -2677,15 +2225,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/14/67f8aa798857f8cf686f515bf93d9bb877ce952ddc8efae0fa25b45ce0d6/opentelemetry_semantic_conventions-0.66b1-py3-none-any.whl", hash = "sha256:d4cddeb4315490b35213f55e2bdc9ac54bb1e4d318927475bed62b35545e581b", size = 206279, upload-time = "2026-10-06T17:32:56.103Z" }, ] -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - [[package]] name = "packaging" version = "26.3" @@ -2749,15 +2288,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/cf/6a51b2c38980e04c279fd2fa908a1b0982064e860444acfca4ec2e2c8359/pandas-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c", size = 9509776, upload-time = "2026-07-22T22:19:26.694Z" }, ] -[[package]] -name = "pandocfilters" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, -] - [[package]] name = "parso" version = "0.8.6" @@ -3442,26 +2972,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, ] -[[package]] -name = "pywinpty" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309, upload-time = "2026-02-04T21:51:09.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/c3/3e75075c7f71735f22b66fab0481f2c98e3a4d58cba55cb50ba29114bcf6/pywinpty-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:dff25a9a6435f527d7c65608a7e62783fc12076e7d44487a4911ee91be5a8ac8", size = 2114430, upload-time = "2026-02-04T21:54:19.485Z" }, - { url = "https://files.pythonhosted.org/packages/8d/1e/8a54166a8c5e4f5cb516514bdf4090be4d51a71e8d9f6d98c0aa00fe45d4/pywinpty-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:fbc1e230e5b193eef4431cba3f39996a288f9958f9c9f092c8a961d930ee8f68", size = 236191, upload-time = "2026-02-04T21:50:36.239Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d4/aeb5e1784d2c5bff6e189138a9ca91a090117459cea0c30378e1f2db3d54/pywinpty-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c9081df0e49ffa86d15db4a6ba61530630e48707f987df42c9d3313537e81fc0", size = 2113098, upload-time = "2026-02-04T21:54:37.711Z" }, - { url = "https://files.pythonhosted.org/packages/b9/53/7278223c493ccfe4883239cf06c823c56460a8010e0fc778eef67858dc14/pywinpty-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:15e79d870e18b678fb8a5a6105fd38496b55697c66e6fc0378236026bc4d59e9", size = 234901, upload-time = "2026-02-04T21:53:31.35Z" }, - { url = "https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab", size = 2112686, upload-time = "2026-02-04T21:52:03.035Z" }, - { url = "https://files.pythonhosted.org/packages/fd/50/724ed5c38c504d4e58a88a072776a1e880d970789deaeb2b9f7bd9a5141a/pywinpty-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:fe1f7911805127c94cf51f89ab14096c6f91ffdcacf993d2da6082b2142a2523", size = 234591, upload-time = "2026-02-04T21:52:29.821Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ad/90a110538696b12b39fd8758a06d70ded899308198ad2305ac68e361126e/pywinpty-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:3f07a6cf1c1d470d284e614733c3d0f726d2c85e78508ea10a403140c3c0c18a", size = 2112360, upload-time = "2026-02-04T21:55:33.397Z" }, - { url = "https://files.pythonhosted.org/packages/44/0f/7ffa221757a220402bc79fda44044c3f2cc57338d878ab7d622add6f4581/pywinpty-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:15c7c0b6f8e9d87aabbaff76468dabf6e6121332c40fc1d83548d02a9d6a3759", size = 233107, upload-time = "2026-02-04T21:51:45.455Z" }, - { url = "https://files.pythonhosted.org/packages/28/88/2ff917caff61e55f38bcdb27de06ee30597881b2cae44fbba7627be015c4/pywinpty-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:d4b6b7b0fe0cdcd02e956bd57cfe9f4e5a06514eecf3b5ae174da4f951b58be9", size = 2113282, upload-time = "2026-02-04T21:52:08.188Z" }, - { url = "https://files.pythonhosted.org/packages/63/32/40a775343ace542cc43ece3f1d1fce454021521ecac41c4c4573081c2336/pywinpty-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:34789d685fc0d547ce0c8a65e5a70e56f77d732fa6e03c8f74fefb8cbb252019", size = 234207, upload-time = "2026-02-04T21:51:58.687Z" }, - { url = "https://files.pythonhosted.org/packages/8d/54/5d5e52f4cb75028104ca6faf36c10f9692389b1986d34471663b4ebebd6d/pywinpty-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c37e224a47a971d1a6e08649a1714dac4f63c11920780977829ed5c8cadead1", size = 2112910, upload-time = "2026-02-04T21:52:30.976Z" }, - { url = "https://files.pythonhosted.org/packages/0a/44/dcd184824e21d4620b06c7db9fbb15c3ad0a0f1fa2e6de79969fb82647ec/pywinpty-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c4e9c3dff7d86ba81937438d5819f19f385a39d8f592d4e8af67148ceb4f6ab5", size = 233425, upload-time = "2026-02-04T21:51:56.754Z" }, -] - [[package]] name = "pyyaml" version = "6.0.3" @@ -3708,39 +3218,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, -] - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, -] - -[[package]] -name = "rfc3987-syntax" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lark" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, -] - [[package]] name = "rich" version = "14.3.3" @@ -3958,15 +3435,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, ] -[[package]] -name = "send2trash" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255, upload-time = "2026-01-14T06:27:36.056Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610, upload-time = "2026-01-14T06:27:35.218Z" }, -] - [[package]] name = "setuptools" version = "84.2.1" @@ -3994,15 +3462,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "soupsieve" -version = "2.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/99/a6ca3beb3ccacb41fb3321d8a60e5566f9e6467601ef8eba6a17e1b89778/soupsieve-2.9.2.tar.gz", hash = "sha256:4a55d8cf158a9c2e587fa4922f1bbb91d68ac829e2d6f25403a85747c71daf74", size = 122445, upload-time = "2026-08-07T00:57:24.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl", hash = "sha256:8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", size = 37370, upload-time = "2026-08-07T00:57:23.524Z" }, -] - [[package]] name = "sqlfluff" version = "4.2.1" @@ -4074,20 +3533,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/be/5d2d47b1fb58943194fb59dcf222f7c4e35122ec0ffe8c36e18b5d728f0b/tblib-3.2.2-py3-none-any.whl", hash = "sha256:26bdccf339bcce6a88b2b5432c988b266ebbe63a4e593f6b578b1d2e723d2b76", size = 12893, upload-time = "2025-11-12T12:21:14.407Z" }, ] -[[package]] -name = "terminado" -version = "0.18.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, -] - [[package]] name = "tiktoken" version = "0.12.0" @@ -4142,18 +3587,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, ] -[[package]] -name = "tinycss2" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/ae/2ca4913e5c0f09781d75482874c3a95db9105462a92ddd303c7d285d3df2/tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957", size = 88195, upload-time = "2025-11-23T10:29:10.082Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404, upload-time = "2025-11-23T10:29:08.676Z" }, -] - [[package]] name = "tomli" version = "2.4.0" @@ -4324,15 +3757,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] -[[package]] -name = "uri-template" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, -] - [[package]] name = "urllib3" version = "2.8.0" @@ -4380,42 +3804,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, ] -[[package]] -name = "webcolors" -version = "25.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, -] - -[[package]] -name = "webencodings" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/a0/8fd707bcb776a7be556bad06a2ea5fb9bd519df78ef8e26f70ccf0f38bff/webencodings-0.6.1.tar.gz", hash = "sha256:565f9ad031c702dae404e27a099e3e09186a3ab1b9520f06d215502b651fd910", size = 15001, upload-time = "2026-08-15T14:22:57.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/c6/040cbc72480d789a5f40d63fb484d3106554c4dfa2d2b70ad5022057750f/webencodings-0.6.1-py3-none-any.whl", hash = "sha256:7fab6269c8bf237c657876b52058ccb182e861518d1c695c1a9aaa8c1c105d5b", size = 8745, upload-time = "2026-08-15T14:22:56.31Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, -] - [[package]] name = "xxhash" version = "3.6.0" From 6910b23f08d461d21834da1ef5a772112381ace4 Mon Sep 17 00:00:00 2001 From: Andre Manoel Date: Fri, 30 Oct 2026 17:25:39 -0300 Subject: [PATCH 2/3] fix: preserve notebook and graph tooling behavior Keep existing widget support for notebook progress output, preserve saved graphs on partial export failures, and hold Graphify at its supported API. Retain compatibility with older notebook build refs and tool overrides. Signed-off-by: Andre Manoel --- .agents/skills/datadesigner-docs/SKILL.md | 2 +- .agents/tests/test_structural_impact.py | 10 +++++- .agents/tools/structural_impact.py | 8 ++++- .github/dependabot.yml | 3 ++ .github/workflows/build-notebooks.yml | 2 +- Makefile | 2 +- fern/components/NotebookViewer.tsx | 2 +- pyproject.toml | 1 + uv.lock | 37 +++++++++++++++++++++++ 9 files changed, 61 insertions(+), 6 deletions(-) diff --git a/.agents/skills/datadesigner-docs/SKILL.md b/.agents/skills/datadesigner-docs/SKILL.md index 139c7dfb1..7eb2365b2 100644 --- a/.agents/skills/datadesigner-docs/SKILL.md +++ b/.agents/skills/datadesigner-docs/SKILL.md @@ -356,7 +356,7 @@ Notebook tutorials are JS-driven via `` rendering converted `.ip ``` docs/notebook_source/*.py (jupytext format — canonical source, edit here) - │ make convert-execute-notebooks # jupytext --execute (needs NVIDIA_API_KEY) + │ make convert-execute-notebooks # jupytext → jupyter-execute (needs NVIDIA_API_KEY) ▼ docs/notebooks/*.ipynb (executed; outputs captured) │ diff --git a/.agents/tests/test_structural_impact.py b/.agents/tests/test_structural_impact.py index eaaca6c86..bc5d47522 100644 --- a/.agents/tests/test_structural_impact.py +++ b/.agents/tests/test_structural_impact.py @@ -19,10 +19,15 @@ EXPORT_FAILURE = """ import runpy import sys +from pathlib import Path from unittest.mock import patch +def fail_export(graph, communities, output_path): + Path(output_path).write_text('{') + raise OSError("graph export failed") + sys.argv = sys.argv[1:] -with patch("graphify.export.to_json", side_effect=OSError("graph export failed")): +with patch("graphify.export.to_json", side_effect=fail_export): runpy.run_path(sys.argv[0], run_name="__main__") """ @@ -139,6 +144,8 @@ def test_rejected_export_preserves_baselines( run_tool("--full") baselines = repo_root / "graphify-out/baselines.json" previous = baselines.read_bytes() + graph_path = baselines.with_name("graph.json") + previous_graph = graph_path.read_bytes() (repo_root / INTERFACE).unlink() result = run_tool("--full", reject_export=True, check=False) @@ -146,6 +153,7 @@ def test_rejected_export_preserves_baselines( assert result.returncode != 0 assert "graph export failed" in result.stderr assert baselines.read_bytes() == previous + assert graph_path.read_bytes() == previous_graph def test_empty_scan_preserves_baselines( diff --git a/.agents/tools/structural_impact.py b/.agents/tools/structural_impact.py index a60bf32cf..16af2bce7 100644 --- a/.agents/tools/structural_impact.py +++ b/.agents/tools/structural_impact.py @@ -376,7 +376,13 @@ def _full_mode(previous_graph_path: str | None = None) -> str: out_dir = _REPO_ROOT / "graphify-out" out_dir.mkdir(exist_ok=True) # This is a complete source snapshot, so deletions must replace the old graph. - to_json(G, communities, str(out_dir / "graph.json")) + graph_path = out_dir / "graph.json" + export_path = graph_path.with_suffix(".tmp") + try: + to_json(G, communities, str(export_path)) + export_path.replace(graph_path) + finally: + export_path.unlink(missing_ok=True) (out_dir / "baselines.json").write_text( json.dumps( { diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 446aa3690..93d4435e2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,6 +14,9 @@ updates: directory: /.agents schedule: interval: weekly + # The structural-analysis adapter targets the Graphify 0.4 API. + ignore: + - dependency-name: graphifyy groups: graphify: patterns: diff --git a/.github/workflows/build-notebooks.yml b/.github/workflows/build-notebooks.yml index 7c2d96578..80b8af897 100644 --- a/.github/workflows/build-notebooks.yml +++ b/.github/workflows/build-notebooks.yml @@ -50,7 +50,7 @@ jobs: - name: Set up Python run: uv python install 3.11 - name: Install notebook dependencies - run: make install-docs-deps DOCS_PYTHON_VERSION=3.11 + run: make install-docs-deps DOCS_PYTHON_VERSION=3.11 NOTEBOOK_CSS=0 - name: Restore notebook cache if: inputs.use_cache id: cache diff --git a/Makefile b/Makefile index b07fe207f..b09dc10ce 100644 --- a/Makefile +++ b/Makefile @@ -509,7 +509,7 @@ update-license-headers: DOCS_PYTHON_VERSION ?= 3.13 DOCS_PYTHON ?= .venv/bin/python DOCS_JUPYTEXT ?= .venv/bin/jupytext -DOCS_NBCLIENT ?= $(dir $(DOCS_JUPYTEXT))jupyter-execute +DOCS_NBCLIENT ?= $(if $(findstring /,$(DOCS_JUPYTEXT)),$(dir $(DOCS_JUPYTEXT)),)jupyter-execute FERN_VERSION ?= $(shell jq -r .version fern/fern.config.json) FERN ?= npx -y fern-api@$(FERN_VERSION) FERN_ORG ?= $(shell jq -r .organization fern/fern.config.json) diff --git a/fern/components/NotebookViewer.tsx b/fern/components/NotebookViewer.tsx index 25698e95e..122959dc5 100644 --- a/fern/components/NotebookViewer.tsx +++ b/fern/components/NotebookViewer.tsx @@ -26,7 +26,7 @@ import type { ReactNode } from "react"; * The trust boundary is the converter pipeline: * * docs/notebook_source/*.py (jupytext source — code-reviewed at PR time) - * └─> jupytext --execute (runs in CI/maintainer shell with NVIDIA_API_KEY) + * └─> jupytext + jupyter-execute (runs in CI/maintainer shell with NVIDIA_API_KEY) * └─> *.ipynb (real outputs captured) * └─> fern/scripts/ipynb-to-fern-json.py * └─> fern/components/notebooks/*.{json,ts} (committed) diff --git a/pyproject.toml b/pyproject.toml index 963945fae..8932a5dc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ notebooks = [ "aiohttp>=3.14.4,<4", # Keep aligned with the security constraint above "datasets>=5.0.1,<6", # 5.0.1 fixes CVE-2026-66007 "ipykernel>=6.29.0,<7", + "ipywidgets>=8.1.8,<9", "nbclient>=0.10.4,<1", "setuptools>=84.1.0,<85", # 84.1.0 fixes the namespace_packages.txt path escape "tornado>=6.6,<7", # 6.6 fixes redirect and HTTP parsing advisories diff --git a/uv.lock b/uv.lock index f88975e3e..6aae489f9 100644 --- a/uv.lock +++ b/uv.lock @@ -830,6 +830,7 @@ notebooks = [ { name = "aiohttp" }, { name = "datasets" }, { name = "ipykernel" }, + { name = "ipywidgets" }, { name = "nbclient" }, { name = "setuptools" }, { name = "tornado" }, @@ -857,6 +858,7 @@ notebooks = [ { name = "aiohttp", specifier = ">=3.14.4,<4" }, { name = "datasets", specifier = ">=5.0.1,<6" }, { name = "ipykernel", specifier = ">=6.29.0,<7" }, + { name = "ipywidgets", specifier = ">=8.1.8,<9" }, { name = "nbclient", specifier = ">=0.10.4,<1" }, { name = "setuptools", specifier = ">=84.1.0,<85" }, { name = "tornado", specifier = ">=6.6,<7" }, @@ -1382,6 +1384,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, ] +[[package]] +name = "ipywidgets" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, +] + [[package]] name = "jedi" version = "0.19.2" @@ -1561,6 +1580,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] +[[package]] +name = "jupyterlab-widgets" +version = "3.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, +] + [[package]] name = "jupytext" version = "1.19.1" @@ -3804,6 +3832,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, ] +[[package]] +name = "widgetsnbextension" +version = "4.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, +] + [[package]] name = "xxhash" version = "3.6.0" From a08376535478109a1ca56b68a22cce84b138b539 Mon Sep 17 00:00:00 2001 From: Andre Manoel Date: Fri, 30 Oct 2026 17:29:40 -0300 Subject: [PATCH 3/3] ci: preserve Graphify security updates Limit the Graphify hold to routine version updates while the structural adapter targets the 0.4 API. Signed-off-by: Andre Manoel --- .github/dependabot.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 93d4435e2..9739ff82b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,9 +14,13 @@ updates: directory: /.agents schedule: interval: weekly - # The structural-analysis adapter targets the Graphify 0.4 API. + # Hold routine upgrades for the 0.4 adapter; keep security updates enabled. ignore: - dependency-name: graphifyy + update-types: + - version-update:semver-major + - version-update:semver-minor + - version-update:semver-patch groups: graphify: patterns: