diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f48ffa6..7649a89 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -72,6 +72,25 @@ jobs: tags: ${{ steps.s3fs-meta.outputs.tags }} labels: ${{ steps.s3fs-meta.outputs.labels }} + - name: Extract tensorrt metadata + id: tensorrt-meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/tensorrt + tags: | + type=ref,event=tag + type=raw,value=latest,enable=${{ github.ref_type == 'tag' || github.ref == 'refs/heads/main' }} + type=sha,prefix= + + - name: Build and push tensorrt + uses: docker/build-push-action@v5 + with: + context: examples/tensorrt + platforms: linux/amd64 + push: true + tags: ${{ steps.tensorrt-meta.outputs.tags }} + labels: ${{ steps.tensorrt-meta.outputs.labels }} + - name: Notify release result if: always() && startsWith(github.ref, 'refs/tags/v') uses: marimo-team/internal-gh-actions/release-notification@main diff --git a/examples/jax/jax_example.py b/examples/jax/jax_example.py new file mode 100644 index 0000000..fbbfb66 --- /dev/null +++ b/examples/jax/jax_example.py @@ -0,0 +1,131 @@ +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "flax>=0.10.0", +# "jax[cuda12]>=0.4.0", +# "marimo>=0.21.1", +# "matplotlib==3.10.8", +# "mofresh", +# "optax>=0.2.0", +# "polars>=1.0", +# ] +# +# [tool.marimo.runtime] +# auto_instantiate = false +# +# [tool.marimo.k8s] +# storage = "5Gi" +# +# [tool.marimo.k8s.resources] +# limits."nvidia.com/gpu" = 1 +# +# [tool.marimo.k8s.nodeSelector] +# "gpu.nvidia.com/class" = "L40" +# /// + +import marimo + +__generated_with = "0.21.1" +app = marimo.App(width="columns", auto_download=["html"]) + +with app.setup(hide_code=True): + import marimo as mo + import jax + import jax.numpy as jnp + import flax.linen as nn + import optax + import polars as pl + import numpy as np + + +@app.cell(hide_code=True) +def _(): + mo.md(r""" + marimo's multi-column layout offers a unique live preview as you work. + """) + return + + +@app.cell +def _(): + import matplotlib.pylab as plt + from mofresh import refresh_matplotlib, ImageRefreshWidget + + widget = ImageRefreshWidget(src="") + + @refresh_matplotlib + def losschart(data): + df = pl.DataFrame(data) + plt.plot(df["epoch"], df["loss_train"]) + + widget + return losschart, widget + + +@app.cell +def _(): + return + + +@app.cell(column=1, hide_code=True) +def _(): + mo.md(r""" + Example network and training with `jax` + """) + return + + +@app.cell +def _(losschart, widget): + datalogs = [] + + def train_identity_network(data, epochs=5000, learning_rate=0.025, iteration=0): + rng = jax.random.PRNGKey(42) + x = jnp.array(data, dtype=jnp.float32) + + model = IdentityNetwork() + params = model.init(rng, x) + + optimizer = optax.sgd(learning_rate) + opt_state = optimizer.init(params) + + def loss_fn(p, batch): + return jnp.mean((model.apply(p, batch) - batch) ** 2) + + for epoch in range(epochs): + loss, grads = jax.value_and_grad(loss_fn)(params, x) + updates, opt_state = optimizer.update(grads, opt_state) + params = optax.apply_updates(params, updates) + + if (epoch + 1) % 25 == 0: + test_data = jnp.array(np.random.rand(n, k), dtype=jnp.float32) + test_loss = loss_fn(params, test_data) + datalogs.append({ + "epoch": epoch + iteration * epochs, + "loss_train": float(loss), + "loss_test": float(test_loss), + }) + widget.src = losschart(datalogs) + + return params + + for i in range(1): + n = 10000 + k = 10 + random_data = np.random.rand(n, k) + trained_params = train_identity_network(random_data, epochs=5000, iteration=i) + return + + +@app.class_definition +class IdentityNetwork(nn.Module): + @nn.compact + def __call__(self, x): + features = x.shape[-1] + x = nn.relu(nn.Dense(features)(x)) + x = nn.relu(nn.Dense(features)(x)) + return x + + +if __name__ == "__main__": + app.run() diff --git a/examples/tensorrt/Dockerfile b/examples/tensorrt/Dockerfile new file mode 100644 index 0000000..0dec7f0 --- /dev/null +++ b/examples/tensorrt/Dockerfile @@ -0,0 +1,21 @@ +FROM nvcr.io/nvidia/tritonserver:25.01-trtllm-python-py3 + +# The marimo operator requires: +# 1. uv — setup-venv init container runs: uv venv /opt/venv +# 2. marimo binary in PATH — main container command is: marimo run/edit ... +RUN pip install uv "marimo>=0.21.1" + +# The operator launches the container without running the NVIDIA entrypoint, so +# env vars it would normally set must be baked in here instead. + +# uv is pip-installed to /usr/local/bin; operator hardcodes /usr/bin +ENV UV="/usr/local/bin/uv" + +# TensorRT-LLM is in dist-packages (Ubuntu pip convention); operator venv lands at /opt/venv +ENV PYTHONPATH="/usr/local/lib/python3.12/dist-packages:/opt/venv/lib/python3.12/site-packages" + +# TensorRT/cuBLAS .so files (entrypoint normally adds these to LD_LIBRARY_PATH) +ENV LD_LIBRARY_PATH="/usr/local/tensorrt/targets/x86_64-linux-gnu/lib:/usr/local/cuda/lib64:/usr/local/cuda/compat/lib:/usr/local/lib/python3.12/dist-packages/tensorrt_llm/libs:/usr/local/lib/python3.12/dist-packages/torch/lib:/usr/local/lib/python3.12/dist-packages/nvidia/cublas/lib:/usr/local/lib/python3.12/dist-packages/nvidia/cuda_runtime/lib" + +# Reduce TRT-LLM log noise by default; notebooks can override if needed. +ENV TRTLLM_LOG_LEVEL="WARNING" diff --git a/examples/tensorrt/README.md b/examples/tensorrt/README.md new file mode 100644 index 0000000..a037333 --- /dev/null +++ b/examples/tensorrt/README.md @@ -0,0 +1,62 @@ +# TensorRT-LLM Example + +Interactive [TensorRT-LLM](https://github.com/NVIDIA/TensorRT-LLM) inference notebook running on the marimo operator. Includes a model picker with several open-weight models and a reactive prompt selector. + +Requires a GPU node with ≥ 24 GB VRAM. The included models range from ~3 GB (TinyLlama 1.1B) up to ~16 GB (Mistral 7B / Minitron 8B). + +## Files + +| File | Purpose | +|---|---| +| `tensorrt.py` | marimo notebook — deploy with the `kubectl-marimo` plugin | +| `tensorrt.yaml` | Plain `MarimoNotebook` manifest — deploy with `kubectl apply` | +| `Dockerfile` | Thin layer over the NVIDIA NGC TRT-LLM container | + +## Deploy with the plugin + +```bash +uv tool install kubectl-marimo + +kubectl marimo edit tensorrt.py -n +``` + +## Deploy with kubectl + +```bash +# Edit nodeSelector if needed, then apply +kubectl apply -f tensorrt.yaml -n + +# Get the access token +kubectl logs -n tensorrt -c marimo | grep access_token + +# Port-forward to access locally +kubectl port-forward -n svc/tensorrt 2718:2718 +``` + +## Build the image + +The pre-built image is at `ghcr.io/marimo-team/marimo-operator/tensorrt:latest` and is rebuilt on every push to main. To build your own: + +```bash +docker build --platform linux/amd64 -t /tensorrt:latest . +docker push /tensorrt:latest +``` + +If your registry is private, create an image pull secret: + +```bash +kubectl create secret docker-registry registry-secret \ + --docker-server= \ + --docker-username= \ + --docker-password= \ + -n + +kubectl patch serviceaccount default -n \ + -p '{"imagePullSecrets":[{"name":"registry-secret"}]}' +``` + +## Further reading + +- [TensorRT-LLM documentation](https://nvidia.github.io/TensorRT-LLM/) +- [FP8 quantization guide](https://nvidia.github.io/TensorRT-LLM/performance/performance-tuning-guide/fp8-quantization.html) +- [marimo operator](https://github.com/marimo-team/marimo-operator) diff --git a/examples/tensorrt/tensorrt.py b/examples/tensorrt/tensorrt.py new file mode 100644 index 0000000..b3a5e8d --- /dev/null +++ b/examples/tensorrt/tensorrt.py @@ -0,0 +1,107 @@ +# /// script +# dependencies = ["marimo>=0.21.1", "setuptools"] +# +# [tool.marimo.k8s] +# image = "ghcr.io/marimo-team/marimo-operator/tensorrt:latest" +# storage = "20Gi" +# +# [tool.marimo.k8s.resources] +# limits."nvidia.com/gpu" = 1 +# +# [tool.marimo.k8s.nodeSelector] +# "gpu.nvidia.com/class" = "L40" +# +# /// + +import marimo + +__generated_with = "0.21.1" +app = marimo.App(width="medium") + +with app.setup: + import gc + import io + import logging + + import marimo as mo + import torch + from tensorrt_llm import LLM, SamplingParams + + +@app.cell +def _(): + # Collect TRT-LLM / torch log lines emitted during model load so we can + # surface them in the notebook rather than losing them to pod stdout. + log_stream = io.StringIO() + logging.basicConfig(stream=log_stream, level=logging.WARNING) + + models = { + "TinyLlama 1.1B (fast)": "TinyLlama/TinyLlama-1.1B-Chat-v1.0", + "Phi-3.5-mini 3.8B": "microsoft/Phi-3.5-mini-instruct", + "Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.3", + "Llama-3.1 8B FP8 (NVIDIA)": "nvidia/Llama-3.1-8B-Instruct-FP8", + "Minitron 8B (NVIDIA)": "nvidia/Mistral-NeMo-Minitron-8B-Instruct", + } + + prompts = [ + "Hello, my name is", + "The capital of France is", + "The future of AI is", + ] + + prompt = mo.ui.dropdown( + label="Select a Prompt", + options=prompts, + value=prompts[0], + ) + + model_picker = mo.ui.dropdown( + label="Model", + options=models, + value="TinyLlama 1.1B (fast)", + ) + + mo.vstack([ + mo.md("## TensorRT-LLM Inference Example, select a model to download and run"), + model_picker, + ]) + return log_stream, model_picker, models, prompt, prompts + + +@app.cell +def _(log_stream, model_picker): + mo.stop(model_picker.value is None) + + # Free the previous model's GPU memory before loading the next one. + # Without this, two models can coexist briefly and OOM a 48 GB L40. + gc.collect() + torch.cuda.empty_cache() + + # Model can be a HuggingFace model name, a local path, or a quantized + # checkpoint such as nvidia/Llama-3.1-8B-Instruct-FP8 on HF. + llm = LLM(model=model_picker.value) + + # Show any WARNING+ log lines collected during model load. + logs = log_stream.getvalue() + mo.accordion({"Model load logs": mo.plain_text(logs) if logs else mo.md("_No warnings._")}) + return (llm,) + + +@app.cell(hide_code=True) +def _(llm, prompt): + sampling_params = SamplingParams(temperature=0.8, top_p=0.95) + + results = [] + for output in llm.generate([prompt.value], sampling_params): + results.append( + mo.md( + f"**Prompt:** {output.prompt} \n" + f"**Generated:** {output.outputs[0].text}" + ) + ) + + mo.vstack([prompt, mo.vstack(results)]) + + +if __name__ == "__main__": + app.run() diff --git a/examples/tensorrt/tensorrt.yaml b/examples/tensorrt/tensorrt.yaml new file mode 100644 index 0000000..3e0df27 --- /dev/null +++ b/examples/tensorrt/tensorrt.yaml @@ -0,0 +1,106 @@ +apiVersion: marimo.io/v1alpha1 +kind: MarimoNotebook +metadata: + name: tensorrt +spec: + image: ghcr.io/marimo-team/marimo-operator/tensorrt:latest + mode: edit + storage: + size: 20Gi + resources: + limits: + nvidia.com/gpu: "1" + # Adjust nodeSelector to match the GPU nodes in your cluster. + podOverrides: + nodeSelector: + gpu.nvidia.com/class: L40 + content: | + # /// script + # dependencies = ["marimo>=0.21.1", "setuptools"] + # /// + + import marimo + + __generated_with = "0.21.1" + app = marimo.App(width="medium") + + with app.setup: + import gc + import io + import logging + + import marimo as mo + import torch + from tensorrt_llm import LLM, SamplingParams + + + @app.cell + def _(): + log_stream = io.StringIO() + logging.basicConfig(stream=log_stream, level=logging.WARNING) + + models = { + "TinyLlama 1.1B (fast)": "TinyLlama/TinyLlama-1.1B-Chat-v1.0", + "Phi-3.5-mini 3.8B": "microsoft/Phi-3.5-mini-instruct", + "Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.3", + "Llama-3.1 8B FP8 (NVIDIA)": "nvidia/Llama-3.1-8B-Instruct-FP8", + "Minitron 8B (NVIDIA)": "nvidia/Mistral-NeMo-Minitron-8B-Instruct", + } + + prompts = [ + "Hello, my name is", + "The capital of France is", + "The future of AI is", + ] + + prompt = mo.ui.dropdown( + label="Select a Prompt", + options=prompts, + value=prompts[0], + ) + + model_picker = mo.ui.dropdown( + label="Model", + options=models, + value="TinyLlama 1.1B (fast)", + ) + + mo.vstack([ + mo.md("## TensorRT-LLM Inference Example, select a model to download and run"), + model_picker, + ]) + return log_stream, model_picker, models, prompt, prompts + + + @app.cell + def _(log_stream, model_picker): + mo.stop(model_picker.value is None) + + gc.collect() + torch.cuda.empty_cache() + + llm = LLM(model=model_picker.value) + + logs = log_stream.getvalue() + mo.accordion({"Model load logs": mo.plain_text(logs) if logs else mo.md("_No warnings._")}) + return (llm,) + + + @app.cell(hide_code=True) + def _(llm, prompt): + sampling_params = SamplingParams(temperature=0.8, top_p=0.95) + + results = [] + for output in llm.generate([prompt.value], sampling_params): + results.append( + mo.md( + f"**Prompt:** {output.prompt} \n" + f"**Generated:** {output.outputs[0].text}" + ) + ) + + mo.vstack([prompt, mo.vstack(results)]) + + + if __name__ == "__main__": + app.run() diff --git a/pkg/resources/pod.go b/pkg/resources/pod.go index 7794fa1..e79eba5 100644 --- a/pkg/resources/pod.go +++ b/pkg/resources/pod.go @@ -87,10 +87,12 @@ func BuildPod(notebook *marimov1alpha1.MarimoNotebook) *corev1.Pod { Name: "copy-content", Image: config.DefaultInitImage, Command: []string{"sh", "-c", fmt.Sprintf( - "cp /content/%s %s/%s", + "cp /content/%s %s/%s && chmod 666 %s/%s", ContentKey, NotebookDir, contentKey, + NotebookDir, + contentKey, )}, VolumeMounts: []corev1.VolumeMount{ {Name: PVCVolumeName, MountPath: NotebookDir}, diff --git a/plugin/kubectl_marimo/formats/python.py b/plugin/kubectl_marimo/formats/python.py index b7a4ff3..d1aea8b 100644 --- a/plugin/kubectl_marimo/formats/python.py +++ b/plugin/kubectl_marimo/formats/python.py @@ -1,6 +1,7 @@ """Parser for marimo Python notebooks.""" import re +import tomllib from typing import Any @@ -17,145 +18,59 @@ def parse_python(content: str) -> tuple[str, dict[str, Any] | None]: def extract_pep723_metadata(content: str) -> dict[str, Any] | None: - """Extract PEP 723 inline script metadata. + """Extract PEP 723 inline script metadata and marimo k8s config. - PEP 723 metadata is embedded in a comment block: + PEP 723 metadata is embedded in a comment block at the top of the file: - # /// script - # dependencies = ["marimo", "pandas"] - # /// + # /// script + # dependencies = ["marimo", "pandas"] + # + # [tool.marimo.k8s] + # image = "custom-image:latest" + # storage = "5Gi" + # /// - We also look for custom marimo fields: - # [tool.marimo.k8s] - # image = "custom-image:latest" - # storage = "5Gi" + The entire block is valid TOML once the leading `# ` is stripped from each line. + Marimo k8s config lives under [tool.marimo.k8s] and its subsections. """ - # Look for PEP 723 script block - # Pattern allows empty comment lines (# or #\n) as well as # followed by content pattern = r"# /// script\n((?:#(?: .*)?\n)*?)# ///" match = re.search(pattern, content) - if not match: return None - metadata = {} - block = match.group(1) - - # Parse TOML-like lines - for line in block.split("\n"): - line = line.lstrip("# ").strip() - if "=" in line: - key, _, value = line.partition("=") - key = key.strip() - value = value.strip() - # Parse simple values - if value.startswith('"') and value.endswith('"'): - value = value[1:-1] - elif value.startswith("'") and value.endswith("'"): - value = value[1:-1] - elif value.startswith("["): - # List - for now just store as string - pass - metadata[key] = value - - # Look for marimo k8s config - k8s_pattern = r"# \[tool\.marimo\.k8s\]\n((?:# .*\n)*)" - k8s_match = re.search(k8s_pattern, content) - if k8s_match: - for line in k8s_match.group(1).split("\n"): - line = line.lstrip("# ").strip() - if line.startswith("["): - # Stop at next section - break - if "=" in line: - key, _, value = line.partition("=") - key = key.strip() - value = value.strip() - value = _parse_toml_value(value) - metadata[key] = value - - # Look for marimo k8s env config - env_pattern = r"# \[tool\.marimo\.k8s\.env\]\n((?:# .*\n)*)" - env_match = re.search(env_pattern, content) - if env_match: - env = {} - for line in env_match.group(1).split("\n"): - line = line.lstrip("# ").strip() - if line.startswith("["): - # Stop at next section - break - if "=" in line: - key, _, value = line.partition("=") - key = key.strip() - value = value.strip() - value = _parse_toml_value(value) - env[key] = value - if env: - metadata["env"] = env - - # Look for marimo k8s nodeSelector config - node_selector_pattern = r"# \[tool\.marimo\.k8s\.nodeSelector\]\n((?:# .*\n)*)" - node_selector_match = re.search(node_selector_pattern, content) - if node_selector_match: - node_selector = {} - for line in node_selector_match.group(1).split("\n"): - line = line.lstrip("# ").strip() - if line.startswith("["): - # Stop at next section - break - if "=" in line: - key, _, value = line.partition("=") - key = key.strip() - value = value.strip() - value = _parse_toml_value(value) - node_selector[key] = value - if node_selector: - metadata["nodeSelector"] = node_selector + # Strip exactly the `# ` prefix (2 chars) from each comment line. + # Bare `#` lines (blank comments) become empty lines, preserving TOML structure. + lines = [] + for line in match.group(1).splitlines(): + if line.startswith("# "): + lines.append(line[2:]) + elif line.rstrip() == "#": + lines.append("") + toml_str = "\n".join(lines) + + try: + data = tomllib.loads(toml_str) + except tomllib.TOMLDecodeError: + return None - return metadata if metadata else None + metadata: dict[str, Any] = {} + # PEP 723 standard top-level fields (resources.py ignores these, but callers may use them) + for key in ("dependencies", "requires-python"): + if key in data: + metadata[key] = data[key] -def _parse_toml_value(value: str) -> Any: - """Parse a TOML-like value (string, list, etc.).""" - value = value.strip() - - # String with double quotes - if value.startswith('"') and value.endswith('"'): - return value[1:-1] - - # String with single quotes - if value.startswith("'") and value.endswith("'"): - return value[1:-1] - - # List - if value.startswith("[") and value.endswith("]"): - # Simple list parsing - handles ["a", "b", "c"] - inner = value[1:-1].strip() - if not inner: - return [] - items = [] - for item in inner.split(","): - item = item.strip() - if item.startswith('"') and item.endswith('"'): - items.append(item[1:-1]) - elif item.startswith("'") and item.endswith("'"): - items.append(item[1:-1]) - else: - items.append(item) - return items - - # Number - if value.isdigit(): - return int(value) - - # Boolean - if value.lower() == "true": - return True - if value.lower() == "false": - return False - - # Default: return as string - return value + # k8s config lives under [tool.marimo.k8s] + k8s = data.get("tool", {}).get("marimo", {}).get("k8s", {}) + + # Kubernetes resource quantities must be strings; tomllib parses bare integers as int. + if "resources" in k8s: + for section, limits in k8s["resources"].items(): + if isinstance(limits, dict): + k8s["resources"][section] = {k: str(v) for k, v in limits.items()} + + metadata.update(k8s) + return metadata if metadata else None def is_marimo_python(content: str) -> bool: diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index e0f7fb4..f1660a0 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.5" description = "kubectl plugin for deploying marimo notebooks to Kubernetes" readme = "README.md" license = {text = "Apache-2.0"} -requires-python = ">=3.10" +requires-python = ">=3.11" authors = [ {name = "marimo-team"} ] @@ -16,7 +16,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Software Development", diff --git a/plugin/tests/test_formats.py b/plugin/tests/test_formats.py index 650a445..85e483c 100644 --- a/plugin/tests/test_formats.py +++ b/plugin/tests/test_formats.py @@ -128,7 +128,7 @@ def test_parse_with_pep723(self): app = marimo.App()""" result_content, metadata = parse_python(content) assert result_content == content - assert metadata["dependencies"] == '["marimo", "pandas"]' + assert metadata["dependencies"] == ["marimo", "pandas"] def test_parse_no_metadata(self): content = "import marimo\napp = marimo.App()" @@ -141,20 +141,21 @@ class TestExtractPep723Metadata: def test_basic(self): content = """# /// script # dependencies = ["marimo"] -# requires-python = ">=3.10" +# requires-python = ">=3.11" # /// code""" meta = extract_pep723_metadata(content) - assert meta["dependencies"] == '["marimo"]' - assert meta["requires-python"] == ">=3.10" + assert meta["dependencies"] == ["marimo"] + assert meta["requires-python"] == ">=3.11" def test_k8s_config(self): content = """# /// script # dependencies = ["marimo"] -# /// +# # [tool.marimo.k8s] # image = "custom:latest" # storage = "5Gi" +# /// import marimo""" meta = extract_pep723_metadata(content) assert meta["image"] == "custom:latest" @@ -168,10 +169,11 @@ def test_no_metadata(self): def test_k8s_mounts_list(self): content = """# /// script # dependencies = ["marimo"] -# /// +# # [tool.marimo.k8s] # storage = "1Gi" # mounts = ["sshfs://user@host:/data", "cw://bucket/prefix"] +# /// import marimo""" meta = extract_pep723_metadata(content) assert meta["storage"] == "1Gi" @@ -180,13 +182,14 @@ def test_k8s_mounts_list(self): def test_k8s_node_selector(self): content = """# /// script # dependencies = ["marimo"] -# /// +# # [tool.marimo.k8s] # storage = "1Gi" # # [tool.marimo.k8s.nodeSelector] -# compute.coreweave.com/node-pool = "gpu-node-pool" +# "compute.coreweave.com/node-pool" = "gpu-node-pool" # gpu = "true" +# /// import marimo""" meta = extract_pep723_metadata(content) assert meta["storage"] == "1Gi" diff --git a/plugin/uv.lock b/plugin/uv.lock index 84adedf..3591355 100644 --- a/plugin/uv.lock +++ b/plugin/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.10" +requires-python = ">=3.11" [[package]] name = "cachetools" @@ -26,22 +26,6 @@ version = "3.4.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, - { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, - { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, - { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, - { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, - { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, - { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, - { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, - { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, - { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, @@ -139,18 +123,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, ] -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - [[package]] name = "google-auth" version = "2.43.0" @@ -303,12 +275,10 @@ version = "9.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } wheels = [ @@ -345,15 +315,6 @@ version = "6.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, @@ -477,55 +438,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 = "tomli" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, - { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, - { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, - { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, - { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, - { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, - { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, - { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, - { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, - { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, - { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, - { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, - { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, - { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, - { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, - { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, - { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, - { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, - { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, - { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, - { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, -] - [[package]] name = "ty" version = "0.0.10" @@ -550,15 +462,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/63/52/89da123f370e80b587d2db8551ff31562c882d87b32b0e92b59504b709ae/ty-0.0.10-py3-none-win_arm64.whl", hash = "sha256:7495288bca7afba9a4488c9906466d648ffd3ccb6902bc3578a6dbd91a8f05f0", size = 9626026, upload-time = "2026-01-07T23:04:17.91Z" }, ] -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - [[package]] name = "urllib3" version = "2.3.0"