From c2956da59f16e60d596cdfdc84f98097712148ba Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Wed, 16 Oct 2024 12:52:17 +1300 Subject: [PATCH] Feature/22 implement usethis tool pre commit (#25) * Add deptry function which installs deptry via uv as a dev dependency. * Add progress message for usethis tool deptry. * Add tests for running deptry after calling `usethis tool deptry`. * Configure the package as a CLI app using typer. * Add pre_commit function to add pre-commit as a dev dep * Create a .pre-commit-config.yaml file automatically. * Add complete logic for adding pre-commit. * Update lockfile * Setup git user in CI * Use global git user config. --- .github/workflows/ci.yml | 5 + pyproject.toml | 8 +- src/usethis/_deptry/core.py | 1 + src/usethis/_git.py | 43 +++++ src/usethis/_pre_commit/config.py | 19 +++ src/usethis/_pre_commit/core.py | 138 ++++++++++++++++ src/usethis/_test.py | 15 ++ src/usethis/_tool.py | 128 +++++++++++++++ src/usethis/tool.py | 23 ++- tests/usethis/test_pre_commit.py | 110 +++++++++++++ tests/usethis/test_tool.py | 257 +++++++++++++++++++++++++++--- uv.lock | 222 +++++++++++++++++++++----- 12 files changed, 904 insertions(+), 65 deletions(-) create mode 100644 src/usethis/_deptry/core.py create mode 100644 src/usethis/_git.py create mode 100644 src/usethis/_pre_commit/config.py create mode 100644 src/usethis/_pre_commit/core.py create mode 100644 src/usethis/_test.py create mode 100644 src/usethis/_tool.py create mode 100644 tests/usethis/test_pre_commit.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c36cec6..3ddd99b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,11 @@ jobs: - name: Checkout code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Setup git user config + run: | + git config --global user.name github-actions[bot] + git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com + - name: Setup uv and handle its cache uses: hynek/setup-cached-uv@49a39f911c85c6ec0c9aadd5a426ae2761afaba2 # v2.0.0 diff --git a/pyproject.toml b/pyproject.toml index 9db7284..3613057 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,13 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [ + "gitpython>=3.1.43", + "packaging>=24.1", + "pydantic>=2.9.2", + "requests>=2.32.3", "rich>=13.8.1", + "ruamel-yaml>=0.18.6", + "tomlkit>=0.13.2", "typer>=0.12.5", ] @@ -21,7 +27,5 @@ dev-dependencies = [ "pytest>=8.3.2", "pytest-md>=0.2.0", "pytest-emoji>=0.2.0", - "pydantic>=2.9.1", - "tomlkit>=0.13.2", "deptry>=0.20.0", ] diff --git a/src/usethis/_deptry/core.py b/src/usethis/_deptry/core.py new file mode 100644 index 0000000..f141856 --- /dev/null +++ b/src/usethis/_deptry/core.py @@ -0,0 +1 @@ +PRE_COMMIT_NAME = "deptry" diff --git a/src/usethis/_git.py b/src/usethis/_git.py new file mode 100644 index 0000000..fa37fe6 --- /dev/null +++ b/src/usethis/_git.py @@ -0,0 +1,43 @@ +import requests + + +class _GitHubTagError(Exception): + """Custom exception for GitHub tag-related errors.""" + + +class _NoTagsFoundError(_GitHubTagError): + """Custom exception raised when no tags are found.""" + + +def _get_github_latest_tag(owner: str, repo: str) -> str: + """Get the name of the most recent tag on the default branch of a GitHub repository. + + Args: + owner: GitHub repository owner (username or organization). + repo: GitHub repository name. + + Returns: + The name of most recent tag of the repository. + + Raises: + GitHubTagError: If there's an issue fetching the tags from the GitHub API. + NoTagsFoundError: If the repository has no tags. + """ + + # GitHub API URL for repository tags + api_url = f"https://api.github.com/repos/{owner}/{repo}/tags" + + # Fetch the tags using the GitHub API + try: + response = requests.get(api_url, timeout=1) + response.raise_for_status() # Raise an error for HTTP issues + except requests.exceptions.HTTPError as err: + raise _GitHubTagError(f"Failed to fetch tags from GitHub API: {err}") + + tags = response.json() + + if not tags: + raise _NoTagsFoundError(f"No tags found for repository '{owner}/{repo}'") + + # Most recent tag's name + return tags[0]["name"] diff --git a/src/usethis/_pre_commit/config.py b/src/usethis/_pre_commit/config.py new file mode 100644 index 0000000..f48d079 --- /dev/null +++ b/src/usethis/_pre_commit/config.py @@ -0,0 +1,19 @@ +from typing import Literal + +from pydantic import BaseModel + + +class HookConfig(BaseModel): + id: str + name: str + entry: str | None = None + language: Literal["system", "python"] | None = None + always_run: bool | None = None + pass_filenames: bool | None = None + additional_dependencies: list[str] | None = None + + +class PreCommitRepoConfig(BaseModel): + repo: str + rev: str | None = None + hooks: list[HookConfig] diff --git a/src/usethis/_pre_commit/core.py b/src/usethis/_pre_commit/core.py new file mode 100644 index 0000000..0d50fad --- /dev/null +++ b/src/usethis/_pre_commit/core.py @@ -0,0 +1,138 @@ +import subprocess +from collections import Counter +from pathlib import Path + +import ruamel.yaml +from ruamel.yaml.util import load_yaml_guess_indent + +from usethis import console +from usethis._deptry.core import PRE_COMMIT_NAME as DEPTRY_PRE_COMMIT_NAME +from usethis._git import _get_github_latest_tag, _GitHubTagError +from usethis._pre_commit.config import PreCommitRepoConfig + +_YAML_CONTENTS_TEMPLATE = """ +repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: "{pkg_version}" + hooks: + - id: validate-pyproject + additional_dependencies: ["validate-pyproject-schema-store[all]"] +""" +# Manually bump this version when necessary +_VALIDATEPYPROJECT_VERSION = "v0.21" + +_HOOK_ORDER = [ + "validate-pyproject", + DEPTRY_PRE_COMMIT_NAME, +] + + +def make_pre_commit_config() -> None: + console.print("✔ Creating .pre-commit-config.yaml file", style="green") + try: + pkg_version = _get_github_latest_tag("abravalheri", "validate-pyproject") + except _GitHubTagError: + # Fallback to last known working version + pkg_version = _VALIDATEPYPROJECT_VERSION + yaml_contents = _YAML_CONTENTS_TEMPLATE.format(pkg_version=pkg_version) + + (Path.cwd() / ".pre-commit-config.yaml").write_text(yaml_contents) + + +def delete_hook(name: str) -> None: + path = Path.cwd() / ".pre-commit-config.yaml" + + with path.open(mode="r") as f: + content, sequence_ind, offset_ind = load_yaml_guess_indent(f) + + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=sequence_ind, sequence=sequence_ind, offset=offset_ind) + + # search across the repos for any hooks with ID equal to name + for repo in content["repos"]: + for hook in repo["hooks"]: + if hook["id"] == name: + repo["hooks"].remove(hook) + + # if repo has no hooks, remove it + if not repo["hooks"]: + content["repos"].remove(repo) + + yaml.dump(content, path) + + +def add_single_hook(config: PreCommitRepoConfig) -> None: + # We should have a canonical sort order for all usethis-supported hooks to decide where to place the section. The main objective with the sort order is to ensure dependency relationships are satisfied. For example, valdiate-pyproject will check if the pyproject.toml is valid - if it isn't then some later tools might fail. It would be better to catch this earlier. A general principle is to move from the simpler hooks to the more complicated. Of course, this order might already be violated, or the config might include unrecognized repos - in any case, we aim to ensure the new tool is configured correctly, so it should be placed after the last of its precedents. This logic can be encoded in the adding function. + + path = Path.cwd() / ".pre-commit-config.yaml" + + with path.open(mode="r") as f: + content, sequence_ind, offset_ind = load_yaml_guess_indent(f) + + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=sequence_ind, sequence=sequence_ind, offset=offset_ind) + + (hook_config,) = config.hooks + hook_name = hook_config.id + + # Get an ordered list of the hooks already in the file + existing_hooks = get_hook_names(path.parent) + + # Get the precendents, i.e. hooks occuring before the new hook + hook_idx = _HOOK_ORDER.index(hook_name) + if hook_idx == -1: + raise ValueError(f"Hook {hook_name} not recognized") + precedents = _HOOK_ORDER[:hook_idx] + + # Find the last of the precedents in the existing hooks + existings_precedents = [hook for hook in existing_hooks if hook in precedents] + if existings_precedents: + last_precedent = existings_precedents[-1] + else: + # Use the last existing hook + last_precedent = existing_hooks[-1] + + # Insert the new hook after the last precedent repo + # Do this by iterating over the repos and hooks, and inserting the new hook after + # the last precedent + new_repos = [] + for repo in content["repos"]: + new_repos.append(repo) + for hook in repo["hooks"]: + if hook["id"] == last_precedent: + new_repos.append(config.model_dump(exclude_none=True)) + content["repos"] = new_repos + + # Dump the new content + yaml.dump(content, path) + + +def get_hook_names(path: Path) -> list[str]: + yaml = ruamel.yaml.YAML() + with (path / ".pre-commit-config.yaml").open(mode="r") as f: + content = yaml.load(f) + + hook_names = [] + for repo in content["repos"]: + for hook in repo["hooks"]: + hook_names.append(hook["id"]) + + # Need to validate there are no duplciates + for name, count in Counter(hook_names).items(): + if count > 1: + raise DuplicatedHookNameError(f"Hook name '{name}' is duplicated") + + return hook_names + + +class DuplicatedHookNameError(ValueError): + """Raised when a hook name is duplicated in a pre-commit configuration file.""" + + +def install_pre_commit() -> None: + console.print("✔ Installing pre-commit hooks", style="green") + subprocess.run( + ["uv", "run", "pre-commit", "install"], + check=True, + stdout=subprocess.DEVNULL, + ) diff --git a/src/usethis/_test.py b/src/usethis/_test.py new file mode 100644 index 0000000..0e8bd1a --- /dev/null +++ b/src/usethis/_test.py @@ -0,0 +1,15 @@ +import os +from contextlib import contextmanager +from pathlib import Path +from typing import Generator + + +@contextmanager +def change_cwd(new_dir: Path) -> Generator[None, None, None]: + """Change the working directory temporarily.""" + old_dir = Path.cwd() + os.chdir(new_dir) + try: + yield + finally: + os.chdir(old_dir) diff --git a/src/usethis/_tool.py b/src/usethis/_tool.py new file mode 100644 index 0000000..d5242f3 --- /dev/null +++ b/src/usethis/_tool.py @@ -0,0 +1,128 @@ +import subprocess +from abc import abstractmethod +from pathlib import Path +from typing import Protocol + +import tomlkit +from packaging.requirements import Requirement +from pydantic import TypeAdapter + +from usethis import console +from usethis._deptry.core import PRE_COMMIT_NAME as DEPTRY_PRE_COMMIT_NAME +from usethis._pre_commit.config import HookConfig, PreCommitRepoConfig +from usethis._pre_commit.core import ( + add_single_hook, + get_hook_names, + make_pre_commit_config, +) + + +class Tool(Protocol): + @property + @abstractmethod + def pypi_name(self) -> str: + """The name of the tool on PyPI.""" + + @property + @abstractmethod + def pre_commit_name(self) -> str: + """The name of the hook to be used in the pre-commit configuration. + + Raises: + NotImplementedError: If the tool does not have a pre-commit configuration. + """ + + raise NotImplementedError + + @abstractmethod + def get_pre_commit_repo_config(self) -> PreCommitRepoConfig: + """Get the pre-commit repository configuration for the tool. + + Returns: + The pre-commit repository configuration. + + Raises: + NotImplementedError: If the tool does not have a pre-commit configuration. + """ + raise NotImplementedError + + def is_used(self) -> bool: + """Whether the tool is being used in the current project.""" + return self.pypi_name in _get_dev_deps(Path.cwd()) + + def add_pre_commit_repo_config(self) -> None: + """Add the tool's pre-commit configuration.""" + # Create a new pre-commit config file if there isn't already one. + if not (Path.cwd() / ".pre-commit-config.yaml").exists(): + make_pre_commit_config() + + try: + pre_commit_name = self.pre_commit_name + repo_config = self.get_pre_commit_repo_config() + except NotImplementedError: + return + + # Add the config for this specific tool. + if pre_commit_name not in get_hook_names(Path.cwd()): + console.print( + f"✔ Adding {pre_commit_name} config to .pre-commit-config.yaml", + style="green", + ) + add_single_hook(repo_config) + + def ensure_dev_dep(self) -> None: + """Add the tool as a development dependency, if it is not already.""" + console.print( + f"✔ Ensuring {self.pypi_name} is a development dependency", style="green" + ) + subprocess.run(["uv", "add", "--dev", "--quiet", self.pypi_name], check=True) + + +def _get_dev_deps(proj_dir: Path) -> list[str]: + pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text()) + req_strs = TypeAdapter(list[str]).validate_python( + pyproject["tool"]["uv"]["dev-dependencies"] + ) + reqs = [Requirement(req_str) for req_str in req_strs] + return [req.name for req in reqs] + + +class PreCommitTool(Tool): + @property + def pypi_name(self) -> str: + return "pre-commit" + + @property + def pre_commit_name(self) -> str: + raise NotImplementedError + + def get_pre_commit_repo_config(self) -> PreCommitRepoConfig: + raise NotImplementedError + + +class DeptryTool(Tool): + @property + def pypi_name(self) -> str: + return "deptry" + + @property + def pre_commit_name(self) -> str: + return DEPTRY_PRE_COMMIT_NAME + + def get_pre_commit_repo_config(self) -> PreCommitRepoConfig: + return PreCommitRepoConfig( + repo="local", + hooks=[ + HookConfig( + id=self.pre_commit_name, + name=self.pre_commit_name, + entry="uv run --frozen deptry src", + language="system", + always_run=True, + pass_filenames=False, + ) + ], + ) + + +ALL_TOOLS: list[Tool] = [PreCommitTool(), DeptryTool()] diff --git a/src/usethis/tool.py b/src/usethis/tool.py index 28f76e8..f764e1f 100644 --- a/src/usethis/tool.py +++ b/src/usethis/tool.py @@ -1,8 +1,7 @@ -import subprocess - import typer -from usethis import console +from usethis._pre_commit.core import install_pre_commit +from usethis._tool import ALL_TOOLS, DeptryTool, PreCommitTool app = typer.Typer(help="Add and configure development tools, e.g. linters") @@ -11,5 +10,19 @@ help="Use the deptry linter: avoid missing or superfluous dependency declarations." ) def deptry() -> None: - console.print("✔ Ensuring deptry is a development dependency", style="green") - subprocess.run(["uv", "add", "--dev", "--quiet", "deptry"], check=True) + tool = DeptryTool() + tool.ensure_dev_dep() + if PreCommitTool().is_used(): + tool.add_pre_commit_repo_config() + + +@app.command( + help="Use the pre-commit framework to manage and maintain pre-commit hooks." +) +def pre_commit() -> None: + tool = PreCommitTool() + tool.ensure_dev_dep() + for tool in ALL_TOOLS: + if tool.is_used(): + tool.add_pre_commit_repo_config() + install_pre_commit() diff --git a/tests/usethis/test_pre_commit.py b/tests/usethis/test_pre_commit.py new file mode 100644 index 0000000..ed3704f --- /dev/null +++ b/tests/usethis/test_pre_commit.py @@ -0,0 +1,110 @@ +from pathlib import Path + +import pytest + +from usethis._pre_commit.core import ( + DuplicatedHookNameError, + delete_hook, + get_hook_names, +) +from usethis._test import change_cwd + + +class TestDeleteHook: + def test_empty(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text("repos: []\n") + with change_cwd(tmp_path): + delete_hook("foo") + assert (tmp_path / ".pre-commit-config.yaml").read_text() == "repos: []\n" + + def test_single(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """repos: + - repo: foo + hooks: + - id: bar +""" + ) + with change_cwd(tmp_path): + delete_hook("bar") + assert (tmp_path / ".pre-commit-config.yaml").read_text() == "repos: []\n" + + def test_multihooks(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """repos: + - repo: foo # comment + hooks: + - id: bar + - id: baz +""" + ) + with change_cwd(tmp_path): + delete_hook("bar") + assert (tmp_path / ".pre-commit-config.yaml").read_text() == ( + """repos: + - repo: foo # comment + hooks: + - id: baz +""" + ) + + +class TestGetHookNames: + def test_empty(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text("repos: []\n") + assert get_hook_names(tmp_path) == [] + + def test_single(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """ +repos: + - repo: foo + hooks: + - id: bar +""" + ) + assert get_hook_names(tmp_path) == ["bar"] + + def test_multihooks(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """ +repos: + - repo: foo + hooks: + - id: bar + - id: baz +""" + ) + assert get_hook_names(tmp_path) == ["bar", "baz"] + + def test_multirepo(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """ +repos: + - repo: foo + hooks: + - id: bar + - repo: baz + hooks: + - id: qux +""" + ) + assert get_hook_names(tmp_path) == ["bar", "qux"] + + def test_duplicated_raises(self, tmp_path: Path): + (tmp_path / ".pre-commit-config.yaml").write_text( + """ +repos: + - repo: foo + hooks: + - id: bar + - repo: baz + hooks: + - id: bar +""" + ) + + with pytest.raises( + DuplicatedHookNameError, match="Hook name 'bar' is duplicated" + ): + get_hook_names(tmp_path) diff --git a/tests/usethis/test_tool.py b/tests/usethis/test_tool.py index 352ffec..fd7d72b 100644 --- a/tests/usethis/test_tool.py +++ b/tests/usethis/test_tool.py @@ -1,30 +1,29 @@ -import os import subprocess -from contextlib import contextmanager from pathlib import Path -from typing import Generator import pytest -import tomlkit -from pydantic import TypeAdapter +from git import Repo -from usethis.tool import deptry +from usethis._pre_commit.core import ( + _HOOK_ORDER, + _VALIDATEPYPROJECT_VERSION, + get_hook_names, +) +from usethis._test import change_cwd +from usethis._tool import ALL_TOOLS, _get_dev_deps +from usethis.tool import deptry, pre_commit -@contextmanager -def change_cwd(new_dir: Path) -> Generator[None, None, None]: - """Change the working directory temporarily.""" - old_dir = Path.cwd() - os.chdir(new_dir) - try: - yield - finally: - os.chdir(old_dir) +@pytest.fixture(scope="function") +def uv_init_dir(tmp_path: Path) -> Path: + subprocess.run(["uv", "init"], cwd=tmp_path, check=True) + return tmp_path -@pytest.fixture -def uv_init_dir(tmp_path: Path) -> None: +@pytest.fixture(scope="function") +def uv_init_repo_dir(tmp_path: Path) -> Path: subprocess.run(["uv", "init"], cwd=tmp_path, check=True) + Repo.init(tmp_path) return tmp_path @@ -36,7 +35,7 @@ def test_dependency_added(self, uv_init_dir: Path): # Assert (dev_dep,) = _get_dev_deps(uv_init_dir) - assert dev_dep.startswith("deptry>=") + assert dev_dep == "deptry" def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]): # Act @@ -75,8 +74,222 @@ def test_run_deptry_pass(self, uv_init_dir: Path): def test_cli(self, uv_init_dir: Path): subprocess.run(["usethis", "tool", "deptry"], cwd=uv_init_dir, check=True) + def test_pre_commit_after( + self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str] + ): + # Act + with change_cwd(uv_init_dir): + deptry() + pre_commit() + + # Assert + # 1. File exists + assert (uv_init_dir / ".pre-commit-config.yaml").exists() + + # 2. Hook is in the file + assert "deptry" in get_hook_names(uv_init_dir) + + # 3. Test file contents + assert (uv_init_dir / ".pre-commit-config.yaml").read_text() == ( + f"""repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: {_VALIDATEPYPROJECT_VERSION} + hooks: + - id: validate-pyproject + additional_dependencies: ['validate-pyproject-schema-store[all]'] + - repo: local + hooks: + - id: deptry + name: deptry + entry: uv run --frozen deptry src + language: system + always_run: true + pass_filenames: false +""" + ) + + # 4. Check messages + out, _ = capfd.readouterr() + assert out == ( + "✔ Ensuring deptry is a development dependency\n" + "✔ Ensuring pre-commit is a development dependency\n" + "✔ Creating .pre-commit-config.yaml file\n" + "✔ Adding deptry config to .pre-commit-config.yaml\n" + "✔ Installing pre-commit hooks\n" + ) + + def test_pre_commit_first( + self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str] + ): + # Act + with change_cwd(uv_init_dir): + pre_commit() + deptry() + + # Assert + # 1. File exists + assert (uv_init_dir / ".pre-commit-config.yaml").exists() + + # 2. Hook is in the file + assert "deptry" in get_hook_names(uv_init_dir) + + # 3. Test file contents + assert (uv_init_dir / ".pre-commit-config.yaml").read_text() == ( + f"""repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: {_VALIDATEPYPROJECT_VERSION} + hooks: + - id: validate-pyproject + additional_dependencies: ['validate-pyproject-schema-store[all]'] + - repo: local + hooks: + - id: deptry + name: deptry + entry: uv run --frozen deptry src + language: system + always_run: true + pass_filenames: false +""" + ) + + # 4. Check messages + out, _ = capfd.readouterr() + assert out == ( + "✔ Ensuring pre-commit is a development dependency\n" + "✔ Creating .pre-commit-config.yaml file\n" + "✔ Installing pre-commit hooks\n" + "✔ Ensuring deptry is a development dependency\n" + "✔ Adding deptry config to .pre-commit-config.yaml\n" + ) + + +class TestToolPreCommit: + def test_dependency_added(self, uv_init_dir: Path): + # Act + with change_cwd(uv_init_dir): + pre_commit() + + # Assert + (dev_dep,) = _get_dev_deps(uv_init_dir) + assert dev_dep == "pre-commit" + + def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]): + # Act + with change_cwd(uv_init_dir): + pre_commit() + + # Assert + out, _ = capfd.readouterr() + assert out == ( + "✔ Ensuring pre-commit is a development dependency\n" + "✔ Creating .pre-commit-config.yaml file\n" + "✔ Installing pre-commit hooks\n" + ) + + def test_config_file_exists(self, uv_init_dir: Path): + # Act + with change_cwd(uv_init_dir): + pre_commit() + + # Assert + assert (uv_init_dir / ".pre-commit-config.yaml").exists() + + def test_config_file_contents(self, uv_init_dir: Path): + # Act + with change_cwd(uv_init_dir): + pre_commit() + + # Assert + contents = (uv_init_dir / ".pre-commit-config.yaml").read_text() + assert contents == ( + f""" +repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: "{_VALIDATEPYPROJECT_VERSION}" + hooks: + - id: validate-pyproject + additional_dependencies: ["validate-pyproject-schema-store[all]"] +""" + ) + + def test_already_exists(self, uv_init_repo_dir: Path): + # Arrange + (uv_init_repo_dir / ".pre-commit-config.yaml").write_text( + """ +repos: + - repo: foo + hooks: + - id: bar +""" + ) + + # Act + with change_cwd(uv_init_repo_dir): + pre_commit() + + # Assert + contents = (uv_init_repo_dir / ".pre-commit-config.yaml").read_text() + assert contents == ( + """ +repos: + - repo: foo + hooks: + - id: bar +""" + ) + + def test_bad_commit(self, uv_init_repo_dir: Path): + # Act + with change_cwd(uv_init_repo_dir): + pre_commit() + subprocess.run(["git", "add", "."], cwd=uv_init_repo_dir, check=True) + subprocess.run( + ["git", "commit", "-m", "Good commit"], cwd=uv_init_repo_dir, check=True + ) + + # Assert + with pytest.raises(subprocess.CalledProcessError): + (uv_init_repo_dir / "pyproject.toml").write_text("[") + subprocess.run(["git", "add", "."], cwd=uv_init_repo_dir, check=True) + subprocess.run( + ["git", "commit", "-m", "Bad commit"], cwd=uv_init_repo_dir, check=True + ) + + def test_cli_pass(self, uv_init_repo_dir: Path): + subprocess.run( + ["usethis", "tool", "pre-commit"], cwd=uv_init_repo_dir, check=True + ) + + subprocess.run( + ["uv", "run", "pre-commit", "run", "--all-files"], + cwd=uv_init_repo_dir, + check=True, + ) + + def test_cli_fail(self, uv_init_repo_dir: Path): + subprocess.run( + ["usethis", "tool", "pre-commit"], cwd=uv_init_repo_dir, check=True + ) + + # Pass invalid TOML to fail the pre-commit for validate-pyproject + (uv_init_repo_dir / "pyproject.toml").write_text("[") + try: + subprocess.run( + ["uv", "run", "pre-commit", "run", "--all-files"], + cwd=uv_init_repo_dir, + check=True, + ) + except subprocess.CalledProcessError: + pass + else: + pytest.fail("Expected subprocess.CalledProcessError") + -def _get_dev_deps(proj_dir: Path) -> list[str]: - pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text()) - dev_deps = pyproject["tool"]["uv"]["dev-dependencies"] - return TypeAdapter(list[str]).validate_python(dev_deps) +class TestAllHooksList: + def test_subset_hook_names(self): + for tool in ALL_TOOLS: + try: + hook_name = tool.pre_commit_name + except NotImplementedError: + continue + assert hook_name in _HOOK_ORDER diff --git a/uv.lock b/uv.lock index 461173b..9f85591 100644 --- a/uv.lock +++ b/uv.lock @@ -14,6 +14,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, + { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, + { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, + { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, + { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, + { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, + { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, + { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, + { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, + { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, + { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, + { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, + { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, + { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + [[package]] name = "click" version = "8.1.7" @@ -53,6 +101,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/53/c9/9d7d86b5fdc452b522ef16df9e27c8404dc6f231fa865a3af31c1dab7563/deptry-0.20.0-cp38-abi3-win_arm64.whl", hash = "sha256:6886ff44aaf26fd83093f14f844ebc84589d90df9bbad9a1625e8a080e6f1be2", size = 1420087 }, ] +[[package]] +name = "gitdb" +version = "4.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/0d/bbb5b5ee188dec84647a4664f3e11b06ade2bde568dbd489d9d64adef8ed/gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b", size = 394469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", size = 62721 }, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a1/106fd9fa2dd989b6fb36e5893961f82992cf676381707253e0bf93eb1662/GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", size = 214149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/bd/cc3a402a6439c15c3d4294333e13042b915bbeab54edc457c723931fed3f/GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff", size = 207337 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -103,51 +184,51 @@ wheels = [ [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/15/3d989541b9c8128b96d532cfd2dd10131ddcc75a807330c00feb3d42a5bd/pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2", size = 768511 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/28/fff23284071bc1ba419635c7e86561c8b9b8cf62a5bcb459b92d7625fd38/pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612", size = 434363 }, + { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, ] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/cc/07bec3fb337ff80eacd6028745bd858b9642f61ee58cfdbfb64451c1def0/pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690", size = 402277 } +sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/35/6d81bc4aa7d06e716f39e2bffb0eabcbcebaf7bab94c2f8278e277ded0ea/pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305", size = 1845250 }, - { url = "https://files.pythonhosted.org/packages/18/42/0821cd46f76406e0fe57df7a89d6af8fddb22cce755bcc2db077773c7d1a/pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb", size = 1769993 }, - { url = "https://files.pythonhosted.org/packages/e5/55/b969088e48bd8ea588548a7194d425de74370b17b385cee4d28f5a79013d/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa", size = 1791250 }, - { url = "https://files.pythonhosted.org/packages/43/c1/1d460d09c012ac76b68b2a1fd426ad624724f93b40e24a9a993763f12c61/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162", size = 1802530 }, - { url = "https://files.pythonhosted.org/packages/70/8e/fd3c9eda00fbdadca726f17a0f863ecd871a65b3a381b77277ae386d3bcd/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801", size = 1997848 }, - { url = "https://files.pythonhosted.org/packages/f0/67/13fa22d7b09395e83721edc31bae2bd5c5e2c36a09d470c18f5d1de46958/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb", size = 2662790 }, - { url = "https://files.pythonhosted.org/packages/fa/1b/1d689c53d15ab67cb0df1c3a2b1df873b50409581e93e4848289dce57e2f/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326", size = 2074114 }, - { url = "https://files.pythonhosted.org/packages/3d/d9/b565048609db77760b9a0900f6e0a3b2f33be47cd3c4a433f49653a0d2b5/pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c", size = 1918153 }, - { url = "https://files.pythonhosted.org/packages/41/94/8ee55c51333ed8df3a6f1e73c6530c724a9a37d326e114c9e3b24faacff9/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c", size = 1969019 }, - { url = "https://files.pythonhosted.org/packages/f7/49/0233bae5778a5526cef000447a93e8d462f4f13e2214c13c5b23d379cb25/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab", size = 2121325 }, - { url = "https://files.pythonhosted.org/packages/42/a1/2f262db2fd6f9c2c9904075a067b1764cc6f71c014be5c6c91d9de52c434/pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c", size = 1725252 }, - { url = "https://files.pythonhosted.org/packages/9a/00/a57937080b49500df790c4853d3e7bc605bd0784e4fcaf1a159456f37ef1/pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b", size = 1920660 }, - { url = "https://files.pythonhosted.org/packages/e1/3c/32958c0a5d1935591b58337037a1695782e61261582d93d5a7f55441f879/pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f", size = 1845068 }, - { url = "https://files.pythonhosted.org/packages/92/a1/7e628e19b78e6ffdb2c92cccbb7eca84bfd3276cee4cafcae8833452f458/pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2", size = 1770095 }, - { url = "https://files.pythonhosted.org/packages/bb/17/d15fd8ce143cd1abb27be924eeff3c5c0fe3b0582f703c5a5273c11e67ce/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791", size = 1790964 }, - { url = "https://files.pythonhosted.org/packages/24/cc/37feff1792f09dc33207fbad3897373229279d1973c211f9562abfdf137d/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423", size = 1802384 }, - { url = "https://files.pythonhosted.org/packages/44/d8/ca9acd7f5f044d9ff6e43d7f35aab4b1d5982b4773761eabe3317fc68e30/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63", size = 1997824 }, - { url = "https://files.pythonhosted.org/packages/35/0f/146269dba21b10d5bf86f9a7a7bbeab4ce1db06f466a1ab5ec3dec68b409/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9", size = 2662907 }, - { url = "https://files.pythonhosted.org/packages/5a/7d/9573f006e39cd1a7b7716d1a264e3f4f353cf0a6042c04c01c6e31666f62/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5", size = 2073953 }, - { url = "https://files.pythonhosted.org/packages/7e/a5/25200aaafd1e97e2ec3c1eb4b357669dd93911f2eba252bc60b6ba884fff/pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855", size = 1917822 }, - { url = "https://files.pythonhosted.org/packages/3e/b4/ac069c58e3cee70c69f03693222cc173fdf740d20d53167bceafc1efc7ca/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4", size = 1968838 }, - { url = "https://files.pythonhosted.org/packages/d1/3d/9f96bbd6212b4b0a6dc6d037e446208d3420baba2b2b81e544094b18a859/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d", size = 2121468 }, - { url = "https://files.pythonhosted.org/packages/ac/50/7399d536d6600d69059a87fff89861332c97a7b3471327a3663c7576e707/pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8", size = 1725373 }, - { url = "https://files.pythonhosted.org/packages/24/ba/9ac8744ab636c1161c598cc5e8261379b6b0f1d63c31242bf9d5ed41ed32/pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1", size = 1920594 }, + { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459 }, + { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007 }, + { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245 }, + { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260 }, + { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872 }, + { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617 }, + { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831 }, + { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453 }, + { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793 }, + { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, + { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, + { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, + { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, + { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, + { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, + { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, + { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, + { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, + { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, + { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, + { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, + { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, + { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, ] [[package]] @@ -198,17 +279,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/71/23d03f57c18116c6770141478e33b3500c4e92500cf4b49a396e9226733f/pytest_md-0.2.0-py3-none-any.whl", hash = "sha256:4c4cd16fea6d1485e87ee254558712c804a96d2aa9674b780e7eb8fb6526e1d1", size = 6117 }, ] +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + [[package]] name = "rich" -version = "13.8.1" +version = "13.9.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/40f084cb7db51c9d1fa29a7120717892aeda9a7711f6225692c957a93535/rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a", size = 222080 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/9e/1784d15b057b0075e5136445aaea92d23955aad2c93eaede673718a40d95/rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c", size = 222843 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/91/5474b84e505a6ccc295b2d322d90ff6aa0746745717839ee0c5fb4fdcceb/rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1", size = 242117 }, +] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/81/4dfc17eb6ebb1aac314a3eb863c1325b907863a1b8b1382cdffcb6ac0ed9/ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b", size = 143362 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/67/8ece580cc363331d9a53055130f86b096bf16e38156e33b1d3014fffda6b/ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636", size = 117761 }, +] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/ab/bab9eb1566cd16f060b54055dd39cf6a34bfa0240c53a7218c43e974295b/ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512", size = 213824 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/11/dadb85e2bd6b1f1ae56669c3e1f0410797f9605d752d68fb47b77f525b31/rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", size = 241608 }, + { url = "https://files.pythonhosted.org/packages/7a/a2/eb5e9d088cb9d15c24d956944c09dca0a89108ad6e2e913c099ef36e3f0d/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1", size = 144636 }, + { url = "https://files.pythonhosted.org/packages/66/98/8de4f22bbfd9135deb3422e96d450c4bc0a57d38c25976119307d2efe0aa/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2", size = 135684 }, + { url = "https://files.pythonhosted.org/packages/30/d3/5fe978cd01a61c12efd24d65fa68c6f28f28c8073a06cf11db3a854390ca/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92", size = 734571 }, + { url = "https://files.pythonhosted.org/packages/55/b3/e2531a050758b717c969cbf76c103b75d8a01e11af931b94ba656117fbe9/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62", size = 643946 }, + { url = "https://files.pythonhosted.org/packages/0d/aa/06db7ca0995b513538402e11280282c615b5ae5f09eb820460d35fb69715/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9", size = 692169 }, + { url = "https://files.pythonhosted.org/packages/27/38/4cf4d482b84ecdf51efae6635cc5483a83cf5ca9d9c13e205a750e251696/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d", size = 740325 }, + { url = "https://files.pythonhosted.org/packages/6f/67/c62c6eea53a4feb042727a3d6c18f50dc99683c2b199c06bd2a9e3db8e22/ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa", size = 98639 }, + { url = "https://files.pythonhosted.org/packages/10/d2/52a3d810d0b5b3720725c0504a27b3fced7b6f310fe928f7019d79387bc1/ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b", size = 115305 }, ] [[package]] @@ -220,6 +344,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] +[[package]] +name = "smmap" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/04/b5bf6d21dc4041000ccba7eb17dd3055feb237e7ffc2c20d3fae3af62baa/smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", size = 22291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da", size = 24282 }, +] + [[package]] name = "tomlkit" version = "0.13.2" @@ -253,37 +386,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, ] +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + [[package]] name = "usethis" version = "0.1.0" source = { editable = "." } dependencies = [ + { name = "gitpython" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, { name = "rich" }, + { name = "ruamel-yaml" }, + { name = "tomlkit" }, { name = "typer" }, ] [package.dev-dependencies] dev = [ { name = "deptry" }, - { name = "pydantic" }, { name = "pytest" }, { name = "pytest-emoji" }, { name = "pytest-md" }, - { name = "tomlkit" }, ] [package.metadata] requires-dist = [ + { name = "gitpython", specifier = ">=3.1.43" }, + { name = "packaging", specifier = ">=24.1" }, + { name = "pydantic", specifier = ">=2.9.2" }, + { name = "requests", specifier = ">=2.32.3" }, { name = "rich", specifier = ">=13.8.1" }, + { name = "ruamel-yaml", specifier = ">=0.18.6" }, + { name = "tomlkit", specifier = ">=0.13.2" }, { name = "typer", specifier = ">=0.12.5" }, ] [package.metadata.requires-dev] dev = [ { name = "deptry", specifier = ">=0.20.0" }, - { name = "pydantic", specifier = ">=2.9.1" }, { name = "pytest", specifier = ">=8.3.2" }, { name = "pytest-emoji", specifier = ">=0.2.0" }, { name = "pytest-md", specifier = ">=0.2.0" }, - { name = "tomlkit", specifier = ">=0.13.2" }, ]