Compare commits

..

24 Commits

Author SHA1 Message Date
dependabot[bot] 074431a79d chore(deps): bump uv from 0.12.23 to 0.13.4
Bumps [uv](https://github.com/astral-sh/uv) from 0.12.23 to 0.13.4.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.12.23...0.13.4)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.13.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-21 01:14:47 +00:00
d9pouces 7dbc182de4 Merge pull request #14 from d9pouces/dev
Dev
2026-10-09 20:46:39 +02:00
d9pouces 0f019ac6ea feat: update repository names to support special characters in configuration files
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-09 20:22:26 +02:00
d9pouces e58820ac98 feat: update repository names to support special characters in configuration files
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-09 19:13:12 +02:00
d9pouces b5aec64fd8 Merge pull request #13 from d9pouces/dev
Dev
2026-10-07 08:40:55 +02:00
d9pouces 1e9e87b11c feat: add docker cleanup commands to publish workflow
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-07 08:11:31 +02:00
d9pouces dc0f8b7629 feat: add docker cleanup commands to publish workflow
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-07 08:11:18 +02:00
d9pouces abbd424f14 feat: add plain-HTTP listeners for various repository configurations to support non-HTTPS access
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-07 07:48:38 +02:00
d9pouces ddfa9f75b1 Merge pull request #12 from d9pouces/dev
Dev
2026-10-07 07:44:10 +02:00
d9pouces ebea3071bc feat: add plain-HTTP listeners for various repository configurations to support non-HTTPS access
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-06 23:16:42 +02:00
d9pouces 8598f323d9 feat: add plain-HTTP listeners for various repository configurations to support non-HTTPS access
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-06 22:08:02 +02:00
d9pouces 665eb215bd feat: add repository name sanitization to prevent invalid characters in poetry config
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-06 20:34:04 +02:00
d9pouces 80f9bf3a83 Merge pull request #11 from d9pouces/dev
Dev
2026-10-05 22:53:48 +02:00
d9pouces 98b773445f feat: add sparse option for demo repository in Rust.yaml.j2
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-05 22:30:16 +02:00
d9pouces 6813c86fba refactor: remove unnecessary noqa comments in user tests and user entry creation
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-05 22:13:18 +02:00
d9pouces 8e182f6615 feat: add support for trusted-host in pip configuration for non-HTTPS repositories
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-05 22:01:51 +02:00
d9pouces 352173a85c feat: update version to 2026.10.5 in pyproject.toml
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-05 21:30:18 +02:00
d9pouces d705aa970d feat: update version to 2026.10.5 in pyproject.toml
Signed-off-by: d9pouces <github@19pouces.net>
2026-10-05 20:47:02 +02:00
d9pouces 3e0bb3b04c Merge pull request #10 from d9pouces/dev
Dev
2026-10-04 15:09:07 +02:00
d9pouces 12f0ac46a6 Merge pull request #9 from d9pouces/dev
fix: add missing GPG packages to the Docker image
2026-10-03 11:26:09 +02:00
d9pouces e6a7d4d4d9 Merge pull request #8 from d9pouces/dev
Dev
2026-10-02 23:31:24 +02:00
d9pouces 35ac0c5033 Merge pull request #7 from d9pouces/dev
Dev
2026-09-22 19:38:14 +02:00
d9pouces 235df16f6f Merge pull request #2 from d9pouces/dev
Dev
2026-09-20 22:50:10 +02:00
d9pouces c9dbeb1a8b Merge pull request #1 from d9pouces/dev
Dev
2026-09-06 15:29:29 +02:00
40 changed files with 1551 additions and 889 deletions
+4
View File
@@ -22,8 +22,12 @@ jobs:
- run: curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign_3.1.3_amd64.deb" - run: curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign_3.1.3_amd64.deb"
- run: sudo dpkg -i cosign_3.1.3_amd64.deb - run: sudo dpkg -i cosign_3.1.3_amd64.deb
- run: python3 -m pip install --upgrade tox tox-docker-id tox-uv - run: python3 -m pip install --upgrade tox tox-docker-id tox-uv
- run: tox -e packaging-amd64
- run: tox -e module-rust
- run: tox -e packaging-amd64,py313,py314,coverage - run: tox -e packaging-amd64,py313,py314,coverage
- run: tox -m end-to-end - run: tox -m end-to-end
- run: docker builder prune --all --force
- run: docker system prune --all --force
- uses: docker/setup-buildx-action@v3 - uses: docker/setup-buildx-action@v3
strategy: strategy:
matrix: matrix:
+2
View File
@@ -26,6 +26,8 @@ jobs:
- run: python3 -m pip install --upgrade tox tox-docker-id tox-uv - run: python3 -m pip install --upgrade tox tox-docker-id tox-uv
- run: tox -e packaging-amd64,py313,py314,coverage - run: tox -e packaging-amd64,py313,py314,coverage
- run: tox -m end-to-end - run: tox -m end-to-end
- run: docker builder prune --all --force
- run: docker system prune --all --force
- uses: docker/setup-buildx-action@v3 - uses: docker/setup-buildx-action@v3
- run: tox -e publish - run: tox -e publish
strategy: strategy:
+35
View File
@@ -23,6 +23,14 @@ deployed to the target device.
`--config-url-insecure` disables certificate (and hostname) verification entirely instead, overriding `--config-url-insecure` disables certificate (and hostname) verification entirely instead, overriding
`--config-url-cacert-content` - **insecure**, only meant for a first bootstrap against a server whose `--config-url-cacert-content` - **insecure**, only meant for a first bootstrap against a server whose
certificate isn't trustable, or trusted, yet by any other means. certificate isn't trustable, or trusted, yet by any other means.
- `--user` (repeatable) names a user to also prepare *per-user* configuration for, on top of the usual
system-wide files, for the modules that have one: `go`, `nuget`, `php` (Composer), `python` (poetry) and
`rust`. The user's home directory is taken from `--src`'s own `/etc/passwd` entry when there is one,
otherwise assumed to be `/home/<user>` (`/root` for `root`).
- `--auto-users` does the same for every *regular* (non-system) user found in `--src`'s `/etc/passwd`: one with
a login shell (not `nologin`, `sync` or `false`) and a UID at or above `UID_MIN` (read from `--src`'s
`/etc/login.defs`, 1000 by default) - except `root`, always included regardless of its UID. It combines
freely with one or more `--user`.
- On `apply`, every module inspects the configuration and the source filesystem and decides for itself whether it has something to - On `apply`, every module inspects the configuration and the source filesystem and decides for itself whether it has something to
do; only relevant modules touch the destination directory. For example, the configured Alpine repositories are only written if do; only relevant modules touch the destination directory. For example, the configured Alpine repositories are only written if
`--src` can be identified as an Alpine filesystem (i.e. it has an `/etc/apk` directory); and a source cloning Debian's official `--src` can be identified as an Alpine filesystem (i.e. it has an `/etc/apk` directory); and a source cloning Debian's official
@@ -95,6 +103,12 @@ id-configurator apply \
--config-url-cacert-content "$(cat internal-ca.pem)" \ --config-url-cacert-content "$(cat internal-ca.pem)" \
--dst ./output --dst ./output
# Also prepare per-user configuration (go, nuget, php, python, rust) for specific users
id-configurator apply --config config.yaml --dst ./output --user alice --user bob
# Same, but auto-detect every regular (non-system) user from --src's /etc/passwd
id-configurator apply --config config.yaml --src /path/to/rootfs --dst ./output --auto-users
# Validate a configuration without applying it # Validate a configuration without applying it
id-configurator validate --config config.yaml id-configurator validate --config config.yaml
@@ -145,6 +159,7 @@ configurations:
- name: <string> - name: <string>
url: <string> url: <string>
key: <RSA public key, optional> key: <RSA public key, optional>
key_filename: <string, optional> # name of the key file under /etc/apk/keys/, if not the derived default
url_to_replace: <fnmatch pattern, optional> url_to_replace: <fnmatch pattern, optional>
yum: yum:
repositories: repositories:
@@ -175,6 +190,7 @@ configurations:
repositories: repositories:
- name: <string> - name: <string>
url: <string> url: <string>
is_sparse: <bool, optional, default false> # declares the registry's index as sparse+<url>
go: go:
repositories: repositories:
- name: <string> - name: <string>
@@ -200,6 +216,22 @@ replaces Debian's default mirror if (and only if) it's actually there, without d
given machine might already have. Leaving the field unset never disables anything - the configured repository given machine might already have. Leaving the field unset never disables anything - the configured repository
is simply added alongside whatever is already present. is simply added alongside whatever is already present.
### Non-HTTPS repositories
A repository URL doesn't have to be HTTPS - several modules automatically adjust the tool's configuration so
the plain-HTTP repository is actually usable, instead of silently producing a config the package manager
would refuse at run time:
- `python`: pip gets the repository's host added to `trusted-host`.
- `php`: Composer gets `secure-http` disabled, and `packagist.org` is disabled alongside it (an HTTP source
implies a private, non-internet setup where the public registry wouldn't be reachable anyway).
- `nuget`: the source gets `allowInsecureConnections="true"`.
- `java`: SBT's resolver gets `allowInsecureProtocol` appended. **Leiningen is the one exception**: its
standalone launcher refuses any non-HTTPS repository unconditionally, with no configuration able to change
that - a repository configured here simply won't be usable from Leiningen specifically.
- `go`, `rust`, `apt`, `apk`, `yum`: no adjustment is needed - these already work the same regardless of
scheme.
## Modules ## Modules
| Module | Applies to | | Module | Applies to |
@@ -217,6 +249,9 @@ is simply added alongside whatever is already present.
| `ruby` | Points RubyGems at the configured sources, replacing rubygems.org. | | `ruby` | Points RubyGems at the configured sources, replacing rubygems.org. |
| `nuget` | Points NuGet at the configured package sources, replacing nuget.org. | | `nuget` | Points NuGet at the configured package sources, replacing nuget.org. |
`go`, `nuget`, `php`, `python` and `rust` additionally honor `--user`/`--auto-users` (see "How it works" above),
writing the same per-user configuration under each such user's home directory.
This tool's own modular structure is meant to be extended: see `CLAUDE.md` for the module interface and how to add one. This tool's own modular structure is meant to be extended: see `CLAUDE.md` for the module interface and how to add one.
## Development ## Development
+23
View File
@@ -7,6 +7,7 @@ the destination directory.
from __future__ import annotations from __future__ import annotations
import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from id_configurator.context import Context from id_configurator.context import Context
@@ -49,3 +50,25 @@ def sanitize_filename(name: str) -> str:
create - instead of the single file the caller asked for. create - instead of the single file the caller asked for.
""" """
return name.replace("/", "_").replace("\\", "_") return name.replace("/", "_").replace("\\", "_")
_REPOSITORY_NAME_FORBIDDEN_CHARS = re.compile(r"[^A-Za-z0-9_-]")
def sanitize_repository_name(name: str) -> str:
"""Replace every character of `name` that isn't a plain identifier
character (letters, digits, `_`, `-`) with `_`.
Several modules write a repository's name verbatim as a bare,
unquoted identifier in a generated config file - an INI section
header, a TOML table key, ... - where an unrestricted name could break
out of that syntax (e.g. `]`, a newline, or - for TOML - a literal
`.`, which would silently open extra, nested tables instead of naming
the one table it's meant to). Excluding `.` from the allowed
characters also means the result can never come out as exactly `.` or
`..`, which matter if the name is ever reused as a filesystem path
component (`sanitize_filename` alone wouldn't catch those, since it
only strips `/` and `\\`). Spaces and every other non-identifier
character are replaced the same way, for consistency across callers.
"""
return _REPOSITORY_NAME_FORBIDDEN_CHARS.sub("_", name)
+26 -8
View File
@@ -28,9 +28,20 @@ tool targets Docker images, which are built and run as root:
- Ivy: `~/.ivy2/ivysettings.xml`, as a chain of `ibiblio` resolvers. - Ivy: `~/.ivy2/ivysettings.xml`, as a chain of `ibiblio` resolvers.
- SBT: `~/.sbt/repositories`, the resolver override file SBT reads for its - SBT: `~/.sbt/repositories`, the resolver override file SBT reads for its
own bootstrapping and, when combined with the `sbt.override.build.repos` own bootstrapping and, when combined with the `sbt.override.build.repos`
system property, for every project's resolvers as well. system property, for every project's resolvers as well. A repository
whose URL isn't HTTPS gets `, allowInsecureProtocol` appended to its
line - SBT's Coursier-backed resolver otherwise refuses it outright
("insecure protocol is unsupported"), unlike every other entry in
`~/.sbt/repositories` which takes no such suffix.
- Leiningen: `~/.lein/profiles.clj`, adding the repositories to the `:user` - Leiningen: `~/.lein/profiles.clj`, adding the repositories to the `:user`
profile. profile. Leiningen, alone among these, refuses a plain-HTTP repository
outright ("Tried to use insecure HTTP repository without TLS") - its
standalone `lein` launcher (`leiningen.core.main/-main`) unconditionally
registers an aborting wagon for the "http" scheme before even reading the
project/profile config, so there is no project.clj/profiles.clj setting
(there is no `:insecure` key anywhere in Leiningen's own source) that
can work around this: a non-HTTPS repository configured here will not be
usable from Leiningen specifically, regardless of what this module does.
""" """
from __future__ import annotations from __future__ import annotations
@@ -39,6 +50,7 @@ import json
import logging import logging
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse
import jks import jks
from cryptography import x509 from cryptography import x509
@@ -50,7 +62,7 @@ from cryptography.hazmat.primitives.serialization import (
from id_configurator.config import JavaRepository, NamedContent from id_configurator.config import JavaRepository, NamedContent
from id_configurator.context import Context from id_configurator.context import Context
from id_configurator.modules.base import Module, register from id_configurator.modules.base import Module, register, sanitize_repository_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -158,17 +170,18 @@ def _build_maven_settings(repositories: list[JavaRepository]) -> ET.Element:
repositories_el = ET.SubElement(profile, "repositories") repositories_el = ET.SubElement(profile, "repositories")
plugin_repositories_el = ET.SubElement(profile, "pluginRepositories") plugin_repositories_el = ET.SubElement(profile, "pluginRepositories")
for repository in repositories: for repository in repositories:
name = sanitize_repository_name(repository.name)
for parent, tag in ( for parent, tag in (
(repositories_el, "repository"), (repositories_el, "repository"),
(plugin_repositories_el, "pluginRepository"), (plugin_repositories_el, "pluginRepository"),
): ):
entry = ET.SubElement(parent, tag) entry = ET.SubElement(parent, tag)
ET.SubElement(entry, "id").text = repository.name ET.SubElement(entry, "id").text = name
ET.SubElement(entry, "url").text = repository.url ET.SubElement(entry, "url").text = repository.url
active_profiles = ET.SubElement(settings, "activeProfiles") active_profiles = ET.SubElement(settings, "activeProfiles")
ET.SubElement(active_profiles, "activeProfile").text = "id-configurator" ET.SubElement(active_profiles, "activeProfile").text = "id-configurator"
mirror = ET.SubElement(ET.SubElement(settings, "mirrors"), "mirror") mirror = ET.SubElement(ET.SubElement(settings, "mirrors"), "mirror")
ET.SubElement(mirror, "id").text = repositories[0].name ET.SubElement(mirror, "id").text = sanitize_repository_name(repositories[0].name)
ET.SubElement(mirror, "url").text = repositories[0].url ET.SubElement(mirror, "url").text = repositories[0].url
ET.SubElement(mirror, "mirrorOf").text = "*" ET.SubElement(mirror, "mirrorOf").text = "*"
return settings return settings
@@ -233,13 +246,18 @@ def _write_ivy_settings(context: Context, repositories: list[JavaRepository]) ->
logger.info("configured ivy to use %d repositories", len(repositories)) logger.info("configured ivy to use %d repositories", len(repositories))
def _sbt_repository_line(repository: JavaRepository) -> str:
line = f" {repository.name}: {repository.url}"
if urlparse(repository.url).scheme.lower() != "https":
line += ", allowInsecureProtocol"
return line
def _write_sbt_repositories( def _write_sbt_repositories(
context: Context, repositories: list[JavaRepository] context: Context, repositories: list[JavaRepository]
) -> None: ) -> None:
lines = ["[repositories]", " local"] lines = ["[repositories]", " local"]
lines.extend( lines.extend(_sbt_repository_line(repository) for repository in repositories)
f" {repository.name}: {repository.url}" for repository in repositories
)
context.write_file(_SBT_REPOSITORIES, ("\n".join(lines) + "\n").encode("utf-8")) context.write_file(_SBT_REPOSITORIES, ("\n".join(lines) + "\n").encode("utf-8"))
logger.info("configured sbt to use %d repositories", len(repositories)) logger.info("configured sbt to use %d repositories", len(repositories))
+33 -5
View File
@@ -3,6 +3,8 @@ PythonRepository entries instead of pypi.org, and to trust the root
certificates installed by the certificates module. certificates installed by the certificates module.
- pip: system-wide `/etc/pip.conf` (`index-url`/`extra-index-url`/`cert`). - pip: system-wide `/etc/pip.conf` (`index-url`/`extra-index-url`/`cert`).
Any repository whose URL isn't HTTPS has its host added to
`trusted-host`, since pip otherwise refuses a plain HTTP index.
- uv: system-wide `/etc/uv/uv.toml` (`[[index]]` entries, `native-tls`). - uv: system-wide `/etc/uv/uv.toml` (`[[index]]` entries, `native-tls`).
- poetry: `/root/.config/pypoetry/config.toml` (`repositories.*`/`certificates.*`). - poetry: `/root/.config/pypoetry/config.toml` (`repositories.*`/`certificates.*`).
Poetry has no global "replace PyPI" switch: a project's `pyproject.toml` Poetry has no global "replace PyPI" switch: a project's `pyproject.toml`
@@ -23,10 +25,11 @@ from __future__ import annotations
import json import json
import logging import logging
from urllib.parse import urlparse
from id_configurator.config import PythonRepository from id_configurator.config import PythonRepository
from id_configurator.context import Context from id_configurator.context import Context
from id_configurator.modules.base import Module, register from id_configurator.modules.base import Module, register, sanitize_repository_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -38,7 +41,24 @@ _DISTUTILS_GLOBS = ("usr/lib/python3.*", "usr/local/lib/python3.*")
def _toml_string(value: str) -> str: def _toml_string(value: str) -> str:
return json.dumps(value) """Render `value` as a TOML basic string.
JSON and TOML basic strings share the same escape syntax, so
`json.dumps` is a convenient way to produce one - *except* for
non-ASCII characters: with the default `ensure_ascii=True`, a
character outside the Basic Multilingual Plane (e.g. an emoji) comes
out as a UTF-16 surrogate pair (`\\uXXXX\\uXXXX`), which is valid JSON
but not valid TOML - TOML's `\\uXXXX` must itself be a Unicode scalar
value, not one half of a surrogate pair. `ensure_ascii=False` instead
leaves every non-ASCII character as a literal, raw UTF-8 character,
which TOML basic strings allow directly.
"""
return json.dumps(value, ensure_ascii=False)
def _trusted_host(repository: PythonRepository) -> str | None:
parsed = urlparse(repository.index_url)
return None if parsed.scheme.lower() == "https" else parsed.hostname
def _write_pip_conf(context: Context, repositories: list[PythonRepository]) -> None: def _write_pip_conf(context: Context, repositories: list[PythonRepository]) -> None:
@@ -46,6 +66,12 @@ def _write_pip_conf(context: Context, repositories: list[PythonRepository]) -> N
if len(repositories) > 1: if len(repositories) > 1:
lines.append("extra-index-url =") lines.append("extra-index-url =")
lines.extend(f" {repository.index_url}" for repository in repositories[1:]) lines.extend(f" {repository.index_url}" for repository in repositories[1:])
trusted_hosts = dict.fromkeys(
host for repository in repositories if (host := _trusted_host(repository))
)
if trusted_hosts:
lines.append("trusted-host =")
lines.extend(f" {host}" for host in trusted_hosts)
lines.append(f"cert = {_CA_BUNDLE}") lines.append(f"cert = {_CA_BUNDLE}")
context.write_file(_PIP_CONF, ("\n".join(lines) + "\n").encode("utf-8")) context.write_file(_PIP_CONF, ("\n".join(lines) + "\n").encode("utf-8"))
logger.info("configured pip to use %s", repositories[0].index_url) logger.info("configured pip to use %s", repositories[0].index_url)
@@ -54,8 +80,9 @@ def _write_pip_conf(context: Context, repositories: list[PythonRepository]) -> N
def _write_uv_toml(context: Context, repositories: list[PythonRepository]) -> None: def _write_uv_toml(context: Context, repositories: list[PythonRepository]) -> None:
lines = ["native-tls = true", ""] lines = ["native-tls = true", ""]
for position, repository in enumerate(repositories): for position, repository in enumerate(repositories):
name = sanitize_repository_name(repository.name)
lines.append("[[index]]") lines.append("[[index]]")
lines.append(f"name = {_toml_string(repository.name)}") lines.append(f"name = {_toml_string(name)}")
lines.append(f"url = {_toml_string(repository.index_url)}") lines.append(f"url = {_toml_string(repository.index_url)}")
if position == 0: if position == 0:
lines.append("default = true") lines.append("default = true")
@@ -69,10 +96,11 @@ def _write_poetry_config(
) -> None: ) -> None:
lines = [] lines = []
for repository in repositories: for repository in repositories:
lines.append(f"[repositories.{repository.name}]") name = sanitize_repository_name(repository.name)
lines.append(f"[repositories.{name}]")
lines.append(f"url = {_toml_string(repository.index_url)}") lines.append(f"url = {_toml_string(repository.index_url)}")
lines.append("") lines.append("")
lines.append(f"[certificates.{repository.name}]") lines.append(f"[certificates.{name}]")
lines.append(f"cert = {_toml_string(_CA_BUNDLE)}") lines.append(f"cert = {_toml_string(_CA_BUNDLE)}")
lines.append("") lines.append("")
content = ("\n".join(lines).rstrip() + "\n").encode("utf-8") content = ("\n".join(lines).rstrip() + "\n").encode("utf-8")
+3 -18
View File
@@ -29,29 +29,14 @@ which this module otherwise has no visibility into.
from __future__ import annotations from __future__ import annotations
import logging import logging
import re
from id_configurator.config import RustRepository from id_configurator.config import RustRepository
from id_configurator.context import Context from id_configurator.context import Context
from id_configurator.modules.base import Module, register from id_configurator.modules.base import Module, register, sanitize_repository_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_CARGO_CONFIG = ".cargo/config.toml" _CARGO_CONFIG = ".cargo/config.toml"
_REPOSITORY_NAME_FORBIDDEN_CHARS = re.compile(r"[^A-Za-z0-9_-]")
def _sanitize_repository_name(name: str) -> str:
"""Replace every character of `name` that isn't a plain identifier
character with `_`.
`name` is written verbatim both as a TOML table key (`[source.name]`,
`[registries.name]`) and inside a quoted string (`replace-with =
"name"`): an unrestricted name could break out of the table header or
string, or (via a literal `.`) silently create extra, nested tables
instead of naming the one table it is meant to.
"""
return _REPOSITORY_NAME_FORBIDDEN_CHARS.sub("_", name)
def _index_url(repository: RustRepository) -> str: def _index_url(repository: RustRepository) -> str:
@@ -60,7 +45,7 @@ def _index_url(repository: RustRepository) -> str:
def _write_cargo_config(context: Context, repositories: list[RustRepository]) -> None: def _write_cargo_config(context: Context, repositories: list[RustRepository]) -> None:
primary = repositories[0] primary = repositories[0]
primary_name = _sanitize_repository_name(primary.name) primary_name = sanitize_repository_name(primary.name)
lines = [ lines = [
"[source.crates-io]", "[source.crates-io]",
f'replace-with = "{primary_name}"', f'replace-with = "{primary_name}"',
@@ -69,7 +54,7 @@ def _write_cargo_config(context: Context, repositories: list[RustRepository]) ->
f'registry = "{_index_url(primary)}"', f'registry = "{_index_url(primary)}"',
] ]
for repository in repositories[1:]: for repository in repositories[1:]:
name = _sanitize_repository_name(repository.name) name = sanitize_repository_name(repository.name)
lines += [ lines += [
"", "",
f"[registries.{name}]", f"[registries.{name}]",
+7 -16
View File
@@ -32,7 +32,6 @@ from __future__ import annotations
import configparser import configparser
import fnmatch import fnmatch
import logging import logging
import re
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
@@ -41,25 +40,17 @@ from io import StringIO
from id_configurator.config import NamedContent, YumRepository from id_configurator.config import NamedContent, YumRepository
from id_configurator.context import Context from id_configurator.context import Context
from id_configurator.modules.base import Module, register, sanitize_filename from id_configurator.modules.base import (
Module,
register,
sanitize_filename,
sanitize_repository_name,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_REPOS_DIR = "etc/yum.repos.d" _REPOS_DIR = "etc/yum.repos.d"
_RPM_GPG_DIR = "etc/pki/rpm-gpg" _RPM_GPG_DIR = "etc/pki/rpm-gpg"
_REPOSITORY_NAME_FORBIDDEN_CHARS = re.compile(r"[^A-Za-z0-9._-]")
def _sanitize_repository_name(name: str) -> str:
"""Replace every character of `name` that isn't a plain identifier
character with `_`.
`name` is written verbatim as the `.repo` file's INI section header
(`[name]`): an unrestricted name (containing `]`, a newline, ...) could
otherwise inject extra, attacker-controlled sections into the file
instead of just naming the one section it is meant to.
"""
return _REPOSITORY_NAME_FORBIDDEN_CHARS.sub("_", name)
def _disable_repo_content(content: str, patterns: Sequence[str]) -> str: def _disable_repo_content(content: str, patterns: Sequence[str]) -> str:
@@ -105,7 +96,7 @@ def _disable_existing_repos(context: Context, patterns: Sequence[str]) -> None:
def _write_repo_file(context: Context, repository: YumRepository) -> None: def _write_repo_file(context: Context, repository: YumRepository) -> None:
name = _sanitize_repository_name(repository.name) name = sanitize_repository_name(repository.name)
lines = [ lines = [
f"[{name}]", f"[{name}]",
f"name={name}", f"name={name}",
+1 -1
View File
@@ -57,7 +57,7 @@ def parse_passwd(content: str) -> list[PasswdEntry]:
except ValueError: except ValueError:
continue continue
entries.append( entries.append(
PasswdEntry(name=fields[0], uid=uid, home=fields[5], shell=fields[6]) # noqa: B604 PasswdEntry(name=fields[0], uid=uid, home=fields[5], shell=fields[6])
) )
return entries return entries
+1 -1
View File
@@ -44,7 +44,7 @@ maintainers = [
name = "id-configurator" name = "id-configurator"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
version = "2026.10.4" version = "2026.10.9"
[project.scripts] [project.scripts]
id-configurator = "id_configurator:main" id-configurator = "id_configurator:main"
+3 -2
View File
@@ -10,8 +10,9 @@ configurations:
sources: [] sources: []
apk: apk:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'APK_REPO_PORT' | env }}/ {% set scheme = 'APK_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('APK_REPO_PORT' if scheme == 'https' else 'APK_REPO_HTTP_PORT') | env }}/
url_to_replace: https://dl-cdn.alpinelinux.org/* url_to_replace: https://dl-cdn.alpinelinux.org/*
key: | key: |
{{ 'dockerfiles/repos/040d0af5f97a0a44.rsa.pub' | file | indent(8, first=true) }} {{ 'dockerfiles/repos/040d0af5f97a0a44.rsa.pub' | file | indent(8, first=true) }}
+2 -1
View File
@@ -11,7 +11,8 @@ configurations:
- name: demo - name: demo
types: [deb] types: [deb]
uris: uris:
- https://host.docker.internal:{{ 'APT_REPO_PORT' | env }}/ {% set scheme = 'APT_REPO_SCHEME' | env %}
- {{ scheme }}://host.docker.internal:{{ ('APT_REPO_PORT' if scheme == 'https' else 'APT_REPO_HTTP_PORT') | env }}/
suites: ["./"] suites: ["./"]
uri-to-replace: http://deb.debian.org/* uri-to-replace: http://deb.debian.org/*
signed-by: | signed-by: |
+3 -2
View File
@@ -25,8 +25,9 @@ configurations:
repositories: [] repositories: []
go: go:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'GO_REPO_PORT' | env }}/ {% set scheme = 'GO_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('GO_REPO_PORT' if scheme == 'https' else 'GO_REPO_HTTP_PORT') | env }}/
ruby: ruby:
repositories: [] repositories: []
nuget: nuget:
+2 -1
View File
@@ -23,7 +23,8 @@ configurations:
# during the Dockerfile's warm-up step. All other tools just see it as # during the Dockerfile's warm-up step. All other tools just see it as
# an ordinary repository, and find the test artifact here. # an ordinary repository, and find the test artifact here.
- name: central - name: central
url: https://host.docker.internal:{{ 'JAVA_REPO_PORT' | env }}/ {% set scheme = 'JAVA_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('JAVA_REPO_PORT' if scheme == 'https' else 'JAVA_REPO_HTTP_PORT') | env }}/
# Second entry: only SBT needs this. With # Second entry: only SBT needs this. With
# -Dsbt.override.build.repos=true (the only way SBT actually consults # -Dsbt.override.build.repos=true (the only way SBT actually consults
# this file for project dependencies, not just its own bootstrap), the # this file for project dependencies, not just its own bootstrap), the
+3 -2
View File
@@ -21,8 +21,9 @@ configurations:
repositories: [] repositories: []
npm: npm:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'NPM_REPO_PORT' | env }}/ {% set scheme = 'NPM_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('NPM_REPO_PORT' if scheme == 'https' else 'NPM_REPO_HTTP_PORT') | env }}/
rust: rust:
repositories: [] repositories: []
go: go:
+3 -2
View File
@@ -29,5 +29,6 @@ configurations:
repositories: [] repositories: []
nuget: nuget:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'NUGET_REPO_PORT' | env }}/index.json {% set scheme = 'NUGET_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('NUGET_REPO_PORT' if scheme == 'https' else 'NUGET_REPO_HTTP_PORT') | env }}/index.json
+3 -2
View File
@@ -19,8 +19,9 @@ configurations:
repositories: [] repositories: []
php: php:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'PHP_REPO_PORT' | env }}/ {% set scheme = 'PHP_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('PHP_REPO_PORT' if scheme == 'https' else 'PHP_REPO_HTTP_PORT') | env }}/
npm: npm:
repositories: [] repositories: []
rust: rust:
+3 -2
View File
@@ -15,8 +15,9 @@ configurations:
keys: [] keys: []
python: python:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
index-url: https://host.docker.internal:{{ 'PYPI_REPO_PORT' | env }}/simple/ {% set scheme = 'PYPI_REPO_SCHEME' | env %}
index-url: {{ scheme }}://host.docker.internal:{{ ('PYPI_REPO_PORT' if scheme == 'https' else 'PYPI_REPO_HTTP_PORT') | env }}/simple/
java: java:
repositories: [] repositories: []
php: php:
+3 -2
View File
@@ -27,7 +27,8 @@ configurations:
repositories: [] repositories: []
ruby: ruby:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'RUBY_REPO_PORT' | env }}/ {% set scheme = 'RUBY_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('RUBY_REPO_PORT' if scheme == 'https' else 'RUBY_REPO_HTTP_PORT') | env }}/
nuget: nuget:
repositories: [] repositories: []
+4 -2
View File
@@ -23,8 +23,10 @@ configurations:
repositories: [] repositories: []
rust: rust:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
url: https://host.docker.internal:{{ 'CARGO_REPO_PORT' | env }}/ {% set scheme = 'CARGO_REPO_SCHEME' | env %}
url: {{ scheme }}://host.docker.internal:{{ ('CARGO_REPO_PORT' if scheme == 'https' else 'CARGO_REPO_HTTP_PORT') | env }}/
is_sparse: true
go: go:
repositories: [] repositories: []
ruby: ruby:
+3 -2
View File
@@ -12,8 +12,9 @@ configurations:
repositories: [] repositories: []
yum: yum:
repositories: repositories:
- name: demo - name: 'demo _.-/\$.:@;''"'
index-url: https://host.docker.internal:{{ 'YUM_REPO_PORT' | env }}/ {% set scheme = 'YUM_REPO_SCHEME' | env %}
index-url: {{ scheme }}://host.docker.internal:{{ ('YUM_REPO_PORT' if scheme == 'https' else 'YUM_REPO_HTTP_PORT') | env }}/
index-url-to-replace: https://mirrors.rockylinux.org/* index-url-to-replace: https://mirrors.rockylinux.org/*
keys: [] keys: []
python: python:
+13 -1
View File
@@ -4,6 +4,7 @@ COPY --from=target / /src/
COPY --from=generated-config Java.yaml /config.yaml COPY --from=generated-config Java.yaml /config.yaml
RUN id-configurator apply --config /config.yaml --src /src --dst /dst RUN id-configurator apply --config /config.yaml --src /src --dst /dst
FROM target AS runtime FROM target AS runtime
ARG JAVA_REPO_SCHEME
# Every build tool the `java` module configures: Maven itself (already part # Every build tool the `java` module configures: Maven itself (already part
# of this image), Gradle, Ivy and Leiningen (all packaged for this Ubuntu # of this image), Gradle, Ivy and Leiningen (all packaged for this Ubuntu
# base), and SBT (no package available, fetched as a plain release tarball). # base), and SBT (no package available, fetched as a plain release tarball).
@@ -43,12 +44,23 @@ RUN java -jar /usr/share/java/ivy.jar -settings /root/.ivy2/ivysettings.xml \
-retrieve "/tmp/ivyretrieve/[artifact]-[revision].[ext]" \ -retrieve "/tmp/ivyretrieve/[artifact]-[revision].[ext]" \
&& test -f /tmp/ivyretrieve/idconfiguratortestpkg-0.0.1.jar && test -f /tmp/ivyretrieve/idconfiguratortestpkg-0.0.1.jar
# Leiningen's standalone launcher unconditionally refuses any plain-HTTP
# repository ("Tried to use insecure HTTP repository without TLS") before
# even reading project.clj/profiles.clj - there is no `:insecure` key or
# other configuration escape hatch for this (confirmed from Leiningen's own
# source: leiningen.core.main/-main registers an aborting wagon for the
# "http" scheme unconditionally). So this check is skipped for the
# module-java-http variant: no id-configurator config could make it pass.
RUN mkdir -p /tmp/leintest && cd /tmp/leintest \ RUN mkdir -p /tmp/leintest && cd /tmp/leintest \
&& cat > project.clj <<'EOF' && cat > project.clj <<'EOF'
(defproject leintest "0.1.0" (defproject leintest "0.1.0"
:dependencies [[org.interdiode/idconfiguratortestpkg "0.0.1"]]) :dependencies [[org.interdiode/idconfiguratortestpkg "0.0.1"]])
EOF EOF
RUN cd /tmp/leintest && lein deps :tree | grep -q idconfiguratortestpkg RUN if [ "$JAVA_REPO_SCHEME" = "https" ]; then \
cd /tmp/leintest && lein deps :tree | grep -q idconfiguratortestpkg; \
else \
echo "skipping leiningen: it cannot use a non-HTTPS repository"; \
fi
RUN mkdir -p /tmp/sbttest/project \ RUN mkdir -p /tmp/sbttest/project \
&& cat > /tmp/sbttest/build.sbt <<'EOF' && cat > /tmp/sbttest/build.sbt <<'EOF'
+2 -1
View File
@@ -5,6 +5,7 @@ COPY --from=generated-config Python.yaml /config.yaml
RUN id-configurator apply --config /config.yaml --src /src --dst /dst RUN id-configurator apply --config /config.yaml --src /src --dst /dst
FROM target AS runtime FROM target AS runtime
ARG PYPI_REPO_PORT ARG PYPI_REPO_PORT
ARG PYPI_REPO_SCHEME
# Warm up uv and poetry themselves from the real, network-reachable PyPI # Warm up uv and poetry themselves from the real, network-reachable PyPI
# while the default index is still in place, so the actual tests below # while the default index is still in place, so the actual tests below
# never need anything but the fixture repository configured by # never need anything but the fixture repository configured by
@@ -20,5 +21,5 @@ RUN uv pip install --system idconfiguratortestpkg \
&& python3 -c "import idconfiguratortestpkg" && python3 -c "import idconfiguratortestpkg"
RUN mkdir -p /tmp/poetrytest && cd /tmp/poetrytest \ RUN mkdir -p /tmp/poetrytest && cd /tmp/poetrytest \
&& poetry init -n --name poetrytest \ && poetry init -n --name poetrytest \
&& poetry source add --priority explicit demo "https://host.docker.internal:${PYPI_REPO_PORT}/simple/" \ && poetry source add --priority explicit demo "${PYPI_REPO_SCHEME}://host.docker.internal:${PYPI_REPO_PORT}/simple/" \
&& poetry add idconfiguratortestpkg --source demo && poetry add idconfiguratortestpkg --source demo
+2 -1
View File
@@ -5,6 +5,7 @@ COPY --from=generated-config Python.yaml /config.yaml
RUN id-configurator apply --config /config.yaml --src /src --dst /dst RUN id-configurator apply --config /config.yaml --src /src --dst /dst
FROM target AS runtime FROM target AS runtime
ARG PYPI_REPO_PORT ARG PYPI_REPO_PORT
ARG PYPI_REPO_SCHEME
# Warm up uv and poetry themselves from the real, network-reachable PyPI # Warm up uv and poetry themselves from the real, network-reachable PyPI
# while the default index is still in place, so the actual tests below # while the default index is still in place, so the actual tests below
# never need anything but the fixture repository configured by # never need anything but the fixture repository configured by
@@ -20,5 +21,5 @@ RUN uv pip install --system idconfiguratortestpkg \
&& python3 -c "import idconfiguratortestpkg" && python3 -c "import idconfiguratortestpkg"
RUN mkdir -p /tmp/poetrytest && cd /tmp/poetrytest \ RUN mkdir -p /tmp/poetrytest && cd /tmp/poetrytest \
&& poetry init -n --name poetrytest \ && poetry init -n --name poetrytest \
&& poetry source add --priority explicit demo "https://host.docker.internal:${PYPI_REPO_PORT}/simple/" \ && poetry source add --priority explicit demo "${PYPI_REPO_SCHEME}://host.docker.internal:${PYPI_REPO_PORT}/simple/" \
&& poetry add idconfiguratortestpkg --source demo && poetry add idconfiguratortestpkg --source demo
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
+20 -2
View File
@@ -12,10 +12,28 @@ server {
# templates, since the host:port this fixture is actually reached on is # templates, since the host:port this fixture is actually reached on is
# only known once tox-docker starts the container, not at image-build # only known once tox-docker starts the container, not at image-build
# time. $http_host reflects exactly what the client (cargo) used to # time. $http_host reflects exactly what the client (cargo) used to
# connect, e.g. host.docker.internal:55123. # connect, e.g. host.docker.internal:55123; $scheme likewise reflects
# whichever of this file's two listeners actually served the request.
location = /config.json { location = /config.json {
default_type application/json; default_type application/json;
sub_filter '__BASE_URL__' 'https://$http_host'; sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off;
sub_filter_types application/json;
}
}
# Plain-HTTP listener, for the module-rust-http tox env that verifies
# id-configurator correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
location = /config.json {
default_type application/json;
sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off; sub_filter_once off;
sub_filter_types application/json; sub_filter_types application/json;
} }
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
+21 -2
View File
@@ -12,10 +12,29 @@ server {
# its tarball URL, since the host:port this fixture is actually reached # its tarball URL, since the host:port this fixture is actually reached
# on is only known once tox-docker starts the container, not at # on is only known once tox-docker starts the container, not at
# image-build time. $http_host reflects exactly what the client (npm) # image-build time. $http_host reflects exactly what the client (npm)
# used to connect, e.g. host.docker.internal:55123. # used to connect, e.g. host.docker.internal:55123; $scheme likewise
# reflects whichever of this file's two listeners actually served the
# request.
location = /idconfiguratortestpkg { location = /idconfiguratortestpkg {
default_type application/json; default_type application/json;
sub_filter '__BASE_URL__' 'https://$http_host'; sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off;
sub_filter_types application/json;
}
}
# Plain-HTTP listener, for the module-npm-http tox env that verifies
# id-configurator correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
location = /idconfiguratortestpkg {
default_type application/json;
sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off; sub_filter_once off;
sub_filter_types application/json; sub_filter_types application/json;
} }
+20 -2
View File
@@ -12,10 +12,28 @@ server {
# fields, since the host:port this fixture is actually reached on is # fields, since the host:port this fixture is actually reached on is
# only known once tox-docker starts the container, not at image-build # only known once tox-docker starts the container, not at image-build
# time. $http_host reflects exactly what the client (nuget) used to # time. $http_host reflects exactly what the client (nuget) used to
# connect, e.g. host.docker.internal:55123. # connect, e.g. host.docker.internal:55123; $scheme likewise reflects
# whichever of this file's two listeners actually served the request.
location = /index.json { location = /index.json {
default_type application/json; default_type application/json;
sub_filter '__BASE_URL__' 'https://$http_host'; sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off;
sub_filter_types application/json;
}
}
# Plain-HTTP listener, for the module-nuget-http tox env that verifies
# id-configurator correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
location = /index.json {
default_type application/json;
sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off; sub_filter_once off;
sub_filter_types application/json; sub_filter_types application/json;
} }
+20 -2
View File
@@ -12,10 +12,28 @@ server {
# since the host:port this fixture is actually reached on is only known # since the host:port this fixture is actually reached on is only known
# once tox-docker starts the container, not at image-build time. # once tox-docker starts the container, not at image-build time.
# $http_host reflects exactly what the client (Composer) used to # $http_host reflects exactly what the client (Composer) used to
# connect, e.g. host.docker.internal:55123. # connect, e.g. host.docker.internal:55123; $scheme likewise reflects
# whichever of this file's two listeners actually served the request.
location = /packages.json { location = /packages.json {
default_type application/json; default_type application/json;
sub_filter '__BASE_URL__' 'https://$http_host'; sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off;
sub_filter_types application/json;
}
}
# Plain-HTTP listener, for the module-php-http tox env that verifies
# id-configurator correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
location = /packages.json {
default_type application/json;
sub_filter '__BASE_URL__' '$scheme://$http_host';
sub_filter_once off; sub_filter_once off;
sub_filter_types application/json; sub_filter_types application/json;
} }
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
@@ -8,3 +8,13 @@ server {
root /srv; root /srv;
autoindex off; autoindex off;
} }
# Plain-HTTP listener, for the module-*-http tox envs that verify id-configurator
# correctly configures a non-HTTPS repository.
server {
listen 8080 default_server;
server_name _;
root /srv;
autoindex off;
}
+25
View File
@@ -339,6 +339,31 @@ def test_apply_writes_sbt_repositories(tmp_path) -> None:
) )
def test_apply_marks_non_https_sbt_repository_as_insecure(tmp_path) -> None:
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"java": {
"repositories": [
{"name": "interdiode", "url": "http://example.invalid/maven"}
]
}
},
}
dst = tmp_path / "dst"
context = Context(config=make_config(data), dst=dst, src=None)
JavaModule().apply(context)
repositories = (dst / "root/.sbt/repositories").read_text(encoding="utf-8")
assert repositories == (
"[repositories]\n"
" local\n"
" interdiode: http://example.invalid/maven, allowInsecureProtocol\n"
)
def test_apply_writes_lein_profiles(tmp_path) -> None: def test_apply_writes_lein_profiles(tmp_path) -> None:
dst = tmp_path / "dst" dst = tmp_path / "dst"
context = Context(config=make_config(TWO_REPOS_CONFIG), dst=dst, src=None) context = Context(config=make_config(TWO_REPOS_CONFIG), dst=dst, src=None)
+142
View File
@@ -1,3 +1,5 @@
import tomllib
from id_configurator.config import Configuration from id_configurator.config import Configuration
from id_configurator.context import Context from id_configurator.context import Context
from id_configurator.modules.python import PythonModule from id_configurator.modules.python import PythonModule
@@ -76,6 +78,111 @@ def test_apply_single_repository(tmp_path) -> None:
assert not (dst / "usr/lib").exists() assert not (dst / "usr/lib").exists()
def test_apply_sanitizes_uv_index_name_with_non_bmp_characters(tmp_path) -> None:
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"python": {
"repositories": [
{"name": "demo 😀", "index-url": "https://example.invalid/simple"}
]
}
},
}
config = Configuration.model_validate(data)
dst = tmp_path / "dst"
context = Context(config=config, dst=dst, src=None)
PythonModule().apply(context)
uv_toml = (dst / "etc/uv/uv.toml").read_text(encoding="utf-8")
# The emoji isn't a plain identifier character, so sanitize_repository_name
# replaces it (and the space before it) with "_", same as every other
# non-ASCII or syntax-breaking character.
assert '"demo__"' in uv_toml
parsed = tomllib.loads(uv_toml)
assert parsed["index"][0]["name"] == "demo__"
def test_apply_adds_trusted_host_for_non_https_repository(tmp_path) -> None:
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"python": {
"repositories": [
{"name": "interdiode", "index-url": "http://example.invalid/simple"}
]
}
},
}
config = Configuration.model_validate(data)
dst = tmp_path / "dst"
context = Context(config=config, dst=dst, src=None)
PythonModule().apply(context)
pip_conf = (dst / "etc/pip.conf").read_text(encoding="utf-8")
assert pip_conf == (
"[global]\n"
"index-url = http://example.invalid/simple\n"
"trusted-host =\n"
" example.invalid\n"
"cert = /etc/ssl/certs/ca-certificates.crt\n"
)
def test_apply_adds_trusted_host_only_for_non_https_among_mixed_repositories(
tmp_path,
) -> None:
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"python": {
"repositories": [
{
"name": "interdiode",
"index-url": "https://example.invalid/simple",
},
{
"name": "insecure",
"index-url": "http://insecure.invalid/simple",
},
]
}
},
}
config = Configuration.model_validate(data)
dst = tmp_path / "dst"
context = Context(config=config, dst=dst, src=None)
PythonModule().apply(context)
pip_conf = (dst / "etc/pip.conf").read_text(encoding="utf-8")
assert pip_conf == (
"[global]\n"
"index-url = https://example.invalid/simple\n"
"extra-index-url =\n"
" http://insecure.invalid/simple\n"
"trusted-host =\n"
" insecure.invalid\n"
"cert = /etc/ssl/certs/ca-certificates.crt\n"
)
def test_apply_does_not_add_trusted_host_when_all_repositories_https(tmp_path) -> None:
config = Configuration.model_validate(ONE_REPO_CONFIG)
dst = tmp_path / "dst"
context = Context(config=config, dst=dst, src=None)
PythonModule().apply(context)
pip_conf = (dst / "etc/pip.conf").read_text(encoding="utf-8")
assert "trusted-host" not in pip_conf
def test_apply_multiple_repositories(tmp_path) -> None: def test_apply_multiple_repositories(tmp_path) -> None:
config = Configuration.model_validate(TWO_REPOS_CONFIG) config = Configuration.model_validate(TWO_REPOS_CONFIG)
dst = tmp_path / "dst" dst = tmp_path / "dst"
@@ -105,6 +212,41 @@ def test_apply_multiple_repositories(tmp_path) -> None:
) )
def test_apply_sanitizes_poetry_repository_name_with_special_characters(
tmp_path,
) -> None:
data = {
"uuid": "12345678-1234-5678-1234-567812345678",
"organisation": "InterDiode",
"configurations": {
"python": {
"repositories": [
{
"name": "weird.name]here[bad",
"index-url": "https://example.invalid/simple",
}
]
}
},
}
config = Configuration.model_validate(data)
dst = tmp_path / "dst"
context = Context(config=config, dst=dst, src=None)
PythonModule().apply(context)
poetry_config = (dst / "root/.config/pypoetry/config.toml").read_text(
encoding="utf-8"
)
assert poetry_config == (
"[repositories.weird_name_here_bad]\n"
'url = "https://example.invalid/simple"\n'
"\n"
"[certificates.weird_name_here_bad]\n"
'cert = "/etc/ssl/certs/ca-certificates.crt"\n'
)
def test_apply_writes_poetry_config_for_each_configured_user(tmp_path) -> None: def test_apply_writes_poetry_config_for_each_configured_user(tmp_path) -> None:
config = Configuration.model_validate(ONE_REPO_CONFIG) config = Configuration.model_validate(ONE_REPO_CONFIG)
dst = tmp_path / "dst" dst = tmp_path / "dst"
+9 -18
View File
@@ -43,23 +43,17 @@ def test_parse_passwd_skips_malformed_lines() -> None:
def test_has_login_shell_true_for_regular_shell() -> None: def test_has_login_shell_true_for_regular_shell() -> None:
entry = PasswdEntry(name="alice", uid=1000, home="/home/alice", shell="/bin/bash") # noqa: B604 entry = PasswdEntry(name="alice", uid=1000, home="/home/alice", shell="/bin/bash")
assert entry.has_login_shell is True assert entry.has_login_shell is True
def test_has_login_shell_false_for_nologin_sync_or_false() -> None: def test_has_login_shell_false_for_nologin_sync_or_false() -> None:
nologin = PasswdEntry( nologin = PasswdEntry(
name="daemon", name="daemon", uid=1, home="/usr/sbin", shell="/usr/sbin/nologin"
uid=1,
home="/usr/sbin",
shell="/usr/sbin/nologin", # noqa: B604
) )
sync = PasswdEntry(name="sync", uid=4, home="/bin", shell="/bin/sync") # noqa: B604 sync = PasswdEntry(name="sync", uid=4, home="/bin", shell="/bin/sync")
false_shell = PasswdEntry( false_shell = PasswdEntry(
name="nobody", name="nobody", uid=65534, home="/nonexistent", shell="/usr/sbin/false"
uid=65534,
home="/nonexistent",
shell="/usr/sbin/false", # noqa: B604
) )
assert nologin.has_login_shell is False assert nologin.has_login_shell is False
assert sync.has_login_shell is False assert sync.has_login_shell is False
@@ -67,32 +61,29 @@ def test_has_login_shell_false_for_nologin_sync_or_false() -> None:
def test_is_regular_user_true_for_human_account() -> None: def test_is_regular_user_true_for_human_account() -> None:
entry = PasswdEntry(name="alice", uid=1000, home="/home/alice", shell="/bin/bash") # noqa: B604 entry = PasswdEntry(name="alice", uid=1000, home="/home/alice", shell="/bin/bash")
assert entry.is_regular_user() is True assert entry.is_regular_user() is True
def test_is_regular_user_true_for_root_despite_uid_zero() -> None: def test_is_regular_user_true_for_root_despite_uid_zero() -> None:
entry = PasswdEntry(name="root", uid=0, home="/root", shell="/bin/bash") # noqa: B604 entry = PasswdEntry(name="root", uid=0, home="/root", shell="/bin/bash")
assert entry.is_regular_user() is True assert entry.is_regular_user() is True
def test_is_regular_user_false_for_root_with_disabled_shell() -> None: def test_is_regular_user_false_for_root_with_disabled_shell() -> None:
entry = PasswdEntry(name="root", uid=0, home="/root", shell="/usr/sbin/nologin") # noqa: B604 entry = PasswdEntry(name="root", uid=0, home="/root", shell="/usr/sbin/nologin")
assert entry.is_regular_user() is False assert entry.is_regular_user() is False
def test_is_regular_user_false_for_disabled_shell_despite_high_uid() -> None: def test_is_regular_user_false_for_disabled_shell_despite_high_uid() -> None:
entry = PasswdEntry( entry = PasswdEntry(
name="nobody", name="nobody", uid=65534, home="/nonexistent", shell="/usr/sbin/false"
uid=65534,
home="/nonexistent",
shell="/usr/sbin/false", # noqa: B604
) )
assert entry.is_regular_user() is False assert entry.is_regular_user() is False
def test_is_regular_user_honors_custom_uid_min() -> None: def test_is_regular_user_honors_custom_uid_min() -> None:
entry = PasswdEntry(name="svc", uid=500, home="/home/svc", shell="/bin/bash") # noqa: B604 entry = PasswdEntry(name="svc", uid=500, home="/home/svc", shell="/bin/bash")
assert entry.is_regular_user(uid_min=1000) is False assert entry.is_regular_user(uid_min=1000) is False
assert entry.is_regular_user(uid_min=100) is True assert entry.is_regular_user(uid_min=100) is True
+248 -4
View File
@@ -1,5 +1,5 @@
[tox] [tox]
env_list = py313,py314,coverage,packaging,packaging-amd64,packaging-arm64,publish,module-python,module-apt,module-yum,module-apk,module-java,module-certificates,module-php,module-npm,module-rust,module-go,module-ruby,module-nuget,config-url env_list = py313,py314,coverage,packaging,packaging-amd64,packaging-arm64,publish,module-python,module-python-http,module-apt,module-apt-http,module-yum,module-yum-http,module-apk,module-apk-http,module-java,module-java-http,module-certificates,module-php,module-php-http,module-npm,module-npm-http,module-rust,module-rust-http,module-go,module-go-http,module-ruby,module-ruby-http,module-nuget,module-nuget-http,config-url
isolated_build = true isolated_build = true
requires = requires =
tox>=4 tox>=4
@@ -91,6 +91,7 @@ passenv =
dockerfile = {tox_root}/tests/dockerfiles/repos/pypi-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/pypi-repo.Dockerfile
expose = expose =
PYPI_REPO_PORT=8443/tcp PYPI_REPO_PORT=8443/tcp
PYPI_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/simple/idconfiguratortestpkg/ || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/simple/idconfiguratortestpkg/ || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -106,17 +107,41 @@ docker =
pypi-repo pypi-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
PYPI_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Python.yaml.j2 {env_dir}/configs/Python.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Python.yaml.j2 {env_dir}/configs/Python.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_PORT={env:PYPI_REPO_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python-alpine.Dockerfile -t id-configurator-python . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_SCHEME={env:PYPI_REPO_SCHEME} --build-arg PYPI_REPO_PORT={env:PYPI_REPO_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python-alpine.Dockerfile -t id-configurator-python .
docker rmi id-configurator-python docker rmi id-configurator-python
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_PORT={env:PYPI_REPO_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python.Dockerfile -t id-configurator-python . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_SCHEME={env:PYPI_REPO_SCHEME} --build-arg PYPI_REPO_PORT={env:PYPI_REPO_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python.Dockerfile -t id-configurator-python .
docker rmi id-configurator-python docker rmi id-configurator-python
[testenv:module-python-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
pypi-repo
allowlist_externals =
docker
description =
Same as module-python, but against a plain-HTTP index, to verify pip's
trusted-host is configured correctly for a non-HTTPS repository.
set_env =
PYPI_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Python.yaml.j2 {env_dir}/configs/Python.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_SCHEME={env:PYPI_REPO_SCHEME} --build-arg PYPI_REPO_PORT={env:PYPI_REPO_HTTP_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python-alpine.Dockerfile -t id-configurator-python-http .
docker rmi id-configurator-python-http
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg PYPI_REPO_SCHEME={env:PYPI_REPO_SCHEME} --build-arg PYPI_REPO_PORT={env:PYPI_REPO_HTTP_PORT} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Python.Dockerfile -t id-configurator-python-http .
docker rmi id-configurator-python-http
[docker:apt-repo] [docker:apt-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/apt-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/apt-repo.Dockerfile
expose = expose =
APT_REPO_PORT=8443/tcp APT_REPO_PORT=8443/tcp
APT_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/Packages || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/Packages || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -132,15 +157,37 @@ docker =
apt-repo apt-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
APT_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Apt.yaml.j2 {env_dir}/configs/Apt.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Apt.yaml.j2 {env_dir}/configs/Apt.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apt.Dockerfile -t id-configurator-apt . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apt.Dockerfile -t id-configurator-apt .
docker rmi id-configurator-apt docker rmi id-configurator-apt
[testenv:module-apt-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
apt-repo
allowlist_externals =
docker
description =
Same as module-apt, but against a plain-HTTP source, since apt's trust
model is the signed-by keyring, independent of transport scheme.
set_env =
APT_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Apt.yaml.j2 {env_dir}/configs/Apt.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apt.Dockerfile -t id-configurator-apt-http .
docker rmi id-configurator-apt-http
[docker:yum-repo] [docker:yum-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/yum-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/yum-repo.Dockerfile
expose = expose =
YUM_REPO_PORT=8443/tcp YUM_REPO_PORT=8443/tcp
YUM_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/repodata/repomd.xml || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/repodata/repomd.xml || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -156,15 +203,37 @@ docker =
yum-repo yum-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
YUM_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Yum.yaml.j2 {env_dir}/configs/Yum.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Yum.yaml.j2 {env_dir}/configs/Yum.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Yum.Dockerfile -t id-configurator-yum . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Yum.Dockerfile -t id-configurator-yum .
docker rmi id-configurator-yum docker rmi id-configurator-yum
[testenv:module-yum-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
yum-repo
allowlist_externals =
docker
description =
Same as module-yum, but against a plain-HTTP repository, since yum/dnf's
trust model is the imported GPG key, independent of transport scheme.
set_env =
YUM_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Yum.yaml.j2 {env_dir}/configs/Yum.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Yum.Dockerfile -t id-configurator-yum-http .
docker rmi id-configurator-yum-http
[docker:apk-repo] [docker:apk-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/apk-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/apk-repo.Dockerfile
expose = expose =
APK_REPO_PORT=8443/tcp APK_REPO_PORT=8443/tcp
APK_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/APKINDEX.tar.gz || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/APKINDEX.tar.gz || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -189,15 +258,39 @@ description =
native platform keeps them in agreement everywhere (including under native platform keeps them in agreement everywhere (including under
QEMU emulation on non-amd64 hosts); CI runners are amd64, so this still QEMU emulation on non-amd64 hosts); CI runners are amd64, so this still
matches the other envs there. matches the other envs there.
set_env =
APK_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Apk.yaml.j2 {env_dir}/configs/Apk.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Apk.yaml.j2 {env_dir}/configs/Apk.yaml
docker buildx build --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apk.Dockerfile -t id-configurator-apk . docker buildx build --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apk.Dockerfile -t id-configurator-apk .
docker rmi id-configurator-apk docker rmi id-configurator-apk
[testenv:module-apk-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
apk-repo
allowlist_externals =
docker
description =
Same as module-apk, but against a plain-HTTP repository, since apk's
trust model is the signed index, independent of transport scheme. See
module-apk's own description for why this isn't pinned to --platform
linux/amd64 either.
set_env =
APK_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Apk.yaml.j2 {env_dir}/configs/Apk.yaml
docker buildx build --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Apk.Dockerfile -t id-configurator-apk-http .
docker rmi id-configurator-apk-http
[docker:java-repo] [docker:java-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/java-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/java-repo.Dockerfile
expose = expose =
JAVA_REPO_PORT=8443/tcp JAVA_REPO_PORT=8443/tcp
JAVA_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/org/interdiode/idconfiguratortestpkg/0.0.1/idconfiguratortestpkg-0.0.1.pom || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/org/interdiode/idconfiguratortestpkg/0.0.1/idconfiguratortestpkg-0.0.1.pom || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -213,11 +306,33 @@ docker =
java-repo java-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
JAVA_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Java.yaml.j2 {env_dir}/configs/Java.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Java.yaml.j2 {env_dir}/configs/Java.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg JAVA_REPO_SCHEME={env:JAVA_REPO_SCHEME} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java .
docker rmi id-configurator-java docker rmi id-configurator-java
[testenv:module-java-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
java-repo
allowlist_externals =
docker
description =
Same as module-java, but against a plain-HTTP repository. Leiningen is
skipped here (see Java.Dockerfile): it refuses any non-HTTPS repository
unconditionally, with no configuration able to change that.
set_env =
JAVA_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Java.yaml.j2 {env_dir}/configs/Java.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg JAVA_REPO_SCHEME={env:JAVA_REPO_SCHEME} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java-http .
docker rmi id-configurator-java-http
[docker:certificate-repo] [docker:certificate-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/certificate-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/certificate-repo.Dockerfile
expose = expose =
@@ -251,6 +366,7 @@ commands =
dockerfile = {tox_root}/tests/dockerfiles/repos/php-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/php-repo.Dockerfile
expose = expose =
PHP_REPO_PORT=8443/tcp PHP_REPO_PORT=8443/tcp
PHP_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/packages.json || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/packages.json || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -266,15 +382,37 @@ docker =
php-repo php-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
PHP_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Php.yaml.j2 {env_dir}/configs/Php.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Php.yaml.j2 {env_dir}/configs/Php.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Php.Dockerfile -t id-configurator-php . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Php.Dockerfile -t id-configurator-php .
docker rmi id-configurator-php docker rmi id-configurator-php
[testenv:module-php-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
php-repo
allowlist_externals =
docker
description =
Same as module-php, but against a plain-HTTP repository, to verify
secure-http is disabled (and packagist.org too) for a non-HTTPS source.
set_env =
PHP_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Php.yaml.j2 {env_dir}/configs/Php.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Php.Dockerfile -t id-configurator-php-http .
docker rmi id-configurator-php-http
[docker:npm-repo] [docker:npm-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/npm-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/npm-repo.Dockerfile
expose = expose =
NPM_REPO_PORT=8443/tcp NPM_REPO_PORT=8443/tcp
NPM_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/idconfiguratortestpkg || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/idconfiguratortestpkg || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -290,15 +428,36 @@ docker =
npm-repo npm-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
NPM_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Npm.yaml.j2 {env_dir}/configs/Npm.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Npm.yaml.j2 {env_dir}/configs/Npm.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Npm.Dockerfile -t id-configurator-npm . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Npm.Dockerfile -t id-configurator-npm .
docker rmi id-configurator-npm docker rmi id-configurator-npm
[testenv:module-npm-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
npm-repo
allowlist_externals =
docker
description =
Same as module-npm, but against a plain-HTTP registry.
set_env =
NPM_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Npm.yaml.j2 {env_dir}/configs/Npm.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Npm.Dockerfile -t id-configurator-npm-http .
docker rmi id-configurator-npm-http
[docker:cargo-repo] [docker:cargo-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/cargo-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/cargo-repo.Dockerfile
expose = expose =
CARGO_REPO_PORT=8443/tcp CARGO_REPO_PORT=8443/tcp
CARGO_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/config.json || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/config.json || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -314,15 +473,36 @@ docker =
cargo-repo cargo-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
CARGO_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Rust.yaml.j2 {env_dir}/configs/Rust.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Rust.yaml.j2 {env_dir}/configs/Rust.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Rust.Dockerfile -t id-configurator-rust . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Rust.Dockerfile -t id-configurator-rust .
docker rmi id-configurator-rust docker rmi id-configurator-rust
[testenv:module-rust-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
cargo-repo
allowlist_externals =
docker
description =
Same as module-rust, but against a plain-HTTP sparse registry.
set_env =
CARGO_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Rust.yaml.j2 {env_dir}/configs/Rust.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Rust.Dockerfile -t id-configurator-rust-http .
docker rmi id-configurator-rust-http
[docker:go-repo] [docker:go-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/go-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/go-repo.Dockerfile
expose = expose =
GO_REPO_PORT=8443/tcp GO_REPO_PORT=8443/tcp
GO_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/example.invalid/idconfiguratortestpkg/@v/list || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/example.invalid/idconfiguratortestpkg/@v/list || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -338,15 +518,37 @@ docker =
go-repo go-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
GO_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Go.yaml.j2 {env_dir}/configs/Go.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Go.yaml.j2 {env_dir}/configs/Go.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Go.Dockerfile -t id-configurator-go . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Go.Dockerfile -t id-configurator-go .
docker rmi id-configurator-go docker rmi id-configurator-go
[testenv:module-go-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
go-repo
allowlist_externals =
docker
description =
Same as module-go, but against a plain-HTTP module proxy, to verify
GOINSECURE is configured correctly for a non-HTTPS GOPROXY.
set_env =
GO_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Go.yaml.j2 {env_dir}/configs/Go.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Go.Dockerfile -t id-configurator-go-http .
docker rmi id-configurator-go-http
[docker:ruby-repo] [docker:ruby-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/ruby-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/ruby-repo.Dockerfile
expose = expose =
RUBY_REPO_PORT=8443/tcp RUBY_REPO_PORT=8443/tcp
RUBY_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/versions || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/versions || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -362,15 +564,36 @@ docker =
ruby-repo ruby-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
RUBY_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Ruby.yaml.j2 {env_dir}/configs/Ruby.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/Ruby.yaml.j2 {env_dir}/configs/Ruby.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Ruby.Dockerfile -t id-configurator-ruby . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Ruby.Dockerfile -t id-configurator-ruby .
docker rmi id-configurator-ruby docker rmi id-configurator-ruby
[testenv:module-ruby-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
ruby-repo
allowlist_externals =
docker
description =
Same as module-ruby, but against a plain-HTTP gem source.
set_env =
RUBY_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/Ruby.yaml.j2 {env_dir}/configs/Ruby.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Ruby.Dockerfile -t id-configurator-ruby-http .
docker rmi id-configurator-ruby-http
[docker:nuget-repo] [docker:nuget-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/nuget-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/nuget-repo.Dockerfile
expose = expose =
NUGET_REPO_PORT=8443/tcp NUGET_REPO_PORT=8443/tcp
NUGET_REPO_HTTP_PORT=8080/tcp
healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/index.json || exit 1 healthcheck_cmd = wget --no-check-certificate -qO- https://127.0.0.1:8443/index.json || exit 1
healthcheck_interval = 1 healthcheck_interval = 1
healthcheck_retries = 30 healthcheck_retries = 30
@@ -386,11 +609,32 @@ docker =
nuget-repo nuget-repo
allowlist_externals = allowlist_externals =
docker docker
set_env =
NUGET_REPO_SCHEME = https
commands = commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/NuGet.yaml.j2 {env_dir}/configs/NuGet.yaml python3 -m id_tools test render-config {tox_root}/tests/configs/NuGet.yaml.j2 {env_dir}/configs/NuGet.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/NuGet.Dockerfile -t id-configurator-nuget . docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/NuGet.Dockerfile -t id-configurator-nuget .
docker rmi id-configurator-nuget docker rmi id-configurator-nuget
[testenv:module-nuget-http]
package = skip
labels = end-to-end
change_dir = {tox_root}
dependency_groups = dev
docker =
nuget-repo
allowlist_externals =
docker
description =
Same as module-nuget, but against a plain-HTTP feed, to verify
allowInsecureConnections is configured correctly for a non-HTTPS source.
set_env =
NUGET_REPO_SCHEME = http
commands =
python3 -m id_tools test render-config {tox_root}/tests/configs/NuGet.yaml.j2 {env_dir}/configs/NuGet.yaml
docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/NuGet.Dockerfile -t id-configurator-nuget-http .
docker rmi id-configurator-nuget-http
[docker:config-url-repo] [docker:config-url-repo]
dockerfile = {tox_root}/tests/dockerfiles/repos/config-url-repo.Dockerfile dockerfile = {tox_root}/tests/dockerfiles/repos/config-url-repo.Dockerfile
expose = expose =
Generated
+258 -253
View File
@@ -1,51 +1,51 @@
requires-python = ">=3.13"
revision = 3
version = 1 version = 1
revision = 3
requires-python = ">=3.13"
[[package]] [[package]]
name = "annotated-doc" name = "annotated-doc"
sdist = {url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.0.5" version = "0.0.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z"} { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" },
] ]
[[package]] [[package]]
name = "annotated-types" name = "annotated-types"
sdist = {url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.8.0" version = "0.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z"} { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" },
] ]
[[package]] [[package]]
name = "cachetools" name = "cachetools"
sdist = {url = "https://files.pythonhosted.org/packages/29/2c/3f18755527b03ca9ff6be724bd5370cb777c76a87f17301377cf04a4729b/cachetools-7.2.0.tar.gz", hash = "sha256:bcac1a1b8da6909994a2957238a57b8140dab7c5c5c69a43669654fe87a33c1d", size = 41129, upload-time = "2026-09-16T20:48:27.209Z"}
source = {registry = "https://pypi.org/simple"}
version = "7.2.0" version = "7.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/29/2c/3f18755527b03ca9ff6be724bd5370cb777c76a87f17301377cf04a4729b/cachetools-7.2.0.tar.gz", hash = "sha256:bcac1a1b8da6909994a2957238a57b8140dab7c5c5c69a43669654fe87a33c1d", size = 41129, upload-time = "2026-09-16T20:48:27.209Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/cc/bb/1c6e7a89b11da19f137e4ef5a5c5fe47a5bb686449ba343944ccf8fb34b2/cachetools-7.2.0-py3-none-any.whl", hash = "sha256:3045213f186b89fdd95d94441354c4bd87c570b7a38d8c3fd1d9dd37f6dc90d8", size = 16918, upload-time = "2026-09-16T20:48:25.575Z"} { url = "https://files.pythonhosted.org/packages/cc/bb/1c6e7a89b11da19f137e4ef5a5c5fe47a5bb686449ba343944ccf8fb34b2/cachetools-7.2.0-py3-none-any.whl", hash = "sha256:3045213f186b89fdd95d94441354c4bd87c570b7a38d8c3fd1d9dd37f6dc90d8", size = 16918, upload-time = "2026-09-16T20:48:25.575Z" },
] ]
[[package]] [[package]]
name = "certifi" name = "certifi"
sdist = {url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z"}
source = {registry = "https://pypi.org/simple"}
version = "2026.7.22" version = "2026.7.22"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z"} { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "pycparser", marker = "implementation_name != 'PyPy'"}
]
name = "cffi" name = "cffi"
sdist = {url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z"}
source = {registry = "https://pypi.org/simple"}
version = "2.1.1" version = "2.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
{ url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
@@ -108,14 +108,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" },
{ url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" },
{ url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" },
{url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z"} { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
] ]
[[package]] [[package]]
name = "charset-normalizer" name = "charset-normalizer"
sdist = {url = "https://files.pythonhosted.org/packages/33/1c/f41d4e74c28ab327ff3acd36053f7ea506c55872d7a90b0fa71aa3ab0c89/charset_normalizer-3.5.2.tar.gz", hash = "sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef", size = 172659, upload-time = "2026-09-30T04:39:23.398Z"}
source = {registry = "https://pypi.org/simple"}
version = "3.5.2" version = "3.5.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/33/1c/f41d4e74c28ab327ff3acd36053f7ea506c55872d7a90b0fa71aa3ab0c89/charset_normalizer-3.5.2.tar.gz", hash = "sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef", size = 172659, upload-time = "2026-09-30T04:39:23.398Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/34/68292d68512768591aaff07c59bb53ee31341c87759433a859c4641a50c2/charset_normalizer-3.5.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5", size = 225946, upload-time = "2026-09-30T04:35:55.313Z" }, { url = "https://files.pythonhosted.org/packages/c5/34/68292d68512768591aaff07c59bb53ee31341c87759433a859c4641a50c2/charset_normalizer-3.5.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5", size = 225946, upload-time = "2026-09-30T04:35:55.313Z" },
{ url = "https://files.pythonhosted.org/packages/e3/80/bee0b01b90ccd5322ae1d0abb33fab1bd95b7c2eadaf02aeccf22e04ee83/charset_normalizer-3.5.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e", size = 238619, upload-time = "2026-09-30T04:35:56.863Z" }, { url = "https://files.pythonhosted.org/packages/e3/80/bee0b01b90ccd5322ae1d0abb33fab1bd95b7c2eadaf02aeccf22e04ee83/charset_normalizer-3.5.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e", size = 238619, upload-time = "2026-09-30T04:35:56.863Z" },
@@ -223,23 +223,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/31/68/fdec18a343f5fb3f310588dd478b09ac4799e0b187dbade3a8cd776f03ef/charset_normalizer-3.5.2-cp37-abi3-win32.whl", hash = "sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf", size = 174499, upload-time = "2026-09-30T04:38:47.999Z" }, { url = "https://files.pythonhosted.org/packages/31/68/fdec18a343f5fb3f310588dd478b09ac4799e0b187dbade3a8cd776f03ef/charset_normalizer-3.5.2-cp37-abi3-win32.whl", hash = "sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf", size = 174499, upload-time = "2026-09-30T04:38:47.999Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8a/b618149cc5207943a0242068d7a27897f56a62947b5a039085f2a22029f8/charset_normalizer-3.5.2-cp37-abi3-win_amd64.whl", hash = "sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036", size = 200092, upload-time = "2026-09-30T04:38:49.707Z" }, { url = "https://files.pythonhosted.org/packages/9d/8a/b618149cc5207943a0242068d7a27897f56a62947b5a039085f2a22029f8/charset_normalizer-3.5.2-cp37-abi3-win_amd64.whl", hash = "sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036", size = 200092, upload-time = "2026-09-30T04:38:49.707Z" },
{ url = "https://files.pythonhosted.org/packages/03/cf/4c66866fa9e2b1c78e3c911516d1de497a677b7ac60f1eceda74ce777ca3/charset_normalizer-3.5.2-cp37-abi3-win_arm64.whl", hash = "sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e", size = 294363, upload-time = "2026-09-30T04:38:51.312Z" }, { url = "https://files.pythonhosted.org/packages/03/cf/4c66866fa9e2b1c78e3c911516d1de497a677b7ac60f1eceda74ce777ca3/charset_normalizer-3.5.2-cp37-abi3-win_arm64.whl", hash = "sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e", size = 294363, upload-time = "2026-09-30T04:38:51.312Z" },
{url = "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685", size = 68872, upload-time = "2026-09-30T04:39:21.828Z"} { url = "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685", size = 68872, upload-time = "2026-09-30T04:39:21.828Z" },
] ]
[[package]] [[package]]
name = "colorama" name = "colorama"
sdist = {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.4.6" version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z"} { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
] ]
[[package]] [[package]]
name = "coverage" name = "coverage"
sdist = {url = "https://files.pythonhosted.org/packages/2f/55/d1eaf3e73781174340a00dc1ba2aee8a65f82fadb18e2797b192b6b3925b/coverage-7.16.2.tar.gz", hash = "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa", size = 971999, upload-time = "2026-09-27T12:29:01.118Z"}
source = {registry = "https://pypi.org/simple"}
version = "7.16.2" version = "7.16.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2f/55/d1eaf3e73781174340a00dc1ba2aee8a65f82fadb18e2797b192b6b3925b/coverage-7.16.2.tar.gz", hash = "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa", size = 971999, upload-time = "2026-09-27T12:29:01.118Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/f6/8eb4f220ef24f84fb27d852d4f9bf83e0c73ec1a4a08dd9a87e3f4529739/coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9", size = 223993, upload-time = "2026-09-27T12:26:40.164Z" }, { url = "https://files.pythonhosted.org/packages/f0/f6/8eb4f220ef24f84fb27d852d4f9bf83e0c73ec1a4a08dd9a87e3f4529739/coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9", size = 223993, upload-time = "2026-09-27T12:26:40.164Z" },
{ url = "https://files.pythonhosted.org/packages/40/23/d4bbaf0c154e0b0c2b5264890dbf6ef098dcb50ec8f2469be9490d191660/coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b", size = 224368, upload-time = "2026-09-27T12:26:41.762Z" }, { url = "https://files.pythonhosted.org/packages/40/23/d4bbaf0c154e0b0c2b5264890dbf6ef098dcb50ec8f2469be9490d191660/coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b", size = 224368, upload-time = "2026-09-27T12:26:41.762Z" },
@@ -316,17 +316,17 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/28/6c/f08e8ee4293e6434035424180bef4d45e028e8ecc006c61bf9453e74405e/coverage-7.16.2-cp315-cp315t-win32.whl", hash = "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35", size = 226583, upload-time = "2026-09-27T12:28:53.33Z" }, { url = "https://files.pythonhosted.org/packages/28/6c/f08e8ee4293e6434035424180bef4d45e028e8ecc006c61bf9453e74405e/coverage-7.16.2-cp315-cp315t-win32.whl", hash = "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35", size = 226583, upload-time = "2026-09-27T12:28:53.33Z" },
{ url = "https://files.pythonhosted.org/packages/f7/fd/3f939c2847f4a72c20cff8b1ac33da78ea91a2d38d9b43336e60db719103/coverage-7.16.2-cp315-cp315t-win_amd64.whl", hash = "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5", size = 227374, upload-time = "2026-09-27T12:28:55.158Z" }, { url = "https://files.pythonhosted.org/packages/f7/fd/3f939c2847f4a72c20cff8b1ac33da78ea91a2d38d9b43336e60db719103/coverage-7.16.2-cp315-cp315t-win_amd64.whl", hash = "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5", size = 227374, upload-time = "2026-09-27T12:28:55.158Z" },
{ url = "https://files.pythonhosted.org/packages/5a/35/b98cdc354c952402132e675a87f2cc3227fb68f959c84aaa491fbe15933d/coverage-7.16.2-cp315-cp315t-win_arm64.whl", hash = "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8", size = 227064, upload-time = "2026-09-27T12:28:57.075Z" }, { url = "https://files.pythonhosted.org/packages/5a/35/b98cdc354c952402132e675a87f2cc3227fb68f959c84aaa491fbe15933d/coverage-7.16.2-cp315-cp315t-win_arm64.whl", hash = "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8", size = 227064, upload-time = "2026-09-27T12:28:57.075Z" },
{url = "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", hash = "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f", size = 215754, upload-time = "2026-09-27T12:28:59.075Z"} { url = "https://files.pythonhosted.org/packages/3f/0c/7a64e1ac90541a8edf50daef0914848011fb057a5bf55284a4811e21939a/coverage-7.16.2-py3-none-any.whl", hash = "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f", size = 215754, upload-time = "2026-09-27T12:28:59.075Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "cffi", marker = "platform_python_implementation != 'PyPy'"}
]
name = "cryptography" name = "cryptography"
sdist = {url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z"}
source = {registry = "https://pypi.org/simple"}
version = "50.0.2" version = "50.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" }, { url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
{ url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" }, { url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
@@ -379,52 +379,52 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" }, { url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
{ url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" }, { url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
{ url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" }, { url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
{url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z"} { url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
] ]
[[package]] [[package]]
name = "distlib" name = "distlib"
sdist = {url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.4.3" version = "0.4.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z"} { url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z" },
] ]
[[package]] [[package]]
name = "docker"
version = "7.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "requests" }, { name = "requests" },
{name = "urllib3"} { name = "urllib3" },
] ]
name = "docker"
sdist = { url = "https://files.pythonhosted.org/packages/88/7f/731ff914b0255d3d065f45fd4e626d4b8c95dbcbaada049f337a6ac16410/docker-7.2.0.tar.gz", hash = "sha256:cebb93773d334f778e023a7ee352a8d6e13ab1bd3b863a4d4a59dec897df43ac", size = 118731, upload-time = "2026-07-09T14:53:46.39Z" } sdist = { url = "https://files.pythonhosted.org/packages/88/7f/731ff914b0255d3d065f45fd4e626d4b8c95dbcbaada049f337a6ac16410/docker-7.2.0.tar.gz", hash = "sha256:cebb93773d334f778e023a7ee352a8d6e13ab1bd3b863a4d4a59dec897df43ac", size = 118731, upload-time = "2026-07-09T14:53:46.39Z" }
source = {registry = "https://pypi.org/simple"}
version = "7.2.0"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl", hash = "sha256:a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f", size = 148775, upload-time = "2026-07-09T14:53:45.224Z"} { url = "https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl", hash = "sha256:a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f", size = 148775, upload-time = "2026-07-09T14:53:45.224Z" },
] ]
[[package]] [[package]]
name = "filelock" name = "filelock"
sdist = {url = "https://files.pythonhosted.org/packages/4b/51/a182494d1d8dde1240bff84dda57d48165d982e59582ce8f167e8e3d7628/filelock-4.0.10.tar.gz", hash = "sha256:00d6a81f976a6332551c2c10f39e12b4abb7e01c64d4c497b81a615bc9186f1f", size = 252487, upload-time = "2026-10-03T23:40:48.884Z"} version = "4.0.11"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
version = "4.0.10" sdist = { url = "https://files.pythonhosted.org/packages/d8/9c/925c4f62920e4200d573d2bfe5cfd1b993134c3aee5f9bd3b7c55e20297e/filelock-4.0.11.tar.gz", hash = "sha256:9dc453a1edd25626a6fc97c2cb1bd9f7e46345843d40b3181c071761fb9a5f7a", size = 253844, upload-time = "2026-10-05T00:57:32.47Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/5b/2a/4b1ea3f2180be251a5cff1e70c8a5b7994ffa5c8eab8937901f8a9db58fe/filelock-4.0.10-py3-none-any.whl", hash = "sha256:698048d158dc3c9e18cc5e88b3efdd4bd0e77d76686b22adb4511cc4b2c2f0db", size = 111193, upload-time = "2026-10-03T23:40:47.334Z"} { url = "https://files.pythonhosted.org/packages/4a/4d/2983a9fc0a412a09151299d1ec9eb7511e3d577f7213e19395fabebf9db6/filelock-4.0.11-py3-none-any.whl", hash = "sha256:1cbdc4ace7fb11d2f6d073f5c3d91537a23b3d58d2bf2fdffc12c6d717a589c6", size = 111688, upload-time = "2026-10-05T00:57:30.896Z" },
] ]
[[package]] [[package]]
name = "id-configurator"
version = "2026.10.9"
source = { editable = "." }
dependencies = [ dependencies = [
{ name = "cryptography" }, { name = "cryptography" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pyjks" }, { name = "pyjks" },
{ name = "pyyaml" }, { name = "pyyaml" },
{name = "typer"} { name = "typer" },
] ]
name = "id-configurator"
source = {editable = "."}
version = "2026.10.3"
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
@@ -432,11 +432,11 @@ dev = [
{ name = "tox" }, { name = "tox" },
{ name = "tox-docker" }, { name = "tox-docker" },
{ name = "tox-uv" }, { name = "tox-uv" },
{name = "typer"} { name = "typer" },
] ]
test = [ test = [
{ name = "pytest" }, { name = "pytest" },
{name = "pytest-cov"} { name = "pytest-cov" },
] ]
[package.metadata] [package.metadata]
@@ -445,7 +445,7 @@ requires-dist = [
{ name = "pydantic", specifier = ">=2.13.4" }, { name = "pydantic", specifier = ">=2.13.4" },
{ name = "pyjks", specifier = ">=20.0.0" }, { name = "pyjks", specifier = ">=20.0.0" },
{ name = "pyyaml", specifier = ">=6.0.3" }, { name = "pyyaml", specifier = ">=6.0.3" },
{name = "typer", specifier = ">=0.27.1"} { name = "typer", specifier = ">=0.27.1" },
] ]
[package.metadata.requires-dev] [package.metadata.requires-dev]
@@ -454,69 +454,69 @@ dev = [
{ name = "tox", specifier = ">=4.60.1" }, { name = "tox", specifier = ">=4.60.1" },
{ name = "tox-docker", specifier = ">=5.0.0" }, { name = "tox-docker", specifier = ">=5.0.0" },
{ name = "tox-uv", specifier = ">=1.36.0" }, { name = "tox-uv", specifier = ">=1.36.0" },
{name = "typer", specifier = ">=0.27.1"} { name = "typer", specifier = ">=0.27.1" },
] ]
test = [ test = [
{ name = "pytest", specifier = ">=9.1.1" }, { name = "pytest", specifier = ">=9.1.1" },
{name = "pytest-cov", specifier = ">=7.1.0"} { name = "pytest-cov", specifier = ">=7.1.0" },
] ]
[[package]] [[package]]
name = "idna" name = "idna"
sdist = {url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z"}
source = {registry = "https://pypi.org/simple"}
version = "3.20" version = "3.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z"} { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" },
] ]
[[package]] [[package]]
name = "iniconfig" name = "iniconfig"
sdist = {url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z"}
source = {registry = "https://pypi.org/simple"}
version = "2.3.0" version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z"} { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
] ]
[[package]] [[package]]
name = "javaobj-py3" name = "javaobj-py3"
sdist = {url = "https://files.pythonhosted.org/packages/6e/a4/a0140d76c62effc710aa180c657792236d10e43d3f993b71bafc3036fea1/javaobj_py3-0.6.1.tar.gz", hash = "sha256:088ff086c6aa2912f342754c43b0ca3a504fb869bb5e6f13395131e87744ef3c", size = 113555, upload-time = "2026-08-12T16:51:05.39Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.6.1" version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6e/a4/a0140d76c62effc710aa180c657792236d10e43d3f993b71bafc3036fea1/javaobj_py3-0.6.1.tar.gz", hash = "sha256:088ff086c6aa2912f342754c43b0ca3a504fb869bb5e6f13395131e87744ef3c", size = 113555, upload-time = "2026-08-12T16:51:05.39Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/20/97/62180aa88dc4719116128a3c2fe22a962cbf69256d517f376565a38467a7/javaobj_py3-0.6.1-py2.py3-none-any.whl", hash = "sha256:da97c61cb050bad37cca00523b1a617cc26ed2b698b6322cbc118820a4f52b30", size = 94957, upload-time = "2026-08-12T16:51:04.022Z"} { url = "https://files.pythonhosted.org/packages/20/97/62180aa88dc4719116128a3c2fe22a962cbf69256d517f376565a38467a7/javaobj_py3-0.6.1-py2.py3-none-any.whl", hash = "sha256:da97c61cb050bad37cca00523b1a617cc26ed2b698b6322cbc118820a4f52b30", size = 94957, upload-time = "2026-08-12T16:51:04.022Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "markupsafe"}
]
name = "jinja2" name = "jinja2"
sdist = {url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z"}
source = {registry = "https://pypi.org/simple"}
version = "3.1.6" version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z"} { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "mdurl"}
]
name = "markdown-it-py" name = "markdown-it-py"
sdist = {url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z"}
source = {registry = "https://pypi.org/simple"}
version = "4.2.0" version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z"} { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
] ]
[[package]] [[package]]
name = "markupsafe" name = "markupsafe"
sdist = {url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z"}
source = {registry = "https://pypi.org/simple"}
version = "3.0.4" version = "3.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" }, { url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" },
{ url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" }, { url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" },
@@ -607,128 +607,133 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/93/2cbd5628435afb6f541bbaced4bce0c2edac4b09a142e6e928b8b0da9858/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894", size = 23712, upload-time = "2026-10-02T23:06:51.759Z" }, { url = "https://files.pythonhosted.org/packages/d7/93/2cbd5628435afb6f541bbaced4bce0c2edac4b09a142e6e928b8b0da9858/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894", size = 23712, upload-time = "2026-10-02T23:06:51.759Z" },
{ url = "https://files.pythonhosted.org/packages/81/99/157e10966b033b363aeda5263e82596ee232a0b1d082fdbf90aa417ff083/markupsafe-3.0.4-cp315-cp315t-win32.whl", hash = "sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78", size = 14300, upload-time = "2026-10-02T23:06:53.241Z" }, { url = "https://files.pythonhosted.org/packages/81/99/157e10966b033b363aeda5263e82596ee232a0b1d082fdbf90aa417ff083/markupsafe-3.0.4-cp315-cp315t-win32.whl", hash = "sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78", size = 14300, upload-time = "2026-10-02T23:06:53.241Z" },
{ url = "https://files.pythonhosted.org/packages/33/05/55884815414c9706a23deca150b72c25a62109e65b0b6ce232077802c719/markupsafe-3.0.4-cp315-cp315t-win_amd64.whl", hash = "sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c", size = 14522, upload-time = "2026-10-02T23:06:54.729Z" }, { url = "https://files.pythonhosted.org/packages/33/05/55884815414c9706a23deca150b72c25a62109e65b0b6ce232077802c719/markupsafe-3.0.4-cp315-cp315t-win_amd64.whl", hash = "sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c", size = 14522, upload-time = "2026-10-02T23:06:54.729Z" },
{url = "https://files.pythonhosted.org/packages/92/f9/ecbde7149e95b8a0f18e16d5d747f7dc06049d5da2e4f77f6f5e4a1f46a8/markupsafe-3.0.4-cp315-cp315t-win_arm64.whl", hash = "sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba", size = 14417, upload-time = "2026-10-02T23:06:56.246Z"} { url = "https://files.pythonhosted.org/packages/92/f9/ecbde7149e95b8a0f18e16d5d747f7dc06049d5da2e4f77f6f5e4a1f46a8/markupsafe-3.0.4-cp315-cp315t-win_arm64.whl", hash = "sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba", size = 14417, upload-time = "2026-10-02T23:06:56.246Z" },
] ]
[[package]] [[package]]
name = "mdurl" name = "mdurl"
sdist = {url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.1.2" version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [ 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"} { 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]] [[package]]
name = "packaging" name = "packaging"
sdist = {url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z"}
source = {registry = "https://pypi.org/simple"}
version = "26.3" version = "26.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z"} { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
] ]
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
sdist = {url = "https://files.pythonhosted.org/packages/42/23/4a86fc741c38c5b69792a4ef954b281afa69bea9f083f881de1b0d23bc07/platformdirs-4.12.3.tar.gz", hash = "sha256:427fc0bb321ae0c5b037fa03238ca74820437be162e78b4848c4d4055b9b766c", size = 59538, upload-time = "2026-10-03T23:40:46.595Z"}
source = {registry = "https://pypi.org/simple"}
version = "4.12.3" version = "4.12.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/42/23/4a86fc741c38c5b69792a4ef954b281afa69bea9f083f881de1b0d23bc07/platformdirs-4.12.3.tar.gz", hash = "sha256:427fc0bb321ae0c5b037fa03238ca74820437be162e78b4848c4d4055b9b766c", size = 59538, upload-time = "2026-10-03T23:40:46.595Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/c5/9b/6ce1ead737fe496611bda600c263b9a11ae7bd8f41bb13f9bd7e0a2c37a4/platformdirs-4.12.3-py3-none-any.whl", hash = "sha256:080f3b39423b5abfca9a23d84c4e9795f54d395cd8459867a8ded44084fcd5f8", size = 32493, upload-time = "2026-10-03T23:40:45.104Z"} { url = "https://files.pythonhosted.org/packages/c5/9b/6ce1ead737fe496611bda600c263b9a11ae7bd8f41bb13f9bd7e0a2c37a4/platformdirs-4.12.3-py3-none-any.whl", hash = "sha256:080f3b39423b5abfca9a23d84c4e9795f54d395cd8459867a8ded44084fcd5f8", size = 32493, upload-time = "2026-10-03T23:40:45.104Z" },
] ]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
sdist = {url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z"}
source = {registry = "https://pypi.org/simple"}
version = "1.6.0" version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z"} { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
] ]
[[package]] [[package]]
name = "pyasn1" name = "pyasn1"
sdist = {url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.6.4" version = "0.6.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z"} { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "pyasn1"}
]
name = "pyasn1-modules" name = "pyasn1-modules"
sdist = {url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.4.2" version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyasn1" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z"} { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
] ]
[[package]] [[package]]
name = "pycparser" name = "pycparser"
sdist = {url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z"}
source = {registry = "https://pypi.org/simple"}
version = "3.0" version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z"} { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
] ]
[[package]] [[package]]
name = "pycryptodomex" name = "pycryptodomex"
sdist = {url = "https://files.pythonhosted.org/packages/c9/85/e24bf90972a30b0fcd16c73009add1d7d7cd9140c2498a68252028899e41/pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da", size = 4922157, upload-time = "2025-05-17T17:23:41.434Z"} version = "3.24.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
version = "3.23.0" sdist = { url = "https://files.pythonhosted.org/packages/4c/25/214ea825a9031f5af2c8b2506ee16701a2560d4712165dd00098dd527bcb/pycryptodomex-3.24.0.tar.gz", hash = "sha256:0428f19f13452c6b89bbaf2c530f84f369873811dfa77f0cee0da4f40fb0474f", size = 4933136, upload-time = "2026-10-04T17:41:08.988Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/2e/00/10edb04777069a42490a38c137099d4b17ba6e36a4e6e28bdc7470e9e853/pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886", size = 2498764, upload-time = "2025-05-17T17:22:21.453Z"}, { url = "https://files.pythonhosted.org/packages/0f/52/5235086483b462600958549b27683ccffbb4941ba512934efc5a14b71a39/pycryptodomex-3.24.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:abf768c285d289c4a0e0678f055663162c9d6257c79f5904fbd2692ade94da1c", size = 2475900, upload-time = "2026-10-04T17:39:26.374Z" },
{url = "https://files.pythonhosted.org/packages/6b/3f/2872a9c2d3a27eac094f9ceaa5a8a483b774ae69018040ea3240d5b11154/pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d", size = 1643012, upload-time = "2025-05-17T17:22:23.702Z"}, { url = "https://files.pythonhosted.org/packages/9e/cd/eeed4b1a1eda71a78defe2c04ff37909461f9c04cd4bde23011d4350cbfb/pycryptodomex-3.24.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c2f42f24d7112ebb93d3aed581d011c82c20101da7d7a6248145c530e99bd2eb", size = 1643488, upload-time = "2026-10-04T17:39:28.545Z" },
{url = "https://files.pythonhosted.org/packages/70/af/774c2e2b4f6570fbf6a4972161adbb183aeeaa1863bde31e8706f123bf92/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa", size = 2187643, upload-time = "2025-05-17T17:22:26.37Z"}, { url = "https://files.pythonhosted.org/packages/18/d2/22005d736858dddd3f7a5ddaf4a91dbb89292f0d9c503a9c13b1bb2ecfb8/pycryptodomex-3.24.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e42a2e9531c40315290278d9eee93ea5031eda3c83969f5709ada51a1c7491b4", size = 2194768, upload-time = "2026-10-04T17:39:31.713Z" },
{url = "https://files.pythonhosted.org/packages/de/a3/71065b24cb889d537954cedc3ae5466af00a2cabcff8e29b73be047e9a19/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8", size = 2273762, upload-time = "2025-05-17T17:22:28.313Z"}, { url = "https://files.pythonhosted.org/packages/19/c4/c350bb48207ae2a6cca02eb45a2dafc3f2d0196b5b33b556cc9b6815e8cc/pycryptodomex-3.24.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7055400f773856cd219680d15525c6fb147237534cbe5394a5f92f57f7efaed", size = 2280612, upload-time = "2026-10-04T17:39:34.828Z" },
{url = "https://files.pythonhosted.org/packages/c9/0b/ff6f43b7fbef4d302c8b981fe58467b8871902cdc3eb28896b52421422cc/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5", size = 2313012, upload-time = "2025-05-17T17:22:30.57Z"}, { url = "https://files.pythonhosted.org/packages/fe/56/539e213fbd572f8e95cdef5a46b0c327c7b2412107c3dba73913cfd20753/pycryptodomex-3.24.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e8a7463d346015b42a0fb9c9179ebb7cccfd86bf40199c6796b546ac7be0b85c", size = 2186291, upload-time = "2026-10-04T17:39:38.184Z" },
{url = "https://files.pythonhosted.org/packages/02/de/9d4772c0506ab6da10b41159493657105d3f8bb5c53615d19452afc6b315/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314", size = 2186856, upload-time = "2025-05-17T17:22:32.819Z"}, { url = "https://files.pythonhosted.org/packages/9b/f3/f8e73be610ef576c6ca98a78c8450a72f12c178bc89d3e94068e48115c97/pycryptodomex-3.24.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f96e10f0edff9d4b6b253e0a78f7c819749521d3ffe6af0139e08fcd35384405", size = 2278371, upload-time = "2026-10-04T17:39:41.539Z" },
{url = "https://files.pythonhosted.org/packages/28/ad/8b30efcd6341707a234e5eba5493700a17852ca1ac7a75daa7945fcf6427/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006", size = 2347523, upload-time = "2025-05-17T17:22:35.386Z"}, { url = "https://files.pythonhosted.org/packages/16/bd/99c89964c5017488992f412f8d645923d8ef996c7f35fc76b7fe019620e1/pycryptodomex-3.24.0-cp313-cp313t-win32.whl", hash = "sha256:3b6276fe007464502df796710fb57dcfeb813b6ccac0eabaf85100be1ad358f2", size = 1792741, upload-time = "2026-10-04T17:39:44.173Z" },
{url = "https://files.pythonhosted.org/packages/0f/02/16868e9f655b7670dbb0ac4f2844145cbc42251f916fc35c414ad2359849/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462", size = 2272825, upload-time = "2025-05-17T17:22:37.632Z"}, { url = "https://files.pythonhosted.org/packages/ca/d5/5a1e469c7eab20c21090d396342894d2f0e25d429eb4df2d9174a03cb226/pycryptodomex-3.24.0-cp313-cp313t-win_amd64.whl", hash = "sha256:88d37b28b450c06a03bd5570467c601fe8c7dbe493ca39771716f56fdbe35d6e", size = 1825623, upload-time = "2026-10-04T17:39:46.422Z" },
{url = "https://files.pythonhosted.org/packages/ca/18/4ca89ac737230b52ac8ffaca42f9c6f1fd07c81a6cd821e91af79db60632/pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328", size = 1772078, upload-time = "2025-05-17T17:22:40Z"}, { url = "https://files.pythonhosted.org/packages/1b/fb/1ca14c7e5318125aff23cd19886a62c8eb9015caa373b5834216c60260a5/pycryptodomex-3.24.0-cp313-cp313t-win_arm64.whl", hash = "sha256:e635c7ac056f2a47d79e2bf7042fb3fc6d175f47796a954388a3cec1f2e082a4", size = 1759392, upload-time = "2026-10-04T17:39:48.676Z" },
{url = "https://files.pythonhosted.org/packages/73/34/13e01c322db027682e00986873eca803f11c56ade9ba5bbf3225841ea2d4/pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708", size = 1803656, upload-time = "2025-05-17T17:22:42.139Z"}, { url = "https://files.pythonhosted.org/packages/a0/dd/8f2d18986f752ceb245d4c3aeb0b13e9ed329d64434969981f0746b5b2ff/pycryptodomex-3.24.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:f6af77f15a91bb6c16607067cfd201330cf5dfefe1c715e955be1fb06d368613", size = 2476818, upload-time = "2026-10-04T17:39:51.311Z" },
{url = "https://files.pythonhosted.org/packages/54/68/9504c8796b1805d58f4425002bcca20f12880e6fa4dc2fc9a668705c7a08/pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4", size = 1707172, upload-time = "2025-05-17T17:22:44.704Z"}, { url = "https://files.pythonhosted.org/packages/8a/64/6bc2394a249d1c7ba330d192e4bf22d7e5a8caf28ef1dd346b8b847b91b3/pycryptodomex-3.24.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:582efdf767d72c12538949b876a64b9a7af30f2cff063c06dd63ece32c4e5c1d", size = 1645659, upload-time = "2026-10-04T17:39:53.936Z" },
{url = "https://files.pythonhosted.org/packages/dd/9c/1a8f35daa39784ed8adf93a694e7e5dc15c23c741bbda06e1d45f8979e9e/pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6", size = 2499240, upload-time = "2025-05-17T17:22:46.953Z"}, { url = "https://files.pythonhosted.org/packages/46/36/f720afe32331ebf200bc37d335120f74da371969722d3c188fac757a8a71/pycryptodomex-3.24.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be79564e05ed620a63f353ea50e0d474eb598f70cda1b28c2ca8e9e2f02c139", size = 2194769, upload-time = "2026-10-04T17:39:57.357Z" },
{url = "https://files.pythonhosted.org/packages/7a/62/f5221a191a97157d240cf6643747558759126c76ee92f29a3f4aee3197a5/pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545", size = 1644042, upload-time = "2025-05-17T17:22:49.098Z"}, { url = "https://files.pythonhosted.org/packages/05/ac/d0c3b629a9e91c9dee019fa59cdf9617b84b0d11909541e1d6c79b24637c/pycryptodomex-3.24.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb393480b5845ef4acbc9f713e4c473ad08be81bc3cdb5ef017cd02bdafd36e1", size = 2280609, upload-time = "2026-10-04T17:39:59.915Z" },
{url = "https://files.pythonhosted.org/packages/8c/fd/5a054543c8988d4ed7b612721d7e78a4b9bf36bc3c5ad45ef45c22d0060e/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587", size = 2186227, upload-time = "2025-05-17T17:22:51.139Z"}, { url = "https://files.pythonhosted.org/packages/5f/0e/e64276b7f3dbe9aeabd9c80dfff10bc97740ad8e24892eb6b0e3cbdcd549/pycryptodomex-3.24.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7b8ad51baf3505dd3d94ec974855359d15769e45c58c2b89c6d4612b1f7ed94", size = 2186290, upload-time = "2026-10-04T17:40:02.856Z" },
{url = "https://files.pythonhosted.org/packages/c8/a9/8862616a85cf450d2822dbd4fff1fcaba90877907a6ff5bc2672cafe42f8/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c", size = 2272578, upload-time = "2025-05-17T17:22:53.676Z"}, { url = "https://files.pythonhosted.org/packages/42/2f/b72cc71bff456971e24e886f1824d2efef7b25fbd806eb821b107f8253e6/pycryptodomex-3.24.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7ffcedf4210d8c206fe9852ccde9f78ec9ce35e8039f0a989900ffe710e7a5db", size = 2278371, upload-time = "2026-10-04T17:40:06.135Z" },
{url = "https://files.pythonhosted.org/packages/46/9f/bda9c49a7c1842820de674ab36c79f4fbeeee03f8ff0e4f3546c3889076b/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c", size = 2312166, upload-time = "2025-05-17T17:22:56.585Z"}, { url = "https://files.pythonhosted.org/packages/70/b0/9d0f0d4d6236f986c942d2933d4a5cbc209ebac1ea7d4c74b6e306efb439/pycryptodomex-3.24.0-cp314-cp314t-win32.whl", hash = "sha256:8d6c5e01e4fb1ddd20cc24d4d8706643dd78b5279453dabcf10ba4e0683bf9b2", size = 1808717, upload-time = "2026-10-04T17:40:09.172Z" },
{url = "https://files.pythonhosted.org/packages/03/cc/870b9bf8ca92866ca0186534801cf8d20554ad2a76ca959538041b7a7cf4/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003", size = 2185467, upload-time = "2025-05-17T17:22:59.237Z"}, { url = "https://files.pythonhosted.org/packages/8f/99/55c75d5c8c0e3c966c6f639e3b2556c54b0172501f7377cff2aa570447c7/pycryptodomex-3.24.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c55e6c42f9eb43d90c15792bf69bc09cb3e13a5e55a30eecb555695a02b0d4c0", size = 1842528, upload-time = "2026-10-04T17:40:12.033Z" },
{url = "https://files.pythonhosted.org/packages/96/e3/ce9348236d8e669fea5dd82a90e86be48b9c341210f44e25443162aba187/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744", size = 2346104, upload-time = "2025-05-17T17:23:02.112Z"}, { url = "https://files.pythonhosted.org/packages/18/3d/2cdbda0eacc9875c5db5cede94389f8ca4a6a4bccbbbc74f47f8fa4f0c6c/pycryptodomex-3.24.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6a62122fb4c85befcc01ec3a1ae5b2ba81689481aba1d383b4e21e34f18900dd", size = 1777885, upload-time = "2026-10-04T17:40:14.361Z" },
{url = "https://files.pythonhosted.org/packages/a5/e9/e869bcee87beb89040263c416a8a50204f7f7a83ac11897646c9e71e0daf/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd", size = 2271038, upload-time = "2025-05-17T17:23:04.872Z"}, { url = "https://files.pythonhosted.org/packages/bc/fd/b9186cea5e823f91f40904373cff30cdaea5dfe1863b4be0ed3bf51e4db6/pycryptodomex-3.24.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ef2bd306328a8447d4d2af6037efddd67ef16630876ed3ec84babd16ca558941", size = 2476748, upload-time = "2026-10-04T17:40:17.326Z" },
{url = "https://files.pythonhosted.org/packages/8d/67/09ee8500dd22614af5fbaa51a4aee6e342b5fa8aecf0a6cb9cbf52fa6d45/pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c", size = 1771969, upload-time = "2025-05-17T17:23:07.115Z"}, { url = "https://files.pythonhosted.org/packages/65/c4/bf354a7c435da08ba9905a302dce8fac57a33b2ab1e51c78542b698b4b89/pycryptodomex-3.24.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:42c8221e3a4ca04b070cb388fca004815f6544951bfb2cc6c7958e293b7caf64", size = 1644339, upload-time = "2026-10-04T17:40:19.872Z" },
{url = "https://files.pythonhosted.org/packages/69/96/11f36f71a865dd6df03716d33bd07a67e9d20f6b8d39820470b766af323c/pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9", size = 1803124, upload-time = "2025-05-17T17:23:09.267Z"}, { url = "https://files.pythonhosted.org/packages/7b/ea/158d0ea838c92ba5d5081ce0f08e4af40d7278f7609ca007fca4d010aa4d/pycryptodomex-3.24.0-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da9996407d03684221a4e1603b70fad97d8772185e5f75addcd97fcbc4c3281f", size = 2193232, upload-time = "2026-10-04T17:40:22.957Z" },
{url = "https://files.pythonhosted.org/packages/f9/93/45c1cdcbeb182ccd2e144c693eaa097763b08b38cded279f0053ed53c553/pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51", size = 1707161, upload-time = "2025-05-17T17:23:11.414Z"} { url = "https://files.pythonhosted.org/packages/c8/9f/376b705a536c65fe41806babe5cdcfb076273a0d72dfbbb92073d3d9b10c/pycryptodomex-3.24.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:556e063b52f338bd924fe5c40692cc1f50cb0453bd1d6580b7ffff2eade1a894", size = 2279395, upload-time = "2026-10-04T17:40:25.777Z" },
{ url = "https://files.pythonhosted.org/packages/d4/72/a0726c7165ba84d66923c3e5d5411c5abec9a9a4c1759a664e5cf8806a79/pycryptodomex-3.24.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b6690bfae043c81108e28e31dc7e3afa8cb1b1eb0ba590fdbff2b66e4c6913e7", size = 2184779, upload-time = "2026-10-04T17:40:28.813Z" },
{ url = "https://files.pythonhosted.org/packages/39/51/c5b9d8fc84d5cf67091a007b476e24f634252fb314472e27a58f3bd97458/pycryptodomex-3.24.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa46af1281be707e0ec5878aea0f77d58d8d4b465f63f7385a71ae0080bfff9", size = 2276914, upload-time = "2026-10-04T17:40:31.23Z" },
{ url = "https://files.pythonhosted.org/packages/c4/28/26f6d1b4257911bdab1c66c2a80e1193131bd8c02fa9ac04453cd45233c1/pycryptodomex-3.24.0-cp37-abi3-win32.whl", hash = "sha256:fab40542a863cc142a763aa92630d613925fe3e937bea3ba5fb735e0c42d4f96", size = 1792631, upload-time = "2026-10-04T17:40:33.61Z" },
{ url = "https://files.pythonhosted.org/packages/4d/91/fa406bbeea093b53d314dfb0fd186658a69a398b73a3a609636b8402b5c3/pycryptodomex-3.24.0-cp37-abi3-win_amd64.whl", hash = "sha256:55f22d72250d7923aac3c2442ec6a1dd8774d2efa3bdcb0f68d5e137d372268e", size = 1825514, upload-time = "2026-10-04T17:40:36.059Z" },
{ url = "https://files.pythonhosted.org/packages/10/b0/c53c7ac97c5deb721aa4f940309802c71ff5ac2fdeac473ce96754c69e5b/pycryptodomex-3.24.0-cp37-abi3-win_arm64.whl", hash = "sha256:908f92fbc307b642b65580d0295698f423b057018d87ae43d441f1d1a00674a2", size = 1759397, upload-time = "2026-10-04T17:40:38.641Z" },
] ]
[[package]] [[package]]
name = "pydantic"
version = "2.13.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-types" }, { name = "annotated-types" },
{ name = "pydantic-core" }, { name = "pydantic-core" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
{name = "typing-inspection"} { name = "typing-inspection" },
] ]
name = "pydantic"
sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" }
source = {registry = "https://pypi.org/simple"}
version = "2.13.5"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z"} { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "typing-extensions"}
]
name = "pydantic-core" name = "pydantic-core"
sdist = {url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z"}
source = {registry = "https://pypi.org/simple"}
version = "2.46.5" version = "2.46.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" }, { url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" },
{ url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" }, { url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" },
@@ -774,92 +779,92 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" }, { url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" },
{ url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" }, { url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" },
{ url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" }, { url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" },
{url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z"} { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" },
] ]
[[package]] [[package]]
name = "pygments" name = "pygments"
sdist = {url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z"}
source = {registry = "https://pypi.org/simple"}
version = "2.21.0" version = "2.21.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z"} { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
] ]
[[package]] [[package]]
name = "pyjks"
version = "20.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "javaobj-py3" }, { name = "javaobj-py3" },
{ name = "pyasn1" }, { name = "pyasn1" },
{ name = "pyasn1-modules" }, { name = "pyasn1-modules" },
{ name = "pycryptodomex" }, { name = "pycryptodomex" },
{name = "twofish"} { name = "twofish" },
] ]
name = "pyjks"
sdist = { url = "https://files.pythonhosted.org/packages/9a/3d/69e49f948f4daa09d27ac3c5378d44107e1bf98b3b5c0f847121c3773945/pyjks-20.0.0.tar.gz", hash = "sha256:0378cec15fb11b2ed27ba54dad9fd987d48e6f62f49fcff138f5f7a8b312b044", size = 25695, upload-time = "2020-04-19T01:37:26.52Z" } sdist = { url = "https://files.pythonhosted.org/packages/9a/3d/69e49f948f4daa09d27ac3c5378d44107e1bf98b3b5c0f847121c3773945/pyjks-20.0.0.tar.gz", hash = "sha256:0378cec15fb11b2ed27ba54dad9fd987d48e6f62f49fcff138f5f7a8b312b044", size = 25695, upload-time = "2020-04-19T01:37:26.52Z" }
source = {registry = "https://pypi.org/simple"}
version = "20.0.0"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/28/46/e1f2c679d9c6c76a5fda4eb99922a51141803089bf58e77c74a1fe6f9033/pyjks-20.0.0-py2.py3-none-any.whl", hash = "sha256:394dee142ecff6b1adc36f64356e5584732f1859575aa03b9cf5d5541a9e3460", size = 45253, upload-time = "2020-04-19T01:37:28.789Z"} { url = "https://files.pythonhosted.org/packages/28/46/e1f2c679d9c6c76a5fda4eb99922a51141803089bf58e77c74a1fe6f9033/pyjks-20.0.0-py2.py3-none-any.whl", hash = "sha256:394dee142ecff6b1adc36f64356e5584732f1859575aa03b9cf5d5541a9e3460", size = 45253, upload-time = "2020-04-19T01:37:28.789Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "packaging"}
]
name = "pyproject-api" name = "pyproject-api"
sdist = {url = "https://files.pythonhosted.org/packages/51/24/757e066eafeeb91ac4887c2edb9c0ac5eb2d5420f836897443577719647f/pyproject_api-1.11.3.tar.gz", hash = "sha256:4ca2f09f628d86ae019d7c701ebb65ee667efc28261f8b9ee3bd55bf0985712c", size = 24944, upload-time = "2026-10-03T23:39:02.985Z"}
source = {registry = "https://pypi.org/simple"}
version = "1.11.3" version = "1.11.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/51/24/757e066eafeeb91ac4887c2edb9c0ac5eb2d5420f836897443577719647f/pyproject_api-1.11.3.tar.gz", hash = "sha256:4ca2f09f628d86ae019d7c701ebb65ee667efc28261f8b9ee3bd55bf0985712c", size = 24944, upload-time = "2026-10-03T23:39:02.985Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/92/14/8475adf84dbb57bb2a7c44c32fc9aafcf8c56b85618e24e2de3734ea1fc6/pyproject_api-1.11.3-py3-none-any.whl", hash = "sha256:d258c7aa390962bb942e5e4f9e66ceeb475e7b79e1cdc49cc7baa524547a64a7", size = 13546, upload-time = "2026-10-03T23:39:01.507Z"} { url = "https://files.pythonhosted.org/packages/92/14/8475adf84dbb57bb2a7c44c32fc9aafcf8c56b85618e24e2de3734ea1fc6/pyproject_api-1.11.3-py3-none-any.whl", hash = "sha256:d258c7aa390962bb942e5e4f9e66ceeb475e7b79e1cdc49cc7baa524547a64a7", size = 13546, upload-time = "2026-10-03T23:39:01.507Z" },
] ]
[[package]] [[package]]
name = "pytest"
version = "9.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" }, { name = "iniconfig" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pluggy" }, { name = "pluggy" },
{name = "pygments"} { name = "pygments" },
] ]
name = "pytest"
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
source = {registry = "https://pypi.org/simple"}
version = "9.1.1"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z"} { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
] ]
[[package]] [[package]]
name = "pytest-cov"
version = "7.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "coverage" }, { name = "coverage" },
{ name = "pluggy" }, { name = "pluggy" },
{name = "pytest"} { name = "pytest" },
] ]
name = "pytest-cov"
sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
source = {registry = "https://pypi.org/simple"}
version = "7.1.0"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z"} { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "filelock"}
]
name = "python-discovery" name = "python-discovery"
sdist = {url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z"}
source = {registry = "https://pypi.org/simple"}
version = "1.6.1" version = "1.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z"} { url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z" },
] ]
[[package]] [[package]]
name = "pywin32" name = "pywin32"
source = {registry = "https://pypi.org/simple"}
version = "312" version = "312"
source = { registry = "https://pypi.org/simple" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" }, { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" },
{ url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" }, { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" },
@@ -869,14 +874,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" }, { url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" }, { url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" },
{ url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" }, { url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" },
{url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z"} { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" },
] ]
[[package]] [[package]]
name = "pyyaml" name = "pyyaml"
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"}
source = {registry = "https://pypi.org/simple"}
version = "6.0.3" 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 = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
@@ -905,56 +910,59 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z"} { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
] ]
[[package]] [[package]]
name = "requests"
version = "2.34.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi" }, { name = "certifi" },
{ name = "charset-normalizer" }, { name = "charset-normalizer" },
{ name = "idna" }, { name = "idna" },
{name = "urllib3"} { name = "urllib3" },
] ]
name = "requests"
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
source = {registry = "https://pypi.org/simple"}
version = "2.34.2"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z"} { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
] ]
[[package]] [[package]]
name = "rich"
version = "15.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "markdown-it-py" }, { name = "markdown-it-py" },
{name = "pygments"} { name = "pygments" },
] ]
name = "rich"
sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
source = {registry = "https://pypi.org/simple"}
version = "15.0.0"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z"} { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
] ]
[[package]] [[package]]
name = "shellingham" name = "shellingham"
sdist = {url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z"}
source = {registry = "https://pypi.org/simple"}
version = "1.5.4" version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z"} { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
] ]
[[package]] [[package]]
name = "tomli-w" name = "tomli-w"
sdist = {url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z"}
source = {registry = "https://pypi.org/simple"}
version = "1.2.0" version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z"} { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
] ]
[[package]] [[package]]
name = "tox"
version = "4.64.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cachetools" }, { name = "cachetools" },
{ name = "colorama" }, { name = "colorama" },
@@ -965,143 +973,140 @@ dependencies = [
{ name = "pyproject-api" }, { name = "pyproject-api" },
{ name = "python-discovery" }, { name = "python-discovery" },
{ name = "tomli-w" }, { name = "tomli-w" },
{name = "virtualenv"} { name = "virtualenv" },
] ]
name = "tox"
sdist = { url = "https://files.pythonhosted.org/packages/11/33/e8f6282e5ced7b01abd968706758014df0b602c622df2c0c61c148921800/tox-4.64.8.tar.gz", hash = "sha256:8d54028cd31f5c8b3dc319e8c2346d720a1ed066aadf3325fd80e9fd3d233825", size = 326058, upload-time = "2026-10-03T23:39:00.492Z" } sdist = { url = "https://files.pythonhosted.org/packages/11/33/e8f6282e5ced7b01abd968706758014df0b602c622df2c0c61c148921800/tox-4.64.8.tar.gz", hash = "sha256:8d54028cd31f5c8b3dc319e8c2346d720a1ed066aadf3325fd80e9fd3d233825", size = 326058, upload-time = "2026-10-03T23:39:00.492Z" }
source = {registry = "https://pypi.org/simple"}
version = "4.64.8"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/5d/cd/f8c1bf8ade3eb15b92e651aa28ec88a64aab45b7115d3269d65459931add/tox-4.64.8-py3-none-any.whl", hash = "sha256:011d8384b649d24505a3477cc79c603d079da53e220593e4cfe4a28fcdc73db2", size = 235967, upload-time = "2026-10-03T23:38:58.822Z"} { url = "https://files.pythonhosted.org/packages/5d/cd/f8c1bf8ade3eb15b92e651aa28ec88a64aab45b7115d3269d65459931add/tox-4.64.8-py3-none-any.whl", hash = "sha256:011d8384b649d24505a3477cc79c603d079da53e220593e4cfe4a28fcdc73db2", size = 235967, upload-time = "2026-10-03T23:38:58.822Z" },
] ]
[[package]] [[package]]
name = "tox-docker"
version = "5.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "docker" }, { name = "docker" },
{name = "tox"} { name = "tox" },
] ]
name = "tox-docker"
sdist = { url = "https://files.pythonhosted.org/packages/de/3c/dcec11dadd48a8f2ba276cd499a4db46a0c70388e0505dae46730a185a35/tox_docker-5.0.0.tar.gz", hash = "sha256:3cbfe70713910b9dbb9f74e20a5c42f19da42d375bac7d6a3ccc11aed0738d7a", size = 19779, upload-time = "2024-05-25T12:50:22.173Z" } sdist = { url = "https://files.pythonhosted.org/packages/de/3c/dcec11dadd48a8f2ba276cd499a4db46a0c70388e0505dae46730a185a35/tox_docker-5.0.0.tar.gz", hash = "sha256:3cbfe70713910b9dbb9f74e20a5c42f19da42d375bac7d6a3ccc11aed0738d7a", size = 19779, upload-time = "2024-05-25T12:50:22.173Z" }
source = {registry = "https://pypi.org/simple"}
version = "5.0.0"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/fc/98/82381ccd7adf9b1d4b488199b916f0b77068e449b7186f8aeeb9f5264f1e/tox_docker-5.0.0-py3-none-any.whl", hash = "sha256:7dd7293ea286dff422eab6c23a70652147b275eb436c02e97a90ac59ba41f8a8", size = 29561, upload-time = "2024-05-25T12:50:20.636Z"} { url = "https://files.pythonhosted.org/packages/fc/98/82381ccd7adf9b1d4b488199b916f0b77068e449b7186f8aeeb9f5264f1e/tox_docker-5.0.0-py3-none-any.whl", hash = "sha256:7dd7293ea286dff422eab6c23a70652147b275eb436c02e97a90ac59ba41f8a8", size = 29561, upload-time = "2024-05-25T12:50:20.636Z" },
] ]
[[package]] [[package]]
name = "tox-uv"
version = "1.36.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "tox-uv-bare" }, { name = "tox-uv-bare" },
{name = "uv"} { name = "uv" },
] ]
name = "tox-uv"
source = {registry = "https://pypi.org/simple"}
version = "1.36.1"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/19/ef/06e0f151d1ebb544bcf27bf8a6914bdc7d9434689278c669efb3c73a8ed5/tox_uv-1.36.1-py3-none-any.whl", hash = "sha256:6996cb49d0b6b838ae015b76611770595cb6f658a27e2a9661766148194c18bd", size = 6567, upload-time = "2026-10-01T23:51:58.511Z"} { url = "https://files.pythonhosted.org/packages/19/ef/06e0f151d1ebb544bcf27bf8a6914bdc7d9434689278c669efb3c73a8ed5/tox_uv-1.36.1-py3-none-any.whl", hash = "sha256:6996cb49d0b6b838ae015b76611770595cb6f658a27e2a9661766148194c18bd", size = 6567, upload-time = "2026-10-01T23:51:58.511Z" },
] ]
[[package]] [[package]]
name = "tox-uv-bare"
version = "1.36.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "packaging" }, { name = "packaging" },
{name = "tox"} { name = "tox" },
] ]
name = "tox-uv-bare"
sdist = { url = "https://files.pythonhosted.org/packages/42/c1/1f5b4ff553013dfd418485cb9359cf305e7f47b946e02a36f4a4c279ac21/tox_uv_bare-1.36.1.tar.gz", hash = "sha256:33a08e4205c63811dd76590590718539fe87dcadda5882f164f747bd5db31a2c", size = 32648, upload-time = "2026-10-01T23:52:01.302Z" } sdist = { url = "https://files.pythonhosted.org/packages/42/c1/1f5b4ff553013dfd418485cb9359cf305e7f47b946e02a36f4a4c279ac21/tox_uv_bare-1.36.1.tar.gz", hash = "sha256:33a08e4205c63811dd76590590718539fe87dcadda5882f164f747bd5db31a2c", size = 32648, upload-time = "2026-10-01T23:52:01.302Z" }
source = {registry = "https://pypi.org/simple"}
version = "1.36.1"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/04/64/ed9b351f7ce794df36923fcde243e90cef07f103ea0c24ae4557e9736b7e/tox_uv_bare-1.36.1-py3-none-any.whl", hash = "sha256:de41cf5450b8f37837fd68834dfebef59939cd10fde030e498cd9a48db19f1e8", size = 22469, upload-time = "2026-10-01T23:51:59.906Z"} { url = "https://files.pythonhosted.org/packages/04/64/ed9b351f7ce794df36923fcde243e90cef07f103ea0c24ae4557e9736b7e/tox_uv_bare-1.36.1-py3-none-any.whl", hash = "sha256:de41cf5450b8f37837fd68834dfebef59939cd10fde030e498cd9a48db19f1e8", size = 22469, upload-time = "2026-10-01T23:51:59.906Z" },
] ]
[[package]] [[package]]
name = "twofish" name = "twofish"
sdist = {url = "https://files.pythonhosted.org/packages/82/b4/9eb026a8e62a04512435d3de25c93f7bda51c8b8c7991c1c0be70b5115a6/twofish-0.3.0.tar.gz", hash = "sha256:b09d8bb50d33b23ff34cafb1f9209f858f752935c6a5c901efb92a41acb830fa", size = 26039, upload-time = "2013-11-15T20:37:59.634Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.3.0" version = "0.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/b4/9eb026a8e62a04512435d3de25c93f7bda51c8b8c7991c1c0be70b5115a6/twofish-0.3.0.tar.gz", hash = "sha256:b09d8bb50d33b23ff34cafb1f9209f858f752935c6a5c901efb92a41acb830fa", size = 26039, upload-time = "2013-11-15T20:37:59.634Z" }
[[package]] [[package]]
name = "typer"
version = "0.27.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-doc" }, { name = "annotated-doc" },
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "rich" }, { name = "rich" },
{name = "shellingham"} { name = "shellingham" },
] ]
name = "typer"
sdist = { url = "https://files.pythonhosted.org/packages/16/f7/57713ba479fd405eb76de31404b2c744c289e336b2d999511ebf51e496f7/typer-0.27.2.tar.gz", hash = "sha256:269b7eb9d3c202ca84b4bc9618cb04ebb43d3d4d1e567e4c768607232c05f945", size = 204045, upload-time = "2026-08-28T10:26:55.046Z" } sdist = { url = "https://files.pythonhosted.org/packages/16/f7/57713ba479fd405eb76de31404b2c744c289e336b2d999511ebf51e496f7/typer-0.27.2.tar.gz", hash = "sha256:269b7eb9d3c202ca84b4bc9618cb04ebb43d3d4d1e567e4c768607232c05f945", size = 204045, upload-time = "2026-08-28T10:26:55.046Z" }
source = {registry = "https://pypi.org/simple"}
version = "0.27.2"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/dc/bf/205d0004930ede8f542fb58f601526fccf4ae7626075ca1e6c4de5d3d652/typer-0.27.2-py3-none-any.whl", hash = "sha256:b3a5fc4342d5fc8fda8fc3010b1cf117e9249aab7fae800c2eff62fd3842d97d", size = 123130, upload-time = "2026-08-28T10:26:53.752Z"} { url = "https://files.pythonhosted.org/packages/dc/bf/205d0004930ede8f542fb58f601526fccf4ae7626075ca1e6c4de5d3d652/typer-0.27.2-py3-none-any.whl", hash = "sha256:b3a5fc4342d5fc8fda8fc3010b1cf117e9249aab7fae800c2eff62fd3842d97d", size = 123130, upload-time = "2026-08-28T10:26:53.752Z" },
] ]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
sdist = {url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z"}
source = {registry = "https://pypi.org/simple"}
version = "4.16.0" version = "4.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z"} { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
] ]
[[package]] [[package]]
dependencies = [
{name = "typing-extensions"}
]
name = "typing-inspection" name = "typing-inspection"
sdist = {url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z"}
source = {registry = "https://pypi.org/simple"}
version = "0.4.4" version = "0.4.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z"} { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" },
] ]
[[package]] [[package]]
name = "urllib3" name = "urllib3"
sdist = {url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z"}
source = {registry = "https://pypi.org/simple"}
version = "2.8.0" version = "2.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z"} { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
] ]
[[package]] [[package]]
name = "uv" name = "uv"
sdist = {url = "https://files.pythonhosted.org/packages/6d/f5/34435c1d9ee52d38bda1d7d2948d8ef48a9ac9ec79e6fbfd81ced8b0ad19/uv-0.12.23.tar.gz", hash = "sha256:e952162a5be6847deaccf3fd5b41a628506fd9ff1ea4363b8c975bd6f927d832", size = 7197708, upload-time = "2026-10-03T17:33:01.864Z"} version = "0.13.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
version = "0.12.23" sdist = { url = "https://files.pythonhosted.org/packages/79/55/609455d21e87430c11fe734abfced50a3bdf177b2f49d41fbb19c199cbc3/uv-0.13.4.tar.gz", hash = "sha256:b0bff5b9a5061f7a35b1d0c1a94c19af134222c48eb7aadc42f85a038be1b248", size = 7482486, upload-time = "2026-10-20T15:12:06.801Z" }
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/7c/af/ed4bcd2fbebe19d613412d02053af44809d02a1204cb6cf0a7a9af7a02fa/uv-0.12.23-py3-none-linux_armv6l.whl", hash = "sha256:8f021dfbe5b6081021dd47fd8372cfba386fc5d39161e73d4f33cff90ce1760c", size = 22770792, upload-time = "2026-10-03T17:31:15.723Z"}, { url = "https://files.pythonhosted.org/packages/2c/bd/4349da5356b17a1c0e36c8552c809925f86f6292de9b1f599f01e8e8093d/uv-0.13.4-py3-none-linux_armv6l.whl", hash = "sha256:205a4f93e568fb24f45a2f3834f1c0a8f1adb5f396bf473c71030ed707dce048", size = 18272390, upload-time = "2026-10-20T15:11:02.468Z" },
{url = "https://files.pythonhosted.org/packages/01/15/a2514d061b92d4b7c0ebefc3da51f3ff5a528e9f4c067ea2cf48c5573e5a/uv-0.12.23-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4951286e68f116fa41897ecf8e248cc2836cb013be6d4f37afae4049f9ef4df7", size = 21459315, upload-time = "2026-10-03T17:31:18.824Z"}, { url = "https://files.pythonhosted.org/packages/21/f2/0cb77a1ae43959772f8f7873eb1f4061907fbb1694ce55c79b231f639b1d/uv-0.13.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b4bbacea78c9db136cd2ec3147cbfd9ed910b4db5e1877109bd74d0e73bb2eaf", size = 17384113, upload-time = "2026-10-20T15:11:07.172Z" },
{url = "https://files.pythonhosted.org/packages/23/e9/b75db33954e985a763bd3277a477cabd551aaba7d08872b22400838dace2/uv-0.12.23-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99197c022afbd1edd0a295e9fdac5078c61eddf2e77657a77294cfe8ea0d94b9", size = 17158822, upload-time = "2026-10-03T17:31:21.13Z"}, { url = "https://files.pythonhosted.org/packages/26/44/5a8733dd1b2e8d6f9cffd1ff6428a3e11dcc7e146a7657e32cfb8d9fb4a4/uv-0.13.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:95f4eb2b991b53b50ec2b18ab612fca8ba1e7a75a47af0331fb8bcd21da3eb03", size = 14433723, upload-time = "2026-10-20T15:11:10.185Z" },
{url = "https://files.pythonhosted.org/packages/39/77/ff15f878a215b543fd5aa05d2e3f939f3a2c46502ff58362eb5b51434af3/uv-0.12.23-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:895137194d242cc8075c3006288b485af54feeae68512f4ffc96f75b27543cc0", size = 22159208, upload-time = "2026-10-03T17:31:23.624Z"}, { url = "https://files.pythonhosted.org/packages/f8/39/be14f051bf34a1822d634819890a2d0cba3b69101e633fb7b9f54d7b4020/uv-0.13.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:e90ab42825587821096cb0d071e387468da38cfa22818c91f9c0b3c0453048e8", size = 17816443, upload-time = "2026-10-20T15:11:13.674Z" },
{url = "https://files.pythonhosted.org/packages/ab/2f/94bfc387f1c8169c2f82415f39a1a318d148104053b0efc70d3e15e72da6/uv-0.12.23-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:c4d236bb220856027ba8e28ef693cac472ee2a740a61faef1ac2001165c7b3d5", size = 22254708, upload-time = "2026-10-03T17:31:26.313Z"}, { url = "https://files.pythonhosted.org/packages/97/a0/5df0f8bc6367dd37b98432a7c462e2918554f6db2a71211574852b8f0727/uv-0.13.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:3a9dcb6762c3ba249d8c084315ba03839082af3ece3536cc4164ec735c351d68", size = 17828025, upload-time = "2026-10-20T15:11:16.806Z" },
{url = "https://files.pythonhosted.org/packages/a2/e2/42eba7023a4ab5496c17dacd27834cb0479fbccdadafe406cd4c187c5b33/uv-0.12.23-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02f2a0fd1cb90b5fc06b52e8cbc3169bdc4e53804e9d66ba0ad516be767424f4", size = 22290428, upload-time = "2026-10-03T17:31:28.895Z"}, { url = "https://files.pythonhosted.org/packages/8b/9c/2fed5357700b7c304b94214979e4efe680c3bc6e337a97eedaa372fe0947/uv-0.13.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ae6ff2b07892379dc767c9064c2b8bbfac88e767edac7ce28e1b0f15d86abb4", size = 17843737, upload-time = "2026-10-20T15:11:20.043Z" },
{url = "https://files.pythonhosted.org/packages/19/cb/c68249501d913d3ef35596283acbe0af0f1b388e27c6b46aa402e968c55d/uv-0.12.23-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:855232fd95d456bd5ee17e262f73112967fbbb2803e745f1911c08ceb042e641", size = 23126626, upload-time = "2026-10-03T17:31:31.242Z"}, { url = "https://files.pythonhosted.org/packages/4c/2e/ee026284f096236dc3e1abed0316f9edab65c0ea176ea0072c064e62478d/uv-0.13.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25cc14ff4fd8335d3be2cc493a621a003fdafcb0fb1c953c905daabe4def5ab8", size = 18576350, upload-time = "2026-10-20T15:11:24.035Z" },
{url = "https://files.pythonhosted.org/packages/14/fb/a6cf5eb99a37368ce8ad698ff406997897502eee5cacfd3d9805a02336ff/uv-0.12.23-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b70220be9056bd05908770179101a53687eb5c89f72a3edc1e20df0cd3f157c2", size = 24336288, upload-time = "2026-10-03T17:31:33.73Z"}, { url = "https://files.pythonhosted.org/packages/6d/f1/67fbdb649c8c5d0cb38ea74829080479b77c8631cda5d77fc1b28f027ca7/uv-0.13.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77062b02856c74697f48328d8a6583ab07e6da0a993e86c2db46f3d1da27c637", size = 19831234, upload-time = "2026-10-20T15:11:27.627Z" },
{url = "https://files.pythonhosted.org/packages/c2/73/809af8a6fc97adfce7750b6b6d8c7fdf3e57c6ad40bc99e6b1228cdabe1e/uv-0.12.23-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08b2fc8e250efdfc1e47da0483f3e19f433bb0934282361817f61c0b19cb3674", size = 24092888, upload-time = "2026-10-03T17:31:36.747Z"}, { url = "https://files.pythonhosted.org/packages/bd/00/7d49e3a1a3e98e5244fa38e0c964dc8de568bf3bb101257a63dc2602efed/uv-0.13.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb0e2028d9a8929884344afb7d91d0f65d9783f640d28aede89fca3602bedde9", size = 19702886, upload-time = "2026-10-20T15:11:31.128Z" },
{url = "https://files.pythonhosted.org/packages/e5/83/85a6c63c24905af4924fddb11a499b934913f59a134248367a1ef1a4716f/uv-0.12.23-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565c6e2874dbeae86c02f3dea97255e878fec672659a73d4930c6b93fcab2fff", size = 20579564, upload-time = "2026-10-03T17:31:39.705Z"}, { url = "https://files.pythonhosted.org/packages/4b/a1/de02c0913726728eca56ee258875cbba9c7b0572c63f494fd9f55c7852e3/uv-0.13.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e4332791b4d55b6dde8695b7fa6079b26991519f44760887c5dfbd5ca4fe53", size = 17181168, upload-time = "2026-10-20T15:11:34.583Z" },
{url = "https://files.pythonhosted.org/packages/df/50/84d0424b63718ac6f277c0b77206751e078a0adea16813320508a033e718/uv-0.12.23-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9346c741175c7fc0d380f30fcfcb8d5e13f5f900b30536230c8d34208524e47a", size = 19859001, upload-time = "2026-10-03T17:31:42.397Z"}, { url = "https://files.pythonhosted.org/packages/a8/cd/f4a3c7377674366798c56aabd0f9a1f05687c3939f5ba857a3ea6093ff84/uv-0.13.4-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:7b3cdf868e5415631fe797b648c0d6945d38d388f23367bdd0b6332e80a1fcb0", size = 16502020, upload-time = "2026-10-20T15:11:37.88Z" },
{url = "https://files.pythonhosted.org/packages/66/b7/03fe205f2f9af0dfa4467e561c7ecc620f03e7f7339d78d16fa38582e207/uv-0.12.23-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:b03b1c35b5f301e888671eb5d0e74b8789558905db1d411cb8e07ea4ec320268", size = 22701871, upload-time = "2026-10-03T17:31:44.804Z"}, { url = "https://files.pythonhosted.org/packages/f9/f4/ce79657c5a0f51b602db9a663529baafff03b9fe8cc41e09ad3f7246b88a/uv-0.13.4-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:4e6b08578c920b86910897f36bb6da7229c8e7e0187976570e2a538cb28b777e", size = 18000980, upload-time = "2026-10-20T15:11:41.59Z" },
{url = "https://files.pythonhosted.org/packages/f3/17/26862297f6bece90d7f825f78a814580b36a9ac69a139f0385660e1a205c/uv-0.12.23-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5b07cbea635177da0d6b902bcde9ce0304c1fa5b49a37b27ef7aa021c5c31697", size = 22814543, upload-time = "2026-10-03T17:31:47.313Z"}, { url = "https://files.pythonhosted.org/packages/21/e6/7d77022d5661f8e01578bc5423bf02af408220e7e7ac5ee15eb94742c7cb/uv-0.13.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d86a5dec8ab30c70519c71ca5085c8aaf28664836cbc600951cedaa3fc678cbf", size = 18179083, upload-time = "2026-10-20T15:11:45.191Z" },
{url = "https://files.pythonhosted.org/packages/da/04/31ae46fe42cbdc91b30162e5d430719911339a18f8ab3fb9126ee409fe29/uv-0.12.23-py3-none-musllinux_1_1_i686.whl", hash = "sha256:7a919e3420adf4cc03895b404af655c7229f646bffabbd9a16979683772a7d34", size = 22269008, upload-time = "2026-10-03T17:31:49.911Z"}, { url = "https://files.pythonhosted.org/packages/f4/40/1bf0324800d38009774ba087a6fbffddc76a3e5bf42c081b9298b1236fb5/uv-0.13.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:5d89d1028c2e22977d9215cbb79cfe12559cb0c8a507a12e2f3a7e47930cf1bf", size = 18010389, upload-time = "2026-10-20T15:11:48.963Z" },
{url = "https://files.pythonhosted.org/packages/71/b7/340260cf1a92017b271869764a68d9a019d796c4a7fa80c6c1bbcf5721eb/uv-0.12.23-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:11d18c1589d7907376b4f3fa31028e46cc609ac4b7d83eabc2b04cd8ca1433cc", size = 23541384, upload-time = "2026-10-03T17:31:52.416Z"}, { url = "https://files.pythonhosted.org/packages/1e/ec/f352ca2cc3b4b6191bb4ec8cdf4bc190eed0ba8bda4c33b7fd66c5d33cec/uv-0.13.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:52b5c753a7c2d5f8323a70bb4c93118988356331d0758afa6446c81f974e7196", size = 19004998, upload-time = "2026-10-20T15:11:52.628Z" },
{url = "https://files.pythonhosted.org/packages/77/76/3a7ab3ab0bd8ab107b50fe981a3198188c8260e9b17b629e64899eca709b/uv-0.12.23-py3-none-win32.whl", hash = "sha256:f05c5ad8d6cb4b2659cb6368ed02c067539415b24a2b69effd8ada576fdbf303", size = 20101333, upload-time = "2026-10-03T17:31:54.811Z"}, { url = "https://files.pythonhosted.org/packages/9e/4f/fc05ff4292edfe785b47874ec86d3d83ca2bf735cff8a9d2170ffb66ad83/uv-0.13.4-py3-none-win32.whl", hash = "sha256:d333f01852b817ba05612de31474739da1af7e5463d119b30a9c4331eba1f408", size = 16190502, upload-time = "2026-10-20T15:11:56.657Z" },
{url = "https://files.pythonhosted.org/packages/af/fd/816f4e8a76197312ec8037369e5b5c8a5ebde7aa118b6250259e031d3444/uv-0.12.23-py3-none-win_amd64.whl", hash = "sha256:fb8a4117a5224d73abe2204a1e744ae34b544f1b9a0b761576851deaa7215c12", size = 18138726, upload-time = "2026-10-03T17:31:57.498Z"}, { url = "https://files.pythonhosted.org/packages/2f/b9/b526eab6c72eacc705a58378f6850fde046b28c86fed01bd42da61367c6b/uv-0.13.4-py3-none-win_amd64.whl", hash = "sha256:b2ff80397273fff0e02c20eacae6882834c811827d6fc08f658db156fe754386", size = 15331833, upload-time = "2026-10-20T15:11:59.733Z" },
{url = "https://files.pythonhosted.org/packages/d9/74/1906f7f3f785fcf37a72b371d4ab58ec7193c5760923136cca1376209bc8/uv-0.12.23-py3-none-win_arm64.whl", hash = "sha256:9a65258535db432df51ca7d26338c01431820b94b634762f5718fc55895f974e", size = 19208424, upload-time = "2026-10-03T17:31:59.726Z"} { url = "https://files.pythonhosted.org/packages/7c/f7/4d5fa89d3d10ff6a84e547ef6e02c45b2f2d50e66682da778acf60db63f1/uv-0.13.4-py3-none-win_arm64.whl", hash = "sha256:deffa5a4afdb48f3e44fb26e80377d256b330ddcad0373daff4b54f6162b43a4", size = 15690151, upload-time = "2026-10-20T15:12:02.74Z" },
] ]
[[package]] [[package]]
name = "virtualenv"
version = "21.14.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "distlib" }, { name = "distlib" },
{ name = "filelock" }, { name = "filelock" },
{ name = "packaging" }, { name = "packaging" },
{ name = "platformdirs" }, { name = "platformdirs" },
{name = "python-discovery"} { name = "python-discovery" },
] ]
name = "virtualenv"
sdist = { url = "https://files.pythonhosted.org/packages/c4/f9/f323b3b6058cff3853b31cf6a49c0425ed797bf9611572ec10d065e08bac/virtualenv-21.14.5.tar.gz", hash = "sha256:c4cb6c13e46b57225a999c7e22a09b163393878facc7ac4c059a57f46faa1647", size = 5467127, upload-time = "2026-10-02T17:42:25.613Z" } sdist = { url = "https://files.pythonhosted.org/packages/c4/f9/f323b3b6058cff3853b31cf6a49c0425ed797bf9611572ec10d065e08bac/virtualenv-21.14.5.tar.gz", hash = "sha256:c4cb6c13e46b57225a999c7e22a09b163393878facc7ac4c059a57f46faa1647", size = 5467127, upload-time = "2026-10-02T17:42:25.613Z" }
source = {registry = "https://pypi.org/simple"}
version = "21.14.5"
wheels = [ wheels = [
{url = "https://files.pythonhosted.org/packages/df/93/084f10ebbecdedf7902ed68fbecc903f0e9c5742155bfffb05eccbc071da/virtualenv-21.14.5-py3-none-any.whl", hash = "sha256:b0651e0174982bba17cc6f0aaa2ef496730cb85a3f35eb5d1119d1c904f7d1da", size = 5487482, upload-time = "2026-10-02T17:42:23.38Z"} { url = "https://files.pythonhosted.org/packages/df/93/084f10ebbecdedf7902ed68fbecc903f0e9c5742155bfffb05eccbc071da/virtualenv-21.14.5-py3-none-any.whl", hash = "sha256:b0651e0174982bba17cc6f0aaa2ef496730cb85a3f35eb5d1119d1c904f7d1da", size = 5487482, upload-time = "2026-10-02T17:42:23.38Z" },
] ]