From 86c5263bdd81d71472c0c4eff42655d860d420ea Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Sun, 18 Jan 2026 10:36:14 +0100 Subject: [PATCH] Revert "Release-0.1.0" --- CHANGELOG.md | 10 - pyproject.toml | 5 +- src/devops/config/base.py | 72 +--- src/devops/config/config_file.py | 112 +----- src/devops/files/update_changelog.py | 2 +- src/devops/scripts/update_changelog.py | 55 +-- tests/config/test_base.py | 135 ------- tests/config/test_config.py | 4 +- tests/config/test_config_file.py | 131 ------- tests/scripts/test_update_changelog_cli.py | 407 --------------------- uv.lock | 226 +++--------- 11 files changed, 68 insertions(+), 1091 deletions(-) delete mode 100644 tests/config/test_base.py delete mode 100644 tests/config/test_config_file.py delete mode 100644 tests/scripts/test_update_changelog_cli.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 48cd7ff..69a10d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,6 @@ All notable changes to this project will be documented in this file. ## Next Release -#### Config - -- Add `changelog_paths` to config toml approach -- Add `default_changelog_path` to config toml approach - -#### API - -- Add `changelog_path` input to `update_changelog` -- Add `update_changelogs` to update multiple changelogs at once - ## [0.0.4](https://github.com/repo/owner/releases/tag/0.0.4) - 2025-12-20 diff --git a/pyproject.toml b/pyproject.toml index 1711f6c..e62e234 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,8 +15,7 @@ test = ["pytest>=9.0.1", "pytest-cov", "coverage", "docstr-coverage"] [project.scripts] cpp_checks = "devops.scripts.cpp_checks:app" -update_changelog = "devops.scripts.update_changelog:update_changelog" -update_changelogs = "devops.scripts.update_changelog:update_changelogs" +update_changelog = "devops.scripts.update_changelog:app" get_latest_tag = "devops.scripts.get_latest_git_tag:latest_tag" increase_latest_tag = "devops.scripts.get_latest_git_tag:increase_tag" generate_toml_template = "devops.scripts.generate_toml_template:app" @@ -25,4 +24,4 @@ add_license_headers = "devops.scripts.add_license_header:add_multiple_headers" filter_buggy_cpp_files = "devops.scripts.cpp_files:app" [tool.setuptools_scm] -version_file = "src/devops/__version__.py" +version_file = "src/devops/__version__.py" \ No newline at end of file diff --git a/src/devops/config/base.py b/src/devops/config/base.py index 0db1cfd..e3e3634 100644 --- a/src/devops/config/base.py +++ b/src/devops/config/base.py @@ -54,7 +54,9 @@ def get_table(mapping: dict[str, Any], key: str) -> dict[str, Any]: return value -def _get_type(mapping: dict[str, Any], key: str, expected_type: type) -> Any: +def _get_type( + mapping: dict[str, Any], key: str, default: Any, expected_type: type +) -> Any: """Get a value of expected type from a mapping. Parameters @@ -63,6 +65,8 @@ def _get_type(mapping: dict[str, Any], key: str, expected_type: type) -> Any: The mapping to extract the value from. key: str The key of the value. + default: Any + The default value to return if the key is not found. expected_type: type The expected type of the value. @@ -76,7 +80,7 @@ def _get_type(mapping: dict[str, Any], key: str, expected_type: type) -> Any: ConfigError If the value associated with the key is not of the expected type. """ - value = mapping.get(key) + value = mapping.get(key, default) if value is None: return None @@ -110,12 +114,7 @@ def get_bool( bool | None The extracted boolean value or None if the key is not found. """ - value = _get_type(mapping, key, bool) - - if value is None: - return default - - return value + return _get_type(mapping, key, default, bool) def get_str( @@ -137,52 +136,7 @@ def get_str( str | None The extracted string value or None if the key is not found. """ - value = _get_type(mapping, key, str) - - if value is None: - return default - - return value - - -def get_str_or_str_list( - mapping: dict[str, Any], key: str, default: str | list[str] | None = None -) -> str | list[str] | None: - """Get a string or list of strings from a mapping. - - Parameters - ---------- - mapping: dict[str, Any] - The mapping to extract the value from. - key: str - The key of the value. - default: str | list[str] | None - The default value to return if the key is not found. - - Returns - ------- - str | list[str] | None - The extracted string or list of strings value or None if the key is not found. - """ - value = mapping.get(key, default) - - if value is None: - return None - - if isinstance(value, str): - return value - - if isinstance(value, list): - if all(isinstance(item, str) for item in value): - return value - msg = ( - f"Expected str or list of str for key '{key}', " - "got list with non-string items" - ) - raise ConfigError(msg) - - msg = f"Expected str or list of str for key '{key}', got {type(value).__name__}" - raise ConfigError(msg) + return _get_type(mapping, key, default, str) def get_str_enum( @@ -211,10 +165,7 @@ def get_str_enum( ConfigError If the value associated with the key is not a valid enum value. """ - value = _get_type(mapping, key, str) - - if value is None: - value = default + value = _get_type(mapping, key, default, str) if value is None: return None @@ -254,10 +205,7 @@ def get_str_list( If the value associated with the key is not a list of strings. """ - value = _get_type(mapping, key, list) - - if value is None: - value = default + value = mapping.get(key, default) if value is None: return [] diff --git a/src/devops/config/config_file.py b/src/devops/config/config_file.py index a292dd7..0b80bd5 100644 --- a/src/devops/config/config_file.py +++ b/src/devops/config/config_file.py @@ -1,9 +1,9 @@ """Module to parse file configuration values.""" -from dataclasses import dataclass, field +from dataclasses import dataclass from pathlib import Path -from .base import ConfigError, get_str, get_str_or_str_list, get_table +from .base import ConfigError, get_str, get_table @dataclass(frozen=True) @@ -11,28 +11,6 @@ class FileConfig: """Dataclass to hold file configuration values.""" encoding: str = "utf-8" - changelog_paths: list[Path] = field(default_factory=lambda: [Path("CHANGELOG.md")]) - _default_changelog_path: Path | None = None - - @property - def default_changelog_path(self) -> Path: - """Get the default changelog path. - - Returns - ------- - Path - The default changelog path. - """ - if self._default_changelog_path is not None: - return self._default_changelog_path - - if not self.changelog_paths: - msg = ( - "No changelog paths configured; " - "cannot determine default changelog path." - ) - raise ConfigError(msg) - return self.changelog_paths[0] def to_toml_lines(self) -> list[str]: """Convert the FileConfig to TOML lines. @@ -45,9 +23,6 @@ def to_toml_lines(self) -> list[str]: """ lines = ["[file]\n"] lines.append(f'#encoding = "{self.encoding}"\n') - paths_str = '", "'.join(str(p) for p in self.changelog_paths) - lines.append(f'#changelog_paths = ["{paths_str}"]\n') - lines.append(f'#default_changelog_path = "{self.default_changelog_path}"\n') return lines @@ -71,93 +46,12 @@ def parse_file_config(raw_config: dict) -> FileConfig: """ table = get_table(raw_config, "file") - encoding = parse_encoding(table) - changelog_paths = parse_changelog_path(table) - default_changelog_path = parse_default_changelog_path(table) - - return FileConfig( - encoding=encoding, - changelog_paths=changelog_paths, - _default_changelog_path=default_changelog_path, - ) - - -def parse_encoding(table: dict) -> str: - """Parse the encoding from a configuration table. - - Parameters - ---------- - table: dict - The configuration table. - - Returns - ------- - str - The parsed encoding. - - Raises - ------ - ConfigError - If the specified encoding is invalid. - """ encoding = get_str(table, "encoding", default=FileConfig.encoding) - # Validate encoding try: Path(__file__).open("r", encoding=encoding).close() except LookupError as e: msg = f"Invalid file encoding specified in configuration: {encoding}" raise ConfigError(msg) from e - return encoding - - -def parse_changelog_path(table: dict) -> list[Path]: - """Parse the changelog paths from a configuration table. - - Parameters - ---------- - table: dict - The configuration table. - - Returns - ------- - list[Path] - The parsed list of changelog paths. - """ - # Use the same default as FileConfig, but expressed as a list of strings. - default_paths = [str(p) for p in FileConfig().changelog_paths] - changelog_paths = get_str_or_str_list( - table, - "changelog_paths", - default=default_paths, - ) - - # If the configuration explicitly provides no value, fall back to the default. - if changelog_paths is None: - changelog_paths = default_paths - if isinstance(changelog_paths, str): - changelog_paths = [changelog_paths] - - return [Path(p) for p in changelog_paths] - - -def parse_default_changelog_path(table: dict) -> Path | None: - """Parse the default changelog path from a configuration table. - - Parameters - ---------- - table: dict - The configuration table. - - Returns - ------- - Path | None - The parsed default changelog path, or None if not specified. - """ - default_path_str = get_str(table, "default_changelog_path", default=None) - - if default_path_str is not None: - return Path(default_path_str) - - return None + return FileConfig(encoding=encoding) diff --git a/src/devops/files/update_changelog.py b/src/devops/files/update_changelog.py index ac0553c..3938943 100644 --- a/src/devops/files/update_changelog.py +++ b/src/devops/files/update_changelog.py @@ -20,7 +20,7 @@ def __init__(self, message: str) -> None: self.message = message -def update_changelog(version: str, changelog_path: Path) -> None: +def update_changelog(version: str, changelog_path: Path = __CHANGELOG_PATH__) -> None: """Update the changelog file with a new version entry. Parameters diff --git a/src/devops/scripts/update_changelog.py b/src/devops/scripts/update_changelog.py index e721c35..93fe779 100644 --- a/src/devops/scripts/update_changelog.py +++ b/src/devops/scripts/update_changelog.py @@ -1,72 +1,33 @@ """Script for updating the changelog file.""" import sys -from pathlib import Path import typer -from devops.config import config +from devops.files import update_changelog from devops.files.update_changelog import DevOpsChangelogError -from devops.files.update_changelog import update_changelog as update_changelog_func from devops.utils import mstd_print -update_changelog = typer.Typer() -update_changelogs = typer.Typer() +app = typer.Typer() -# Alias for tests and backwards compatibility -app = update_changelog - -@update_changelog.command() -def _update_changelog(version: str, changelog_path: str | None = None) -> None: +@app.command() +def main(version: str) -> None: """Update the changelog file with a new version entry. Parameters ---------- version: str The new version to add to the changelog. - changelog_path: str | None - The path to the changelog file. If None, it defaults - to the default_changelog_path from the configuration file. + """ - if changelog_path is None: - changelog_path = config.file.default_changelog_path - else: - changelog_path = Path(changelog_path) try: - update_changelog_func(version, changelog_path) + update_changelog.update_changelog(version) mstd_print(f"✅ CHANGELOG.md updated for version {version}") except DevOpsChangelogError as e: mstd_print(f"❌ Error updating changelog: {e}") sys.exit(1) -@update_changelogs.command() -def _update_changelogs(version: str, changelog_paths: list[str] | None = None) -> None: - """Update multiple changelog files with a new version entry. - - Parameters - ---------- - version: str - The new version to add to the changelogs. - changelog_paths: list[str] | None - The list of paths to the changelog files. If None, it defaults - to the changelog_paths from the configuration file. - """ - if changelog_paths is None: - changelog_paths = config.file.changelog_paths - else: - changelog_paths = [Path(p) for p in changelog_paths] - - failed_updates = [] - - for changelog_path in changelog_paths: - try: - update_changelog_func(version, changelog_path) - mstd_print(f"✅ {changelog_path} updated for version {version}") - except DevOpsChangelogError as e: - mstd_print(f"❌ Error updating {changelog_path}: {e}") - failed_updates.append(changelog_path) - - if failed_updates: - sys.exit(1) +if __name__ == "__main__": + app() diff --git a/tests/config/test_base.py b/tests/config/test_base.py deleted file mode 100644 index 1218923..0000000 --- a/tests/config/test_base.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Tests for devops.config.base module.""" - -import pytest - -from devops.config.base import ConfigError, get_str_or_str_list - - -class TestGetStrOrStrList: - """Test cases for get_str_or_str_list function.""" - - def test_string_value(self) -> None: - """Test that a string value is returned correctly.""" - mapping = {"key": "value"} - result = get_str_or_str_list(mapping, "key") - assert result == "value" - assert isinstance(result, str) - - def test_list_of_strings(self) -> None: - """Test that a list of strings is returned correctly.""" - mapping = {"key": ["value1", "value2", "value3"]} - result = get_str_or_str_list(mapping, "key") - assert result == ["value1", "value2", "value3"] - assert isinstance(result, list) - assert all(isinstance(item, str) for item in result) - - def test_empty_list(self) -> None: - """Test that an empty list is handled correctly.""" - mapping = {"key": []} - result = get_str_or_str_list(mapping, "key") - assert result == [] - assert isinstance(result, list) - - def test_mixed_list_with_non_strings(self) -> None: - """Test that a list with non-string items raises ConfigError.""" - mapping = {"key": ["string", 42, "another"]} - - with pytest.raises(ConfigError) as exc_info: - get_str_or_str_list(mapping, "key") - - assert "Expected str or list of str for key 'key'" in str(exc_info.value) - assert "got list with non-string items" in str(exc_info.value) - - def test_mixed_list_with_none(self) -> None: - """Test that a list containing None raises ConfigError.""" - mapping = {"key": ["string", None, "another"]} - - with pytest.raises(ConfigError) as exc_info: - get_str_or_str_list(mapping, "key") - - assert "Expected str or list of str for key 'key'" in str(exc_info.value) - assert "got list with non-string items" in str(exc_info.value) - - def test_non_string_non_list_int(self) -> None: - """Test that an integer value raises ConfigError.""" - mapping = {"key": 42} - - with pytest.raises(ConfigError) as exc_info: - get_str_or_str_list(mapping, "key") - - assert "Expected str or list of str for key 'key'" in str(exc_info.value) - assert "got int" in str(exc_info.value) - - def test_non_string_non_list_dict(self) -> None: - """Test that a dict value raises ConfigError.""" - mapping = {"key": {"nested": "value"}} - - with pytest.raises(ConfigError) as exc_info: - get_str_or_str_list(mapping, "key") - - assert "Expected str or list of str for key 'key'" in str(exc_info.value) - assert "got dict" in str(exc_info.value) - - def test_non_string_non_list_bool(self) -> None: - """Test that a boolean value raises ConfigError.""" - mapping = {"key": True} - - with pytest.raises(ConfigError) as exc_info: - get_str_or_str_list(mapping, "key") - - assert "Expected str or list of str for key 'key'" in str(exc_info.value) - assert "got bool" in str(exc_info.value) - - def test_missing_key_no_default(self) -> None: - """Test that a missing key without default returns None.""" - mapping = {"other_key": "value"} - result = get_str_or_str_list(mapping, "missing_key") - assert result is None - - def test_missing_key_with_string_default(self) -> None: - """Test that a missing key returns the string default value.""" - mapping = {"other_key": "value"} - result = get_str_or_str_list(mapping, "missing_key", default="default_value") - assert result == "default_value" - assert isinstance(result, str) - - def test_missing_key_with_list_default(self) -> None: - """Test that a missing key returns the list default value.""" - mapping = {"other_key": "value"} - result = get_str_or_str_list( - mapping, "missing_key", default=["default1", "default2"] - ) - assert result == ["default1", "default2"] - assert isinstance(result, list) - - def test_missing_key_with_none_default(self) -> None: - """Test that a missing key with None default returns None.""" - mapping = {"other_key": "value"} - result = get_str_or_str_list(mapping, "missing_key", default=None) - assert result is None - - def test_explicit_none_value(self) -> None: - """Test that an explicit None value in mapping returns None.""" - mapping = {"key": None} - result = get_str_or_str_list(mapping, "key") - assert result is None - - def test_explicit_none_value_with_default(self) -> None: - """Test that an explicit None value returns None even with default.""" - mapping = {"key": None} - result = get_str_or_str_list(mapping, "key", default="default_value") - assert result is None - - def test_empty_string(self) -> None: - """Test that an empty string is handled correctly.""" - mapping = {"key": ""} - result = get_str_or_str_list(mapping, "key") - assert result == "" - assert isinstance(result, str) - - def test_single_item_list(self) -> None: - """Test that a single-item list is returned correctly.""" - mapping = {"key": ["single"]} - result = get_str_or_str_list(mapping, "key") - assert result == ["single"] - assert isinstance(result, list) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index a915789..5e41bac 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -92,7 +92,7 @@ def test_parse_config_buggy_cpp_macros_not_list() -> None: with pytest.raises(ConfigError) as exc_info: parse_config(raw_config) - assert "Expected list for key" in str(exc_info.value) + assert "Expected list of strings for key" in str(exc_info.value) assert "buggy_cpp_macros" in str(exc_info.value) @@ -122,7 +122,7 @@ def test_parse_config_buggy_cpp_macros_is_dict() -> None: with pytest.raises(ConfigError) as exc_info: parse_config(raw_config) - assert "Expected list for key" in str(exc_info.value) + assert "Expected list of strings for key" in str(exc_info.value) assert "buggy_cpp_macros" in str(exc_info.value) diff --git a/tests/config/test_config_file.py b/tests/config/test_config_file.py deleted file mode 100644 index aaae92b..0000000 --- a/tests/config/test_config_file.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Tests for devops.config.config_file module.""" - -from pathlib import Path - -import pytest - -from devops.config.base import ConfigError -from devops.config.config_file import ( - FileConfig, - parse_default_changelog_path, - parse_file_config, -) - - -class TestParseDefaultChangelogPath: - """Tests for parse_default_changelog_path function.""" - - def test_parse_default_changelog_path_with_valid_path(self) -> None: - """Test parsing a valid path string.""" - table = {"default_changelog_path": "docs/CHANGELOG.md"} - result = parse_default_changelog_path(table) - - assert result == Path("docs/CHANGELOG.md") - assert isinstance(result, Path) - - def test_parse_default_changelog_path_with_none(self) -> None: - """Test parsing when key is missing returns None.""" - table: dict[str, str] = {} - result = parse_default_changelog_path(table) - - assert result is None - - def test_parse_default_changelog_path_with_explicit_none(self) -> None: - """Test parsing when value is explicitly None.""" - table = {"default_changelog_path": None} - result = parse_default_changelog_path(table) - - assert result is None - - def test_parse_default_changelog_path_with_relative_path(self) -> None: - """Test parsing with a relative path.""" - table = {"default_changelog_path": "../CHANGES.md"} - result = parse_default_changelog_path(table) - - assert result == Path("../CHANGES.md") - - def test_parse_default_changelog_path_with_absolute_path(self) -> None: - """Test parsing with an absolute path.""" - table = {"default_changelog_path": "/usr/local/CHANGELOG.md"} - result = parse_default_changelog_path(table) - - assert result == Path("/usr/local/CHANGELOG.md") - - -class TestFileConfigDefaultChangelogPathInteraction: - """Tests for how default_changelog_path interacts with FileConfig.""" - - def test_file_config_uses_explicit_default_changelog_path(self) -> None: - """Test that FileConfig uses explicit default_changelog_path when provided.""" - raw_config = { - "file": { - "changelog_paths": ["CHANGELOG.md", "docs/CHANGELOG.md"], - "default_changelog_path": "docs/CHANGELOG.md", - } - } - result = parse_file_config(raw_config) - - assert result.default_changelog_path == Path("docs/CHANGELOG.md") - assert result.changelog_paths == [ - Path("CHANGELOG.md"), - Path("docs/CHANGELOG.md"), - ] - - def test_file_config_defaults_to_first_changelog_path(self) -> None: - """Test FileConfig defaults to first changelog_path when no default present.""" - raw_config = { - "file": { - "changelog_paths": ["CHANGELOG.md", "docs/CHANGELOG.md"], - } - } - result = parse_file_config(raw_config) - - assert result.default_changelog_path == Path("CHANGELOG.md") - - def test_file_config_default_changelog_path_with_single_path(self) -> None: - """Test default_changelog_path with single changelog path.""" - raw_config = { - "file": { - "changelog_paths": "CHANGELOG.md", - } - } - result = parse_file_config(raw_config) - - assert result.default_changelog_path == Path("CHANGELOG.md") - - def test_file_config_default_changelog_path_not_in_list(self) -> None: - """Test that default_changelog_path can be different from changelog_paths.""" - raw_config = { - "file": { - "changelog_paths": ["CHANGELOG.md"], - "default_changelog_path": "docs/CHANGES.md", - } - } - result = parse_file_config(raw_config) - - # The default_changelog_path doesn't need to be in changelog_paths - assert result.default_changelog_path == Path("docs/CHANGES.md") - assert result.changelog_paths == [Path("CHANGELOG.md")] - - def test_file_config_raises_error_when_no_paths_configured(self) -> None: - """Test accessing default_changelog_path. - - When no changelog paths are configured, accessing default_changelog_path - """ - # Create a FileConfig with empty changelog_paths and no default - config = FileConfig(changelog_paths=[], _default_changelog_path=None) - - with pytest.raises(ConfigError) as exc_info: - _ = config.default_changelog_path - - assert "No changelog paths configured" in str(exc_info.value) - assert "cannot determine default changelog path" in str(exc_info.value) - - def test_file_config_with_no_file_section(self) -> None: - """Test parsing config with no file section uses defaults.""" - raw_config: dict[str, dict[str, str]] = {} - result = parse_file_config(raw_config) - - # Should use defaults - assert result.default_changelog_path == Path("CHANGELOG.md") - assert result.changelog_paths == [Path("CHANGELOG.md")] diff --git a/tests/scripts/test_update_changelog_cli.py b/tests/scripts/test_update_changelog_cli.py deleted file mode 100644 index 2292948..0000000 --- a/tests/scripts/test_update_changelog_cli.py +++ /dev/null @@ -1,407 +0,0 @@ -"""Tests for update_changelog CLI script.""" - -from __future__ import annotations - -import typing -from pathlib import Path -from unittest.mock import patch - -from typer.testing import CliRunner - -from devops.files.update_changelog import DevOpsChangelogError -from devops.scripts.update_changelog import update_changelog, update_changelogs - -if typing.TYPE_CHECKING: - from pathlib import Path as PathType - -runner = CliRunner() - - -class TestUpdateChangelogCLI: - """Tests for update_changelog Typer CLI command.""" - - def test_update_changelog_command_exists(self) -> None: - """Test that update_changelog command is registered.""" - result = runner.invoke(update_changelog, ["--help"]) - assert result.exit_code == 0 - assert "Update the changelog file" in result.stdout - - def test_update_changelog_with_changelog_path_argument( - self, tmp_path: PathType - ) -> None: - """Test update_changelog command with changelog_path argument. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - # Create a test changelog file - changelog_file = tmp_path / "TEST_CHANGELOG.md" - changelog_file.write_text( - "# Changelog\n\n## Next Release\n\n- Test change\n\n" - "\n" - ) - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - result = runner.invoke( - update_changelog, ["1.0.0", "--changelog-path", str(changelog_file)] - ) - - assert result.exit_code == 0 - # Should call update_changelog with the provided path - assert mock_update.call_count == 1 - call_args = mock_update.call_args[0] - assert call_args[0] == "1.0.0" - # Should convert string to Path - assert isinstance(call_args[1], Path) - assert call_args[1] == Path(changelog_file) - - def test_update_changelog_uses_config_default_when_no_arg(self) -> None: - """Test update_changelog uses config default when no changelog_path provided.""" - with ( - patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update, - patch("devops.scripts.update_changelog.config") as mock_config, - ): - mock_update.return_value = None - mock_config.file.default_changelog_path = Path("/config/path/CHANGELOG.md") - - _result = runner.invoke(update_changelog, ["1.0.0"]) - - # Should use the global config's default_changelog_path - assert mock_update.call_count == 1 - call_args = mock_update.call_args[0] - assert call_args[0] == "1.0.0" - # The config returns a Path object - assert call_args[1] == Path("/config/path/CHANGELOG.md") - - def test_update_changelog_converts_string_to_path(self, tmp_path: PathType) -> None: - """Test that string input is properly handled for Path conversion. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog_file = tmp_path / "CHANGELOG.md" - changelog_file.write_text( - "# Changelog\n\n## Next Release\n\n- Feature\n\n\n" - ) - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - # Pass as string - result = runner.invoke( - update_changelog, ["2.0.0", "--changelog-path", str(changelog_file)] - ) - - assert result.exit_code == 0 - assert mock_update.call_count == 1 - # Verify it's called with a Path object - call_args = mock_update.call_args[0] - assert isinstance(call_args[1], Path) - assert call_args[1] == Path(changelog_file) - - def test_update_changelog_handles_error(self) -> None: - """Test update_changelog command handles DevOpsChangelogError.""" - with ( - patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update, - patch("devops.scripts.update_changelog.config") as mock_config, - ): - mock_update.side_effect = DevOpsChangelogError("Test error") - mock_config.file.default_changelog_path = Path("/default/CHANGELOG.md") - - result = runner.invoke(update_changelog, ["1.0.0"]) - - assert result.exit_code == 1 - assert "Error updating changelog" in result.stdout - - def test_update_changelog_success_message(self, tmp_path: PathType) -> None: - """Test that successful update shows correct message. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog_file = tmp_path / "CHANGELOG.md" - changelog_file.write_text( - "# Changelog\n\n## Next Release\n\n- Change\n\n\n" - ) - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - result = runner.invoke( - update_changelog, ["3.0.0", "--changelog-path", str(changelog_file)] - ) - - assert result.exit_code == 0 - assert "✅ CHANGELOG.md updated for version 3.0.0" in result.stdout - - -class TestUpdateChangelogsCLI: - """Tests for update_changelogs Typer CLI command.""" - - def test_update_changelogs_command_exists(self) -> None: - """Test that update_changelogs command is registered.""" - result = runner.invoke(update_changelogs, ["--help"]) - assert result.exit_code == 0 - assert "Update multiple changelog files" in result.stdout - - def test_update_changelogs_with_multiple_explicit_paths( - self, tmp_path: PathType - ) -> None: - """Test update_changelogs with multiple explicit changelog paths. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - # Create test changelog files - changelog1 = tmp_path / "CHANGELOG1.md" - changelog2 = tmp_path / "CHANGELOG2.md" - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - result = runner.invoke( - update_changelogs, - [ - "1.0.0", - "--changelog-paths", - str(changelog1), - "--changelog-paths", - str(changelog2), - ], - ) - - assert result.exit_code == 0 - # Should call update_changelog_func twice, once for each path - assert mock_update.call_count == 2 - - # Check both calls - first_call = mock_update.call_args_list[0] - assert first_call[0][0] == "1.0.0" - assert isinstance(first_call[0][1], Path) - assert first_call[0][1] == Path(changelog1) - - second_call = mock_update.call_args_list[1] - assert second_call[0][0] == "1.0.0" - assert isinstance(second_call[0][1], Path) - assert second_call[0][1] == Path(changelog2) - - def test_update_changelogs_uses_config_defaults(self) -> None: - """Test update_changelogs uses config defaults when no paths provided.""" - with ( - patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update, - patch("devops.scripts.update_changelog.config") as mock_config, - ): - mock_update.return_value = None - mock_config.file.changelog_paths = [ - Path("/config/CHANGELOG1.md"), - Path("/config/CHANGELOG2.md"), - Path("/config/CHANGELOG3.md"), - ] - - result = runner.invoke(update_changelogs, ["2.0.0"]) - - assert result.exit_code == 0 - # Should call update_changelog_func three times - assert mock_update.call_count == 3 - - # Verify all three config paths were used - call_paths = [call[0][1] for call in mock_update.call_args_list] - assert Path("/config/CHANGELOG1.md") in call_paths - assert Path("/config/CHANGELOG2.md") in call_paths - assert Path("/config/CHANGELOG3.md") in call_paths - - def test_update_changelogs_handles_partial_failures( - self, tmp_path: PathType - ) -> None: - """Test update_changelogs handles partial failures correctly. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog1 = tmp_path / "CHANGELOG1.md" - changelog2 = tmp_path / "CHANGELOG2.md" - changelog3 = tmp_path / "CHANGELOG3.md" - - def mock_update_side_effect(_version: str, path: Path) -> None: - # Fail for the second changelog - if path == Path(changelog2): - msg = "Mock error for CHANGELOG2" - raise DevOpsChangelogError(msg) - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.side_effect = mock_update_side_effect - - result = runner.invoke( - update_changelogs, - [ - "1.0.0", - "--changelog-paths", - str(changelog1), - "--changelog-paths", - str(changelog2), - "--changelog-paths", - str(changelog3), - ], - ) - - # Should exit with error code 1 due to failures - assert result.exit_code == 1 - - # Should have tried all three changelogs - assert mock_update.call_count == 3 - - # Check output contains success and error messages - # Use 'in' check that accounts for potential line wrapping - assert "✅" in result.stdout - assert str(changelog1) in result.stdout - assert "1.0.0" in result.stdout - assert "❌ Error updating" in result.stdout - assert str(changelog2) in result.stdout - assert "Mock error for CHANGELOG2" in result.stdout - assert str(changelog3) in result.stdout - - def test_update_changelogs_handles_all_failures(self, tmp_path: PathType) -> None: - """Test update_changelogs when all updates fail. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog1 = tmp_path / "CHANGELOG1.md" - changelog2 = tmp_path / "CHANGELOG2.md" - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.side_effect = DevOpsChangelogError("Mock error") - - result = runner.invoke( - update_changelogs, - [ - "1.0.0", - "--changelog-paths", - str(changelog1), - "--changelog-paths", - str(changelog2), - ], - ) - - # Should exit with error code 1 - assert result.exit_code == 1 - - # Should have tried both changelogs - assert mock_update.call_count == 2 - - # Both should show error messages - assert "❌ Error updating" in result.stdout - assert str(changelog1) in result.stdout - assert str(changelog2) in result.stdout - assert "Mock error" in result.stdout - - def test_update_changelogs_converts_strings_to_paths( - self, tmp_path: PathType - ) -> None: - """Test that string paths are properly converted to Path objects. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog1 = tmp_path / "CHANGELOG1.md" - changelog2 = tmp_path / "CHANGELOG2.md" - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - result = runner.invoke( - update_changelogs, - [ - "1.0.0", - "--changelog-paths", - str(changelog1), - "--changelog-paths", - str(changelog2), - ], - ) - - assert result.exit_code == 0 - assert mock_update.call_count == 2 - - # Verify both calls received Path objects - for call in mock_update.call_args_list: - assert isinstance(call[0][1], Path) - - def test_update_changelogs_success_messages(self, tmp_path: PathType) -> None: - """Test that successful updates show correct messages for each file. - - Parameters - ---------- - tmp_path: PathType - Temporary path for creating test files. - - """ - changelog1 = tmp_path / "PROJECT_CHANGELOG.md" - changelog2 = tmp_path / "API_CHANGELOG.md" - - with patch( - "devops.scripts.update_changelog.update_changelog_func" - ) as mock_update: - mock_update.return_value = None - - result = runner.invoke( - update_changelogs, - [ - "3.5.0", - "--changelog-paths", - str(changelog1), - "--changelog-paths", - str(changelog2), - ], - ) - - assert result.exit_code == 0 - - # Should show success message for each changelog with specific filename - assert "✅" in result.stdout - assert "PROJECT_CHANGELOG" in result.stdout - assert "API_CHANGELOG" in result.stdout - assert "3.5.0" in result.stdout diff --git a/uv.lock b/uv.lock index fb2bae2..e189042 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,23 @@ version = 1 revision = 3 requires-python = ">=3.12" +[[package]] +name = "checks" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pytest" }, + { name = "ruff" }, + { name = "typer" }, +] + +[package.metadata] +requires-dist = [ + { name = "pytest", specifier = ">=9.0.1" }, + { name = "ruff", specifier = ">=0.14.6" }, + { name = "typer", specifier = ">=0.20.0" }, +] + [[package]] name = "click" version = "8.3.1" @@ -23,119 +40,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "coverage" -version = "7.13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" }, - { url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" }, - { url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" }, - { url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" }, - { url = "https://files.pythonhosted.org/packages/d0/bf/9939c5d6859c380e405b19e736321f1c7d402728792f4c752ad1adcce005/coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7", size = 221487, upload-time = "2025-12-28T15:41:11.468Z" }, - { url = "https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6", size = 222299, upload-time = "2025-12-28T15:41:13.386Z" }, - { url = "https://files.pythonhosted.org/packages/10/79/176a11203412c350b3e9578620013af35bcdb79b651eb976f4a4b32044fa/coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c", size = 220941, upload-time = "2025-12-28T15:41:14.975Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" }, - { url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" }, - { url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" }, - { url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" }, - { url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" }, - { url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f7/91fa73c4b80305c86598a2d4e54ba22df6bf7d0d97500944af7ef155d9f7/coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461", size = 221512, upload-time = "2025-12-28T15:41:35.519Z" }, - { url = "https://files.pythonhosted.org/packages/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500", size = 222321, upload-time = "2025-12-28T15:41:37.371Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b8/bdcb7253b7e85157282450262008f1366aa04663f3e3e4c30436f596c3e2/coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9", size = 220949, upload-time = "2025-12-28T15:41:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" }, - { url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" }, - { url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" }, - { url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/88/4f/3c159b7953db37a7b44c0eab8a95c37d1aa4257c47b4602c04022d5cb975/coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842", size = 222184, upload-time = "2025-12-28T15:41:59.763Z" }, - { url = "https://files.pythonhosted.org/packages/58/a5/6b57d28f81417f9335774f20679d9d13b9a8fb90cd6160957aa3b54a2379/coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2", size = 223250, upload-time = "2025-12-28T15:42:01.52Z" }, - { url = "https://files.pythonhosted.org/packages/81/7c/160796f3b035acfbb58be80e02e484548595aa67e16a6345e7910ace0a38/coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09", size = 221521, upload-time = "2025-12-28T15:42:03.275Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" }, - { url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" }, - { url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" }, - { url = "https://files.pythonhosted.org/packages/f0/bc/fd4c1da651d037a1e3d53e8cb3f8182f4b53271ffa9a95a2e211bacc0349/coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5", size = 221777, upload-time = "2025-12-28T15:42:23.919Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/71acabdc8948464c17e90b5ffd92358579bd0910732c2a1c9537d7536aa6/coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a", size = 222592, upload-time = "2025-12-28T15:42:25.619Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c8/a6fb943081bb0cc926499c7907731a6dc9efc2cbdc76d738c0ab752f1a32/coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0", size = 221169, upload-time = "2025-12-28T15:42:27.629Z" }, - { url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" }, - { url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" }, - { url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" }, - { url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" }, - { url = "https://files.pythonhosted.org/packages/06/c5/8c0515692fb4c73ac379d8dc09b18eaf0214ecb76ea6e62467ba7a1556ff/coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f", size = 222562, upload-time = "2025-12-28T15:42:49.144Z" }, - { url = "https://files.pythonhosted.org/packages/05/0e/c0a0c4678cb30dac735811db529b321d7e1c9120b79bd728d4f4d6b010e9/coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79", size = 223670, upload-time = "2025-12-28T15:42:51.218Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/b177aa0011f354abf03a8f30a85032686d290fdeed4222b27d36b4372a50/coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4", size = 221707, upload-time = "2025-12-28T15:42:53.034Z" }, - { url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" }, -] - -[[package]] -name = "devops" -source = { editable = "." } -dependencies = [ - { name = "typer" }, -] - -[package.optional-dependencies] -test = [ - { name = "coverage" }, - { name = "docstr-coverage" }, - { name = "pytest" }, - { name = "pytest-cov" }, -] - -[package.metadata] -requires-dist = [ - { name = "coverage", marker = "extra == 'test'" }, - { name = "docstr-coverage", marker = "extra == 'test'" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=9.0.1" }, - { name = "pytest-cov", marker = "extra == 'test'" }, - { name = "typer", specifier = ">=0.20.0" }, -] -provides-extras = ["test"] - -[[package]] -name = "docstr-coverage" -version = "2.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "pyyaml" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/86/d3f02e5baf426eac0f039849272204649137449f050d3fe0eb104c6f399c/docstr-coverage-2.3.2.tar.gz", hash = "sha256:e99a28c502ed21ae8a310cb9e14e8de2d7cff44d365b46fa6dca6de05bf156a0", size = 26750, upload-time = "2024-05-07T16:54:33.514Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/f7/7923d915a80aa2e04939260f7a000e1a353b25c7ffcf7771cd777559c27a/docstr_coverage-2.3.2-py3-none-any.whl", hash = "sha256:37a885d6560ad87e289b23bf0e54527885bacbf6b17cf55278d514dd0fef8ff5", size = 25816, upload-time = "2024-05-07T16:54:31.636Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -209,66 +113,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, ] -[[package]] -name = "pytest-cov" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - [[package]] name = "rich" version = "14.2.0" @@ -283,24 +127,38 @@ wheels = [ ] [[package]] -name = "shellingham" -version = "1.5.4" +name = "ruff" +version = "0.14.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, + { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, + { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, + { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, + { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, + { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, + { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, + { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, + { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, + { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, + { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, ] [[package]] -name = "tqdm" -version = "4.67.1" +name = "shellingham" +version = "1.5.4" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]]