From 34d786f6e04b3469e27b48e6cbb4c63279b01622 Mon Sep 17 00:00:00 2001 From: Jacques Thomazo Date: Mon, 20 Jul 2026 00:45:18 +0200 Subject: [PATCH] Implement documentation quality commands --- .github/workflows/quality.yml | 9 + .gitignore | 1 + AGENTS.md | 21 +- CHANGELOG.md | 1 + README.md | 9 + SECURITY.md | 6 + docs/index.md | 15 ++ docs/quality-gates.md | 15 ++ docs/repository-shapes.md | 1 + examples/doc-only/repo-quality.toml | 2 +- mkdocs.yml | 26 +++ pyproject.toml | 6 + repo-quality.toml | 4 + src/repo_quality/docs.py | 337 ++++++++++++++++++++++++++++ tests/test_docs.py | 162 +++++++++++++ uv.lock | 239 ++++++++++++++++++++ 16 files changed, 845 insertions(+), 9 deletions(-) create mode 100644 docs/index.md create mode 100644 mkdocs.yml create mode 100644 src/repo_quality/docs.py create mode 100644 tests/test_docs.py diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 822de65..2fdb7ca 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -53,5 +53,14 @@ jobs: - name: Check repository conformance run: uv run repo-quality check . + - name: Check documentation formatting + run: uv run check-docs-format + + - name: Check documentation links + run: uv run check-docs-links + + - name: Build documentation + run: uv run build-docs + - name: Build distribution run: uv build diff --git a/.gitignore b/.gitignore index efaee89..e2f6aa2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ htmlcov/ # Build output build/ dist/ +site/ # Editors and operating systems .idea/ diff --git a/AGENTS.md b/AGENTS.md index 4fa7f14..3ea2a60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,14 +35,19 @@ must surface ambiguous policy rather than invent it. Run from the repository root: -1. `uv sync --locked --all-groups` -2. `uv run pre-commit run --all-files` -3. `uv run ruff format --check .` -4. `uv run ruff check .` -5. `uv run ty check` -6. `uv run pytest` -7. `uv run repo-quality check .` -8. `uv build` +```bash +uv sync --locked --all-groups +uv run pre-commit run --all-files +uv run ruff format --check . +uv run ruff check . +uv run ty check +uv run pytest +uv run repo-quality check . +uv run check-docs-format +uv run check-docs-links +uv run build-docs +uv build +``` CI must run the same validation commands. Do not bypass a failed gate or edit generated files to hide a mismatch. diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ec6a8..1e4f040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ semantic versioning once the first stable release is published. repositories. - Self-auditing `repo-quality check` CLI. - Golden-reference CI, tests, contributor guidance, and security policy. +- Executable documentation formatting, link-checking, and site-build commands. ### Changed diff --git a/README.md b/README.md index d4a1976..c7c5ed3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ Install the development environment and verify this repository: ```bash uv sync --locked --all-groups uv run repo-quality check . +uv run check-docs-format +uv run check-docs-links +uv run build-docs uv run pytest ``` @@ -64,6 +67,11 @@ provide fast feedback, while CI runs the authoritative locked gate chain. See [Quality Gates](docs/quality-gates.md) for universal, profile-specific, and conditional gates. +The documentation commands are installed with the package. They check Markdown +formatting, validate local links and heading fragments, retry external link +checks, and build a strict MkDocs site. Known external exceptions can be made +explicit with repeatable `--exclude-url-prefix` arguments. + ## Repository Layout ```text @@ -75,6 +83,7 @@ conditional gates. |-- tests/ # Behavior and conformance tests |-- AGENTS.md # Instructions for coding agents |-- CONTRIBUTING.md # Human contribution workflow +|-- mkdocs.yml # Strict documentation-site configuration |-- SECURITY.md # Vulnerability reporting and security boundary |-- pyproject.toml # Package and tool configuration |-- repo-quality.toml # Declared profile and quality contract diff --git a/SECURITY.md b/SECURITY.md index e417b0d..4000bf8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -19,3 +19,9 @@ workaround. Do not include real credentials or sensitive production data. The `repo-quality` CLI reads repository files and reports findings. It does not execute commands declared in `repo-quality.toml`, install dependencies, access the network, or modify the target repository. + +`check-docs-links` makes outbound HEAD or GET requests to HTTP(S) targets found +in Markdown unless `--no-external` is set. `build-docs` runs MkDocs and replaces +the configured site output directory, which is `site/` in this repository. +Review configuration and link targets before running these commands against an +untrusted repository. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..85bab70 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,15 @@ +# Repository Quality Reference + +This documentation defines a small, executable quality standard for repositories +maintained by humans and coding agents. Python and `uv` are the maintained default +language and tooling ecosystem. + +## Start Here + +- Read the [standard](standard.md) for normative requirements. +- Choose a supported [repository shape](repository-shapes.md). +- Adopt the appropriate [quality gates](quality-gates.md). +- Use the [adoption guide](adoption.md) to introduce the standard incrementally. + +The project README contains installation and command examples. This site holds the +durable standard, operating guidance, and architectural decisions. diff --git a/docs/quality-gates.md b/docs/quality-gates.md index 369d337..3fc6723 100644 --- a/docs/quality-gates.md +++ b/docs/quality-gates.md @@ -73,6 +73,21 @@ A documentation repository MUST define equivalents for: The exact tools are repository decisions. Python-based tooling managed through `uv` is preferred to preserve the default ecosystem and a single lock file. +This reference package provides a maintained default command set: + +```bash +uv run check-docs-format +uv run check-docs-links +uv run build-docs +``` + +`check-docs-format` runs mdformat in check mode with consistent ordered-list +numbering. `check-docs-links` validates local targets and heading fragments, +checks each unique external HTTP(S) target, and retries transient failures twice. +Repositories MAY exclude a known external URL prefix with +`--exclude-url-prefix` when the exception is intentional and visible in the CI +command. `build-docs` runs a strict MkDocs build using `mkdocs.yml`. + ## Conditional Gates Add these only when the repository has the associated failure mode: diff --git a/docs/repository-shapes.md b/docs/repository-shapes.md index 9d8c8d6..4736c6e 100644 --- a/docs/repository-shapes.md +++ b/docs/repository-shapes.md @@ -29,6 +29,7 @@ runbooks, architecture guidance, or a knowledge base. |-- tools/ # optional Python doc tooling |-- AGENTS.md |-- README.md +|-- mkdocs.yml # when a documentation site is built |-- pyproject.toml # when Python tooling is used `-- uv.lock # when Python tooling is used ``` diff --git a/examples/doc-only/repo-quality.toml b/examples/doc-only/repo-quality.toml index 58715f0..e54a4a0 100644 --- a/examples/doc-only/repo-quality.toml +++ b/examples/doc-only/repo-quality.toml @@ -2,7 +2,7 @@ schema_version = 1 [repository] purpose = "doc-only" -required_paths = ["CONTRIBUTING.md"] +required_paths = ["CONTRIBUTING.md", "mkdocs.yml"] [documentation] readme_sections = ["Purpose", "Navigation", "Contributing"] diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..0041ed7 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,26 @@ +site_name: Repository Quality Reference +site_description: Portable quality profiles for Python-first repositories +repo_url: https://github.com/JayTeeBat/repo-quality-reference +docs_dir: docs +site_dir: site +strict: true + +theme: + name: mkdocs + +nav: + - Overview: index.md + - Standard: standard.md + - Repository Shapes: repository-shapes.md + - Quality Gates: quality-gates.md + - README and AGENTS: readme-and-agents.md + - Repository Lifecycle: lifecycle.md + - Adoption: adoption.md + - Architecture Decisions: + - Index: adr/README.md + - Self-Auditing Reference: adr/0001-self-auditing-reference.md + +validation: + links: + anchors: warn + unrecognized_links: warn diff --git a/pyproject.toml b/pyproject.toml index 07fbbc9..216c625 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,17 @@ authors = [ { name = "Jacques Thomazo" }, ] dependencies = [ + "markdown>=3.10,<4", "markdown-it-py>=3.0.0,<5", + "mdformat>=1.0.0,<2", + "mkdocs>=1.6.1,<2", "pyyaml>=6.0.2,<7", ] [project.scripts] +build-docs = "repo_quality.docs:build_main" +check-docs-format = "repo_quality.docs:format_main" +check-docs-links = "repo_quality.docs:links_main" repo-quality = "repo_quality.cli:main" [dependency-groups] diff --git a/repo-quality.toml b/repo-quality.toml index 2f6fcf0..8152d2e 100644 --- a/repo-quality.toml +++ b/repo-quality.toml @@ -7,6 +7,7 @@ required_paths = [ "CHANGELOG.md", "CONTRIBUTING.md", "LICENSE", + "mkdocs.yml", "SECURITY.md", "docs/adr", "examples", @@ -39,5 +40,8 @@ commands = [ "uv run ty check", "uv run pytest", "uv run repo-quality check .", + "uv run check-docs-format", + "uv run check-docs-links", + "uv run build-docs", "uv build", ] diff --git a/src/repo_quality/docs.py b/src/repo_quality/docs.py new file mode 100644 index 0000000..a137246 --- /dev/null +++ b/src/repo_quality/docs.py @@ -0,0 +1,337 @@ +"""Portable documentation quality commands.""" + +from __future__ import annotations + +import argparse +import subprocess +import sys +import time +from collections.abc import Callable, Sequence +from dataclasses import dataclass +from pathlib import Path +from urllib.error import HTTPError, URLError +from urllib.parse import unquote, urlsplit +from urllib.request import Request, urlopen + +from markdown.extensions.toc import slugify +from markdown_it import MarkdownIt + +IGNORED_DIRECTORIES = frozenset( + {".git", ".pytest_cache", ".ruff_cache", ".ty_cache", ".venv", "build", "dist", "site"} +) +USER_AGENT = "repo-quality-reference/0.1 (+https://github.com/JayTeeBat/repo-quality-reference)" + + +@dataclass(frozen=True) +class DocumentationLink: + """A link found in a Markdown document.""" + + source: Path + target: str + + +@dataclass(frozen=True) +class LinkIssue: + """An invalid or unreachable documentation link.""" + + source: Path + target: str + message: str + + +def format_main(argv: Sequence[str] | None = None) -> int: + """Check Markdown formatting with mdformat.""" + + parser = _root_parser("Check Markdown formatting without modifying files.") + args = parser.parse_args(argv) + root = _resolve_root(parser, args.root) + paths = [str(path.relative_to(root)) for path in markdown_paths(root)] + if not paths: + print(f"PASS {root} (no Markdown files)") + return 0 + return _run_module(root, "mdformat", "--check", "--number", *paths) + + +def links_main(argv: Sequence[str] | None = None) -> int: + """Check local and external links in Markdown documents.""" + + parser = _root_parser("Check links in Markdown documents.") + parser.add_argument( + "--exclude-url-prefix", + action="append", + default=[], + metavar="PREFIX", + help="skip external URLs beginning with PREFIX; may be repeated", + ) + parser.add_argument( + "--external-timeout", + type=float, + default=10.0, + metavar="SECONDS", + help="timeout for each external request (default: 10)", + ) + parser.add_argument( + "--external-retries", + type=int, + default=2, + metavar="COUNT", + help="retries after a transient external failure (default: 2)", + ) + parser.add_argument( + "--no-external", + action="store_true", + help="check local links only", + ) + args = parser.parse_args(argv) + if args.external_timeout <= 0: + parser.error("--external-timeout must be greater than zero") + if args.external_retries < 0: + parser.error("--external-retries must not be negative") + + root = _resolve_root(parser, args.root) + issues = check_links( + root, + check_external=not args.no_external, + excluded_url_prefixes=tuple(args.exclude_url_prefix), + timeout=args.external_timeout, + retries=args.external_retries, + ) + if not issues: + print(f"PASS {root} (documentation links)") + return 0 + + for issue in issues: + print(f"ERROR {issue.source}: {issue.message}: {issue.target}") + return 1 + + +def build_main(argv: Sequence[str] | None = None) -> int: + """Build the documentation site with strict MkDocs warnings.""" + + parser = _root_parser("Build the documentation site with MkDocs.") + parser.add_argument( + "--config-file", + type=Path, + default=Path("mkdocs.yml"), + help="configuration path relative to the repository root", + ) + args = parser.parse_args(argv) + root = _resolve_root(parser, args.root) + return _run_module( + root, + "mkdocs", + "build", + "--strict", + "--config-file", + str(args.config_file), + ) + + +def markdown_paths(root: Path) -> tuple[Path, ...]: + """Return Markdown files below *root*, excluding generated directories.""" + + return tuple( + path + for path in sorted(root.rglob("*.md")) + if not any(part in IGNORED_DIRECTORIES for part in path.relative_to(root).parts) + ) + + +def check_links( + root: Path, + *, + check_external: bool = True, + excluded_url_prefixes: tuple[str, ...] = (), + timeout: float = 10.0, + retries: int = 2, + open_url: Callable[..., object] = urlopen, + sleep: Callable[[float], None] = time.sleep, +) -> tuple[LinkIssue, ...]: + """Return documentation link issues below *root*.""" + + root = root.resolve() + links = _markdown_links(root) + issues = list(_check_local_links(root, links)) + if check_external: + issues.extend( + _check_external_links( + links, + excluded_url_prefixes=excluded_url_prefixes, + timeout=timeout, + retries=retries, + open_url=open_url, + sleep=sleep, + ) + ) + return tuple(issues) + + +def _root_parser(description: str) -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=description) + parser.add_argument("root", nargs="?", type=Path, default=Path.cwd()) + return parser + + +def _resolve_root(parser: argparse.ArgumentParser, path: Path) -> Path: + root = path.resolve() + if not root.is_dir(): + parser.error(f"repository root is not a directory: {path}") + return root + + +def _run_module(root: Path, module: str, *arguments: str) -> int: + result = subprocess.run( + [sys.executable, "-m", module, *arguments], + cwd=root, + check=False, + ) + return result.returncode + + +def _markdown_links(root: Path) -> tuple[DocumentationLink, ...]: + parser = MarkdownIt() + links: list[DocumentationLink] = [] + for path in markdown_paths(root): + source = path.relative_to(root) + for token in parser.parse(path.read_text(encoding="utf-8")): + if token.type != "inline" or token.children is None: + continue + for child in token.children: + attribute = "href" if child.type == "link_open" else "src" + if child.type not in {"link_open", "image"}: + continue + target = child.attrGet(attribute) + if isinstance(target, str) and target: + links.append(DocumentationLink(source, target)) + return tuple(links) + + +def _check_local_links(root: Path, links: tuple[DocumentationLink, ...]) -> tuple[LinkIssue, ...]: + issues: list[LinkIssue] = [] + heading_cache: dict[Path, frozenset[str]] = {} + for link in links: + parsed = urlsplit(link.target) + if parsed.scheme or parsed.netloc: + continue + + source_path = root / link.source + target_path = source_path if not parsed.path else source_path.parent / unquote(parsed.path) + target_path = target_path.resolve() + if not target_path.is_relative_to(root) or not target_path.exists(): + issues.append(LinkIssue(link.source, link.target, "local target does not exist")) + continue + + markdown_target = _markdown_target(target_path) + if parsed.fragment and markdown_target is not None: + headings = heading_cache.setdefault(markdown_target, _heading_ids(markdown_target)) + fragment = unquote(parsed.fragment).casefold() + if fragment not in headings: + issues.append( + LinkIssue(link.source, link.target, "heading fragment does not exist") + ) + return tuple(issues) + + +def _markdown_target(path: Path) -> Path | None: + if path.is_file() and path.suffix.casefold() == ".md": + return path + if path.is_dir() and (path / "index.md").is_file(): + return path / "index.md" + return None + + +def _heading_ids(path: Path) -> frozenset[str]: + tokens = MarkdownIt().parse(path.read_text(encoding="utf-8")) + identifiers: set[str] = set() + counts: dict[str, int] = {} + for index, token in enumerate(tokens[:-1]): + if token.type != "heading_open" or tokens[index + 1].type != "inline": + continue + base = slugify(tokens[index + 1].content.strip(), "-") + count = counts.get(base, 0) + counts[base] = count + 1 + identifiers.add(base if count == 0 else f"{base}_{count}") + return frozenset(identifier.casefold() for identifier in identifiers) + + +def _check_external_links( + links: tuple[DocumentationLink, ...], + *, + excluded_url_prefixes: tuple[str, ...], + timeout: float, + retries: int, + open_url: Callable[..., object], + sleep: Callable[[float], None], +) -> tuple[LinkIssue, ...]: + sources_by_url: dict[str, set[Path]] = {} + for link in links: + parsed = urlsplit(link.target) + if parsed.scheme not in {"http", "https"}: + continue + if any(link.target.startswith(prefix) for prefix in excluded_url_prefixes): + continue + sources_by_url.setdefault(link.target, set()).add(link.source) + + issues: list[LinkIssue] = [] + for target, sources in sources_by_url.items(): + error = _external_link_error( + target, + timeout=timeout, + retries=retries, + open_url=open_url, + sleep=sleep, + ) + if error is not None: + issues.extend(LinkIssue(source, target, error) for source in sorted(sources)) + return tuple(issues) + + +def _external_link_error( + target: str, + *, + timeout: float, + retries: int, + open_url: Callable[..., object], + sleep: Callable[[float], None], +) -> str | None: + for attempt in range(retries + 1): + try: + status = _external_status(target, timeout=timeout, open_url=open_url) + if status < 400: + return None + return f"external target returned HTTP {status}" + except HTTPError as error: + if error.code in {401, 403}: + return None + if error.code not in {408, 425, 429, 500, 502, 503, 504}: + return f"external target returned HTTP {error.code}" + message = f"external target returned HTTP {error.code}" + except (TimeoutError, URLError) as error: + reason = error.reason if isinstance(error, URLError) else error + message = f"external target could not be reached ({reason})" + + if attempt < retries: + sleep(0.5 * (2**attempt)) + return message + + +def _external_status( + target: str, + *, + timeout: float, + open_url: Callable[..., object], +) -> int: + headers = {"User-Agent": USER_AGENT} + try: + response = open_url(Request(target, method="HEAD", headers=headers), timeout=timeout) + except HTTPError as error: + if error.code not in {405, 501}: + raise + error.close() + response = open_url(Request(target, method="GET", headers=headers), timeout=timeout) + + status = getattr(response, "status", 200) + close = getattr(response, "close", None) + if callable(close): + close() + return status diff --git a/tests/test_docs.py b/tests/test_docs.py new file mode 100644 index 0000000..66f5089 --- /dev/null +++ b/tests/test_docs.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from email.message import Message +from pathlib import Path +from urllib.error import HTTPError, URLError +from urllib.request import Request + +import pytest + +import repo_quality.docs +from repo_quality.docs import build_main, check_links, format_main, links_main + + +class Response: + status = 200 + + def close(self) -> None: + pass + + +def write_markdown(root: Path, relative_path: str, content: str) -> None: + path = root / relative_path + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + + +def test_check_links_accepts_existing_targets_and_fragments(tmp_path: Path) -> None: + write_markdown( + tmp_path, + "README.md", + "# Project\n\n[Guide](docs/guide.md#first-steps)\n", + ) + write_markdown(tmp_path, "docs/guide.md", "# Guide\n\n## First Steps\n") + + issues = check_links(tmp_path, check_external=False) + + assert issues == () + + +def test_check_links_reports_missing_targets_and_fragments(tmp_path: Path) -> None: + write_markdown( + tmp_path, + "README.md", + "# Project\n\n[Missing](docs/missing.md)\n[Section](#missing-section)\n", + ) + + issues = check_links(tmp_path, check_external=False) + + assert [(issue.target, issue.message) for issue in issues] == [ + ("docs/missing.md", "local target does not exist"), + ("#missing-section", "heading fragment does not exist"), + ] + + +def test_check_links_retries_transient_external_failures(tmp_path: Path) -> None: + write_markdown(tmp_path, "README.md", "# Project\n\n[Service](https://example.com)\n") + attempts = 0 + delays: list[float] = [] + + def open_url(*_args: object, **_kwargs: object) -> Response: + nonlocal attempts + attempts += 1 + if attempts == 1: + raise URLError("temporary failure") + return Response() + + issues = check_links( + tmp_path, + retries=2, + open_url=open_url, + sleep=delays.append, + ) + + assert issues == () + assert attempts == 2 + assert delays == [0.5] + + +def test_check_links_reports_permanent_external_failure(tmp_path: Path) -> None: + write_markdown(tmp_path, "README.md", "# Project\n\n[Missing](https://example.com/404)\n") + + def open_url(*_args: object, **_kwargs: object) -> Response: + raise HTTPError("https://example.com/404", 404, "Not Found", Message(), None) + + issues = check_links(tmp_path, open_url=open_url) + + assert [(issue.target, issue.message) for issue in issues] == [ + ("https://example.com/404", "external target returned HTTP 404") + ] + + +def test_check_links_falls_back_to_get_when_head_is_unsupported(tmp_path: Path) -> None: + write_markdown(tmp_path, "README.md", "# Project\n\n[Service](https://example.com)\n") + methods: list[str] = [] + + def open_url(request: Request, **_kwargs: object) -> Response: + methods.append(request.get_method()) + if request.get_method() == "HEAD": + raise HTTPError(request.full_url, 405, "Method Not Allowed", Message(), None) + return Response() + + issues = check_links(tmp_path, open_url=open_url) + + assert issues == () + assert methods == ["HEAD", "GET"] + + +def test_check_links_honors_external_url_exclusions(tmp_path: Path) -> None: + write_markdown(tmp_path, "README.md", "# Project\n\n[Status](https://status.example.com/x)\n") + + def fail_if_called(*_args: object, **_kwargs: object) -> Response: + raise AssertionError("excluded URL was requested") + + issues = check_links( + tmp_path, + excluded_url_prefixes=("https://status.example.com/",), + open_url=fail_if_called, + ) + + assert issues == () + + +def test_links_command_reports_issues(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + write_markdown(tmp_path, "README.md", "# Project\n\n[Missing](missing.md)\n") + + exit_code = links_main([str(tmp_path), "--no-external"]) + + assert exit_code == 1 + assert "local target does not exist" in capsys.readouterr().out + + +def test_format_command_checks_all_markdown_files( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + write_markdown(tmp_path, "README.md", "# Project\n") + calls: list[tuple[Path, str, tuple[str, ...]]] = [] + + def run_module(root: Path, module: str, *arguments: str) -> int: + calls.append((root, module, arguments)) + return 0 + + monkeypatch.setattr(repo_quality.docs, "_run_module", run_module) + + exit_code = format_main([str(tmp_path)]) + + assert exit_code == 0 + assert calls == [(tmp_path, "mdformat", ("--check", "--number", "README.md"))] + + +def test_build_command_uses_strict_mkdocs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + calls: list[tuple[Path, str, tuple[str, ...]]] = [] + + def run_module(root: Path, module: str, *arguments: str) -> int: + calls.append((root, module, arguments)) + return 0 + + monkeypatch.setattr(repo_quality.docs, "_run_module", run_module) + + exit_code = build_main([str(tmp_path)]) + + assert exit_code == 0 + assert calls == [(tmp_path, "mkdocs", ("build", "--strict", "--config-file", "mkdocs.yml"))] diff --git a/uv.lock b/uv.lock index 5f036c8..d691129 100644 --- a/uv.lock +++ b/uv.lock @@ -81,6 +81,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, ] +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -121,6 +133,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/4a/e213905d3b8ad3d35d14fc056b36134a274e7f6a1050e94428b5be10a94c/filelock-3.31.0-py3-none-any.whl", hash = "sha256:739b73e580fe88bb78d830aeddbc492519ece3d97ac8368de13a2032c61010c1", size = 96080, upload-time = "2026-07-18T05:53:27.732Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + [[package]] name = "identify" version = "2.6.19" @@ -148,6 +172,27 @@ 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" }, ] +[[package]] +name = "jinja2" +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 = [ + { 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]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + [[package]] name = "markdown-it-py" version = "4.2.0" @@ -160,6 +205,81 @@ 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" }, ] +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mdformat" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/05/32b5e14b192b0a8a309f32232c580aefedd9d06017cb8fe8fce34bec654c/mdformat-1.0.0.tar.gz", hash = "sha256:4954045fcae797c29f86d4ad879e43bb151fa55dbaf74ac6eaeacf1d45bb3928", size = 56953, upload-time = "2025-10-16T12:05:03.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/9a/8fe71b95985ca7a4001effbcc58e5a07a1f2a2884203f74dcf48a3b08315/mdformat-1.0.0-py3-none-any.whl", hash = "sha256:bca015d65a1d063a02e885a91daee303057bc7829c2cd37b2075a50dbb65944b", size = 53288, upload-time = "2025-10-16T12:05:02.607Z" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -169,6 +289,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, +] + [[package]] name = "nodeenv" version = "1.10.0" @@ -187,6 +354,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "platformdirs" version = "4.10.1" @@ -258,6 +434,18 @@ 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" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-discovery" version = "1.4.4" @@ -317,12 +505,27 @@ wheels = [ { 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]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "repo-quality-reference" version = "0.1.0" source = { editable = "." } dependencies = [ + { name = "markdown" }, { name = "markdown-it-py" }, + { name = "mdformat" }, + { name = "mkdocs" }, { name = "pyyaml" }, ] @@ -338,7 +541,10 @@ dev = [ [package.metadata] requires-dist = [ + { name = "markdown", specifier = ">=3.10,<4" }, { name = "markdown-it-py", specifier = ">=3.0.0,<5" }, + { name = "mdformat", specifier = ">=1.0.0,<2" }, + { name = "mkdocs", specifier = ">=1.6.1,<2" }, { name = "pyyaml", specifier = ">=6.0.2,<7" }, ] @@ -401,6 +607,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "ty" version = "0.0.61" @@ -449,3 +664,27 @@ sdist = { url = "https://files.pythonhosted.org/packages/34/d9/b477fddb68840b570 wheels = [ { url = "https://files.pythonhosted.org/packages/c1/7c/4e7225d46d634a0d8d534dd8a6ce0c319d09b4d0cf0337eb314ca4789d8c/virtualenv-21.6.1-py3-none-any.whl", hash = "sha256:afe991df855715a2b2f60edfcc0107ef95a79fdfd8cb4cdaa71603d1c12e463b", size = 5506392, upload-time = "2026-07-10T19:33:51.629Z" }, ] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +]