From 94c02b3faf89cacf7186ad8a84e9200ede851574 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 02:28:08 +0800 Subject: [PATCH 001/275] refactor(default): capitalize all constants and remove unnecessary variable --- commitizen/bump.py | 8 ++++---- commitizen/commands/changelog.py | 2 +- commitizen/commands/init.py | 4 ++-- commitizen/config/__init__.py | 2 +- .../conventional_commits.py | 8 ++++---- commitizen/cz/customize/customize.py | 8 ++++---- commitizen/defaults.py | 19 +++++++++---------- tests/test_conf.py | 4 ++-- 8 files changed, 27 insertions(+), 28 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index adfab64cb0..76a8e15893 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -8,7 +8,7 @@ from string import Template from typing import cast -from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message, encoding +from commitizen.defaults import BUMP_MESSAGE, ENCODING, MAJOR, MINOR, PATCH from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open from commitizen.version_schemes import Increment, Version @@ -64,7 +64,7 @@ def update_version_in_files( files: list[str], *, check_consistency: bool = False, - encoding: str = encoding, + encoding: str = ENCODING, ) -> list[str]: """Change old version to the new one in every file given. @@ -121,7 +121,7 @@ def _bump_with_regex( current_version: str, new_version: str, regex: str, - encoding: str = encoding, + encoding: str = ENCODING, ) -> tuple[bool, str]: current_version_found = False lines = [] @@ -148,6 +148,6 @@ def create_commit_message( message_template: str | None = None, ) -> str: if message_template is None: - message_template = bump_message + message_template = BUMP_MESSAGE t = Template(message_template) return t.safe_substitute(current_version=current_version, new_version=new_version) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index fb6f76a6b7..0e4efabfa1 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -78,7 +78,7 @@ def __init__(self, config: BaseConfig, args): self.change_type_order = ( self.config.settings.get("change_type_order") or self.cz.change_type_order - or defaults.change_type_order + or defaults.CHANGE_TYPE_ORDER ) self.rev_range = args.get("rev_range") self.tag_format: str = ( diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 20277399d8..0eb3d99d17 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -11,7 +11,7 @@ from commitizen.__version__ import __version__ from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz import registry -from commitizen.defaults import DEFAULT_SETTINGS, config_files +from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme @@ -165,7 +165,7 @@ def _ask_config_path(self) -> str: name: str = questionary.select( "Please choose a supported config file: ", - choices=config_files, + choices=CONFIG_FILES, default=default_path, style=self.cz.style, ).unsafe_ask() diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index f3720bb1b3..9dfd591c40 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -28,7 +28,7 @@ def read_cfg(filepath: str | None = None) -> BaseConfig: cfg_paths = ( path / Path(filename) for path in cfg_search_paths - for filename in defaults.config_files + for filename in defaults.CONFIG_FILES ) for filename in cfg_paths: diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index c7b88258cb..af29a209fc 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -28,9 +28,9 @@ def parse_subject(text): class ConventionalCommitsCz(BaseCommitizen): - bump_pattern = defaults.bump_pattern - bump_map = defaults.bump_map - bump_map_major_version_zero = defaults.bump_map_major_version_zero + bump_pattern = defaults.BUMP_PATTERN + bump_map = defaults.BUMP_MAP + bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO commit_parser = r"^((?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?|\w+!):\s(?P.*)?" # noqa change_type_map = { "feat": "Feat", @@ -38,7 +38,7 @@ class ConventionalCommitsCz(BaseCommitizen): "refactor": "Refactor", "perf": "Perf", } - changelog_pattern = defaults.bump_pattern + changelog_pattern = defaults.BUMP_PATTERN def questions(self) -> Questions: questions: Questions = [ diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index d53ae29f1b..afa35e92de 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -21,10 +21,10 @@ class CustomizeCommitsCz(BaseCommitizen): - bump_pattern = defaults.bump_pattern - bump_map = defaults.bump_map - bump_map_major_version_zero = defaults.bump_map_major_version_zero - change_type_order = defaults.change_type_order + bump_pattern = defaults.BUMP_PATTERN + bump_map = defaults.BUMP_MAP + bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO + change_type_order = defaults.CHANGE_TYPE_ORDER def __init__(self, config: BaseConfig): super().__init__(config) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 0b78e1b0bb..0b6c28e6a9 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -60,8 +60,7 @@ class Settings(TypedDict, total=False): extras: dict[str, Any] -name: str = "cz_conventional_commits" -config_files: list[str] = [ +CONFIG_FILES: list[str] = [ "pyproject.toml", ".cz.toml", ".cz.json", @@ -70,10 +69,10 @@ class Settings(TypedDict, total=False): "cz.yaml", "cz.toml", ] -encoding: str = "utf-8" +ENCODING = "utf-8" DEFAULT_SETTINGS: Settings = { - "name": name, + "name": "cz_conventional_commits", "version": None, "version_files": [], "version_provider": "commitizen", @@ -102,7 +101,7 @@ class Settings(TypedDict, total=False): "pre_bump_hooks": [], "post_bump_hooks": [], "prerelease_offset": 0, - "encoding": encoding, + "encoding": ENCODING, "always_signoff": False, "template": None, # default provided by plugin "extras": {}, @@ -114,8 +113,8 @@ class Settings(TypedDict, total=False): CHANGELOG_FORMAT = "markdown" -bump_pattern = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):" -bump_map = OrderedDict( +BUMP_PATTERN = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):" +BUMP_MAP = OrderedDict( ( (r"^.+!$", MAJOR), (r"^BREAKING[\-\ ]CHANGE", MAJOR), @@ -125,7 +124,7 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) -bump_map_major_version_zero = OrderedDict( +BUMP_MAP_MAJOR_VERSION_ZERO = OrderedDict( ( (r"^.+!$", MINOR), (r"^BREAKING[\-\ ]CHANGE", MINOR), @@ -135,8 +134,8 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) -change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] -bump_message = "bump: version $current_version → $new_version" +CHANGE_TYPE_ORDER = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] +BUMP_MESSAGE = "bump: version $current_version → $new_version" def get_tag_regexes( diff --git a/tests/test_conf.py b/tests/test_conf.py index 80d58983e7..f89a0049f4 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -151,7 +151,7 @@ def test_find_git_project_root(tmpdir): @pytest.mark.parametrize( - "config_files_manager", defaults.config_files.copy(), indirect=True + "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_set_key(config_files_manager): _conf = config.read_cfg() @@ -162,7 +162,7 @@ def test_set_key(config_files_manager): class TestReadCfg: @pytest.mark.parametrize( - "config_files_manager", defaults.config_files.copy(), indirect=True + "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_load_conf(_, config_files_manager): cfg = config.read_cfg() From 8d573069ff591f909c9b9725da769fbab05d5bb0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 May 2025 15:17:25 +0000 Subject: [PATCH 002/275] =?UTF-8?q?bump:=20version=204.7.1=20=E2=86=92=204?= =?UTF-8?q?.7.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df32532598..7497946571 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.7.1 # automatically updated by Commitizen + rev: v4.7.2 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 486ee8dcb2..de772c6a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.7.2 (2025-05-18) + +### Refactor + +- **default**: capitalize all constants and remove unnecessary variable + ## v4.7.1 (2025-05-16) ### Fix diff --git a/commitizen/__version__.py b/commitizen/__version__.py index cc72154c1b..98c926d162 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.7.1" +__version__ = "4.7.2" diff --git a/pyproject.toml b/pyproject.toml index 734d5127be..8d9214615a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.7.1" +version = "4.7.2" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.7.1" +version = "4.7.2" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 767c5fede43c8f8858778b3e7f372c532495c48e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 19 May 2025 20:17:08 +0800 Subject: [PATCH 003/275] docs(ISSUE_TEMPLATE): correct labels --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/documentation.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9ed5badae4..9d597c7b36 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: 🛠 Bug report description: Create a report to help us improve title: Good bug title tells us about precise symptom, not about the root cause. -labels: [bug] +labels: ['type: bug'] body: - type: textarea id: description diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml index 51d378b747..8fe40136c3 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -1,7 +1,7 @@ name: 📖 Documentation description: Suggest an improvement for the documentation of this project title: Content to be added or fixed -labels: [documentation] +labels: ['type: documentation'] body: - type: checkboxes id: type diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 7d67eb18af..4bd19b9b3a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: 🚀 Feature request description: Suggest an idea for this project title: "" -labels: [feature] +labels: ['type: feature'] body: - type: textarea id: description From 2982c6e5e31c9fd171e364c2a2866dc109303c20 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 19 May 2025 20:32:54 +0800 Subject: [PATCH 004/275] docs(label_issues): add logics for adding os related labels --- .github/workflows/label_issues.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml index 45ca450f89..ec18d98be2 100644 --- a/.github/workflows/label_issues.yml +++ b/.github/workflows/label_issues.yml @@ -15,9 +15,31 @@ jobs: - uses: actions/github-script@v7 with: script: | - github.rest.issues.addLabels({ + const issue = await github.rest.issues.get({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - labels: ['issue-status: needs-triage'] - }) + }); + + const body = issue.data.body || ''; + + const osLabels = new Set(); // Use a Set to avoid duplicates + + if (body.includes('Operating System: Darwin')) { + osLabels.add('os: macOS'); + } + + if (body.includes('Operating System: Linux')) { + osLabels.add('os: Linux'); + } + + if (body.includes('Operating System: Windows')) { + osLabels.add('os: Windows'); + } + + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['issue-status: needs-triage', ...osLabels], + }); From 49f4d2b1a68de7e9b78cd39497483787de892db2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 19 May 2025 21:13:19 +0800 Subject: [PATCH 005/275] docs(label_issues): add comment --- .github/workflows/label_issues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml index ec18d98be2..e74a36c4bd 100644 --- a/.github/workflows/label_issues.yml +++ b/.github/workflows/label_issues.yml @@ -25,6 +25,7 @@ jobs: const osLabels = new Set(); // Use a Set to avoid duplicates + // Parse the "Environment" section, output of `cz version --report` if (body.includes('Operating System: Darwin')) { osLabels.add('os: macOS'); } From e3b4465261cfe9c541cdd1174adae53bc7e309cb Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 19 May 2025 22:05:33 +0800 Subject: [PATCH 006/275] docs(bug_report): add fallback command if cz version --report is not available --- .github/ISSUE_TEMPLATE/bug_report.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9d597c7b36..5eac443f29 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -53,6 +53,14 @@ body: ```bash cz version --report ``` + + If `cz version --report` is not available, please run the following commands to retrieve your environment information: + + ```bash + echo "Commitizen Version: $(cz version)" + echo "Python Version: $(python3 -c 'import sys; print(sys.version)')" + echo "Operating System: $(python3 -c 'import platform; print(platform.system())')" + ``` placeholder: | Commitizen Version: 4.0.0 Python Version: 3.13.3 (main, Apr 8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)] From 87873f99d89b3da65388d1a93827a1536e29348e Mon Sep 17 00:00:00 2001 From: Jani Jappinen <2580412+jappja@users.noreply.github.com> Date: Thu, 15 May 2025 09:19:20 +0000 Subject: [PATCH 007/275] feat(cli): add --tag-format argument to changelog command Changelog command already checks "tag_format" argument but cli support was missing. --- commitizen/cli.py | 4 ++++ ...angelog_command_shows_description_when_use_help_option.txt | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 72d824380d..d08afc6706 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -450,6 +450,10 @@ def __call__( "help": "Export the changelog template into this file instead of rendering it", }, *deepcopy(tpl_arguments), + { + "name": "--tag-format", + "help": "The format of the tag, wrap around simple quotes", + }, ], }, { diff --git a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt index 461eb2edd6..91b7f389b5 100644 --- a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt @@ -3,7 +3,7 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME] [--start-rev START_REV] [--merge-prerelease] [--version-scheme {pep440,semver,semver2}] [--export-template EXPORT_TEMPLATE] [--template TEMPLATE] - [--extra EXTRA] + [--extra EXTRA] [--tag-format TAG_FORMAT] [rev_range] generate changelog (note that it will overwrite existing file) @@ -37,3 +37,5 @@ options: changelog template file name (relative to the current working directory) --extra, -e EXTRA a changelog extra variable (in the form 'key=value') + --tag-format TAG_FORMAT + The format of the tag, wrap around simple quotes From dbdfa60e1f95eccd9eb5d22e20f854043b2a8d37 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 20 May 2025 15:03:32 +0000 Subject: [PATCH 008/275] docs(cli/screenshots): update CLI screenshots [skip ci] --- docs/images/cli_help/cz_changelog___help.svg | 194 ++++++++++--------- 1 file changed, 101 insertions(+), 93 deletions(-) diff --git a/docs/images/cli_help/cz_changelog___help.svg b/docs/images/cli_help/cz_changelog___help.svg index 1160ccf6cf..69304f40cf 100644 --- a/docs/images/cli_help/cz_changelog___help.svg +++ b/docs/images/cli_help/cz_changelog___help.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - - $ cz changelog --help -usage: cz changelog [-h][--dry-run][--file-name FILE_NAME] -[--unreleased-version UNRELEASED_VERSION][--incremental] -[--start-rev START_REV][--merge-prerelease] -[--version-scheme {pep440,semver,semver2}] -[--export-template EXPORT_TEMPLATE][--template TEMPLATE] -[--extra EXTRA] - - -generate changelog (note that it will overwrite existing file) - -positional arguments: -  rev_range             generates changelog for the given version (e.g: 1.5.3) -                        or version range (e.g: 1.5.3..1.7.9) - -options: -  -h, --help            show this help message and exit -  --dry-run             show changelog to stdout -  --file-name FILE_NAME -                        file name of changelog (default: 'CHANGELOG.md') -  --unreleased-version UNRELEASED_VERSION -                        set the value for the new version (use the tag value), -                        instead of using unreleased -  --incremental         generates changelog from last created version, useful -                        if the changelog has been manually modified -  --start-rev START_REV -                        start rev of the changelog. If not set, it will -                        generate changelog from the start -  --merge-prerelease    collect all changes from prereleases into next non- -                        prerelease. If not set, it will include prereleases in -                        the changelog -  --version-scheme {pep440,semver,semver2} -                        choose version scheme -  --export-template EXPORT_TEMPLATE -                        Export the changelog template into this file instead -                        of rendering it -  --template, -t TEMPLATE -                        changelog template file name (relative to the current -                        working directory) -  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') - + + $ cz changelog --help +usage: cz changelog [-h][--dry-run][--file-name FILE_NAME] +[--unreleased-version UNRELEASED_VERSION][--incremental] +[--start-rev START_REV][--merge-prerelease] +[--version-scheme {pep440,semver,semver2}] +[--export-template EXPORT_TEMPLATE][--template TEMPLATE] +[--extra EXTRA][--tag-format TAG_FORMAT] + + +generate changelog (note that it will overwrite existing file) + +positional arguments: +  rev_range             generates changelog for the given version (e.g: 1.5.3) +                        or version range (e.g: 1.5.3..1.7.9) + +options: +  -h, --help            show this help message and exit +  --dry-run             show changelog to stdout +  --file-name FILE_NAME +                        file name of changelog (default: 'CHANGELOG.md') +  --unreleased-version UNRELEASED_VERSION +                        set the value for the new version (use the tag value), +                        instead of using unreleased +  --incremental         generates changelog from last created version, useful +                        if the changelog has been manually modified +  --start-rev START_REV +                        start rev of the changelog. If not set, it will +                        generate changelog from the start +  --merge-prerelease    collect all changes from prereleases into next non- +                        prerelease. If not set, it will include prereleases in +                        the changelog +  --version-scheme {pep440,semver,semver2} +                        choose version scheme +  --export-template EXPORT_TEMPLATE +                        Export the changelog template into this file instead +                        of rendering it +  --template, -t TEMPLATE +                        changelog template file name (relative to the current +                        working directory) +  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') +  --tag-format TAG_FORMAT +                        The format of the tag, wrap around simple quotes + From 225fd76ecbd80fd81471886047d3b60b9710bbf9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 20 May 2025 15:03:39 +0000 Subject: [PATCH 009/275] =?UTF-8?q?bump:=20version=204.7.2=20=E2=86=92=204?= =?UTF-8?q?.8.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7497946571..ff222cac1a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.7.2 # automatically updated by Commitizen + rev: v4.8.0 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index de772c6a75..d11930cce2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.8.0 (2025-05-20) + +### Feat + +- **cli**: add --tag-format argument to changelog command + ## v4.7.2 (2025-05-18) ### Refactor diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 98c926d162..ea674c5c54 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.7.2" +__version__ = "4.8.0" diff --git a/pyproject.toml b/pyproject.toml index 8d9214615a..4e41a885fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.7.2" +version = "4.8.0" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.7.2" +version = "4.8.0" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 10581b3b4399abb2972d932d22d9b4b55b433a3b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 16 May 2025 02:18:37 +0800 Subject: [PATCH 010/275] test(changelog): code cleanup and better type --- tests/test_changelog.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 067adb4a91..b1c7c802e1 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -556,8 +556,8 @@ @pytest.fixture -def gitcommits() -> list: - commits = [ +def gitcommits() -> list[git.GitCommit]: + return [ git.GitCommit( commit["rev"], commit["title"], @@ -568,13 +568,11 @@ def gitcommits() -> list: ) for commit in COMMITS_DATA ] - return commits @pytest.fixture -def tags() -> list: - tags = [git.GitTag(*tag) for tag in TAGS] - return tags +def tags() -> list[git.GitTag]: + return [git.GitTag(*tag) for tag in TAGS] @pytest.fixture From d0c26742ef05f3976bf3e42486857612b17e83d4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 14 May 2025 18:05:55 +0800 Subject: [PATCH 011/275] refactor(customize): improve code readability --- commitizen/cz/customize/customize.py | 54 ++++++++-------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index afa35e92de..53ada4b2c0 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -33,35 +33,17 @@ def __init__(self, config: BaseConfig): raise MissingCzCustomizeConfigError() self.custom_settings = self.config.settings["customize"] - custom_bump_pattern = self.custom_settings.get("bump_pattern") - if custom_bump_pattern: - self.bump_pattern = custom_bump_pattern - - custom_bump_map = self.custom_settings.get("bump_map") - if custom_bump_map: - self.bump_map = custom_bump_map - - custom_bump_map_major_version_zero = self.custom_settings.get( - "bump_map_major_version_zero" - ) - if custom_bump_map_major_version_zero: - self.bump_map_major_version_zero = custom_bump_map_major_version_zero - - custom_change_type_order = self.custom_settings.get("change_type_order") - if custom_change_type_order: - self.change_type_order = custom_change_type_order - - commit_parser = self.custom_settings.get("commit_parser") - if commit_parser: - self.commit_parser = commit_parser - - changelog_pattern = self.custom_settings.get("changelog_pattern") - if changelog_pattern: - self.changelog_pattern = changelog_pattern - - change_type_map = self.custom_settings.get("change_type_map") - if change_type_map: - self.change_type_map = change_type_map + for attr_name in [ + "bump_pattern", + "bump_map", + "bump_map_major_version_zero", + "change_type_order", + "commit_parser", + "changelog_pattern", + "change_type_map", + ]: + if value := self.custom_settings.get(attr_name): + setattr(self, attr_name, value) def questions(self) -> Questions: return self.custom_settings.get("questions", [{}]) @@ -70,8 +52,7 @@ def message(self, answers: dict) -> str: message_template = Template(self.custom_settings.get("message_template", "")) if getattr(Template, "substitute", None): return message_template.substitute(**answers) # type: ignore - else: - return message_template.render(**answers) + return message_template.render(**answers) def example(self) -> str: return self.custom_settings.get("example") or "" @@ -83,12 +64,7 @@ def schema(self) -> str: return self.custom_settings.get("schema") or "" def info(self) -> str: - info_path = self.custom_settings.get("info_path") - info = self.custom_settings.get("info") - if info_path: + if info_path := self.custom_settings.get("info_path"): with open(info_path, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content - elif info: - return info - return "" + return f.read() + return self.custom_settings.get("info") or "" From ba571827e4bba1b5ac5fbdaf38302e53cd8efe75 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 May 2025 03:12:05 +0000 Subject: [PATCH 012/275] =?UTF-8?q?bump:=20version=204.8.0=20=E2=86=92=204?= =?UTF-8?q?.8.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff222cac1a..f1aef91193 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.8.0 # automatically updated by Commitizen + rev: v4.8.1 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index d11930cce2..9904ae3bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.8.1 (2025-05-22) + +### Refactor + +- **customize**: improve code readability + ## v4.8.0 (2025-05-20) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index ea674c5c54..1969a66ecd 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.8.0" +__version__ = "4.8.1" diff --git a/pyproject.toml b/pyproject.toml index 4e41a885fa..853d9ccaf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.8.0" +version = "4.8.1" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.8.0" +version = "4.8.1" tag_format = "v$version" version_files = [ "pyproject.toml:version", From fe72043eeec8ffdd34d721ada65c45c622acdac2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 19:59:18 +0800 Subject: [PATCH 013/275] refactor(check): remove unnecessary variable --- commitizen/commands/check.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 087db03ea5..a0d06cbc47 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -72,15 +72,11 @@ def __call__(self): raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'") pattern = self.cz.schema_pattern() - ill_formatted_commits = [ - commit - for commit in commits - if not self.validate_commit_message(commit.message, pattern) - ] displayed_msgs_content = "\n".join( [ f'commit "{commit.rev}": "{commit.message}"' - for commit in ill_formatted_commits + for commit in commits + if not self.validate_commit_message(commit.message, pattern) ] ) if displayed_msgs_content: From 7fd6ce33d59f90f0a85826b54224aa063ddfdc70 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 20:05:39 +0800 Subject: [PATCH 014/275] refactor(check): simplify code --- commitizen/commands/check.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index a0d06cbc47..1e3b8464e1 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -88,24 +88,21 @@ def __call__(self): ) out.success("Commit validation: successful!") + def _get_commit_message(self) -> str | None: + if self.commit_msg_file is None: + # Get commit message from command line (--message) + return self.commit_msg + + with open(self.commit_msg_file, encoding=self.encoding) as commit_file: + # Get commit message from file (--commit-msg-file) + return commit_file.read() + def _get_commits(self): - msg = None - # Get commit message from file (--commit-msg-file) - if self.commit_msg_file is not None: - # Enter this branch if commit_msg_file is "". - with open(self.commit_msg_file, encoding=self.encoding) as commit_file: - msg = commit_file.read() - # Get commit message from command line (--message) - elif self.commit_msg is not None: - msg = self.commit_msg - if msg is not None: - msg = self._filter_comments(msg) - return [git.GitCommit(rev="", title="", body=msg)] + if (msg := self._get_commit_message()) is not None: + return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))] # Get commit messages from git log (--rev-range) - if self.rev_range: - return git.get_commits(end=self.rev_range) - return git.get_commits() + return git.get_commits(end=self.rev_range or "HEAD") @staticmethod def _filter_comments(msg: str) -> str: From a8094aebad266040ef07f118a96c88a93f4aecf8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 May 2025 03:16:18 +0000 Subject: [PATCH 015/275] =?UTF-8?q?bump:=20version=204.8.1=20=E2=86=92=204?= =?UTF-8?q?.8.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 7 +++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f1aef91193..4e108eb9b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.8.1 # automatically updated by Commitizen + rev: v4.8.2 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 9904ae3bbf..5b737a851a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.8.2 (2025-05-22) + +### Refactor + +- **check**: simplify code +- **check**: remove unnecessary variable + ## v4.8.1 (2025-05-22) ### Refactor diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 1969a66ecd..8e288a597c 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.8.1" +__version__ = "4.8.2" diff --git a/pyproject.toml b/pyproject.toml index 853d9ccaf8..b306e5e78c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.8.1" +version = "4.8.2" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.8.1" +version = "4.8.2" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 9420b44c86a9271d0d9f107360501a058533f9e2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 23 May 2025 16:55:07 +0800 Subject: [PATCH 016/275] docs(label_pr.yml): add labels based on new PR title --- .github/workflows/label_pr.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index b409c8b757..176d7cc794 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -17,3 +17,31 @@ jobs: - uses: actions/labeler@v5 with: configuration-path: .github/labeler.yml + - name: Label based on PR title + uses: actions/github-script@v7 + with: + script: | + const title = context.payload.pull_request.title.toLowerCase(); + const labels = []; + + if (title.includes("fix") || title.includes("bug")) { + labels.push("type: bug"); + } + if (title.includes("feat")) { + labels.push("type: feature"); + } + if (title.includes("doc")) { + labels.push("type: documentation"); + } + if (title.includes("refactor")) { + labels.push("type: refactor"); + } + + if (labels.length > 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels + }); + } From 5aa58c6638f7e2237f596ec373e9001e21ba1a8c Mon Sep 17 00:00:00 2001 From: Adrian Carolli Date: Mon, 26 May 2025 19:45:10 -0400 Subject: [PATCH 017/275] docs: add installation and usage guide for cz-ai plugin leveraging GPT-4o --- docs/third-party-commitizen.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md index 1c8ef8a183..b6d598985f 100644 --- a/docs/third-party-commitizen.md +++ b/docs/third-party-commitizen.md @@ -3,6 +3,22 @@ In addition to the native templates, some alternative commit format templates are available as PyPI packages (installable with `pip`). +### [cz-ai](https://github.com/watadarkstar/cz_ai) + +A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes. + +#### Installation + +```sh +pip install cz-ai +``` + +#### Usage + +```sh +cz --name cz_ai commit +``` + ### [Conventional JIRA](https://pypi.org/project/conventional-JIRA/) Just like _conventional commit_ format, but the scope has been restricted to a From e69e0a1babe6b9dd64f7c3cabb2246bd299379ab Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 17:04:41 +0800 Subject: [PATCH 018/275] docs: fix typos and grammar mistakes --- docs/commands/changelog.md | 10 +++++----- docs/commands/check.md | 22 ++++++++++----------- docs/commands/commit.md | 2 +- docs/config.md | 6 +++--- docs/contributing.md | 32 +++++++++++++++---------------- docs/customization.md | 26 ++++++++++++------------- docs/faq.md | 8 ++++---- docs/third-party-commitizen.md | 2 +- docs/tutorials/auto_check.md | 18 ++++++++--------- docs/tutorials/dev_releases.md | 11 +++-------- docs/tutorials/github_actions.md | 4 ++-- docs/tutorials/gitlab_ci.md | 8 ++++---- docs/tutorials/tag_format.md | 14 +++++++------- docs/tutorials/writing_commits.md | 16 ++++++++-------- 14 files changed, 87 insertions(+), 92 deletions(-) diff --git a/docs/commands/changelog.md b/docs/commands/changelog.md index cbf22b15a7..aee1a0e075 100644 --- a/docs/commands/changelog.md +++ b/docs/commands/changelog.md @@ -56,7 +56,7 @@ These are the variables used by the changelog generator. It will create a full block like above per version found in the tags. And it will create a list of the commits found. The `change_type` and the `scope` are optional, they don't need to be provided, -but if your regex does they will be rendered. +but if your regex does, they will be rendered. The format followed by the changelog is the one from [keep a changelog][keepachangelog] and the following variables are expected: @@ -108,7 +108,7 @@ cz bump --changelog This value can be updated in the `toml` file with the key `changelog_file` under `tools.commitizen` -Specify the name of the output file, remember that changelog only works with markdown. +Specify the name of the output file, remember that changelog only works with Markdown. ```bash cz changelog --file-name="CHANGES.md" @@ -120,7 +120,7 @@ This flag can be set in the `toml` file with the key `changelog_incremental` und Benefits: -- Build from latest version found in changelog, this is useful if you have a different changelog and want to use commitizen +- Build from the latest version found in changelog, this is useful if you have a different changelog and want to use commitizen - Update unreleased area - Allows users to manually touch the changelog without being rewritten. @@ -185,8 +185,8 @@ See [the template customization section](../customization.md#customizing-the-cha Supported hook methods: -- per parsed message: useful to add links -- end of changelog generation: useful to send slack or chat message, or notify another department +- Per parsed message: Useful to add links +- End of changelog generation: Useful to send Slack or chat messages, or notify another department Read more about hooks in the [customization page][customization] diff --git a/docs/commands/check.md b/docs/commands/check.md index e45ecd86c8..33e41e04f8 100644 --- a/docs/commands/check.md +++ b/docs/commands/check.md @@ -2,9 +2,9 @@ ## About -This feature checks whether the commit message follows the given committing rules. And comment in git message will be ignored. +This feature checks whether the commit message follows the given committing rules. Comments in git messages will be ignored. -If you want to setup an automatic check before every git commit, please refer to +If you want to set up an automatic check before every git commit, please refer to [Automatically check message before commit](../tutorials/auto_check.md). ## Usage @@ -27,7 +27,7 @@ $ cz check --rev-range REV_RANGE For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range ..HEAD`. -For more info on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). +For more information on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). ### Commit Message @@ -47,7 +47,7 @@ In this option, MESSAGE is the commit message to be checked. $ echo MESSAGE | cz check ``` -In this option, MESSAGE is piped to cz check and would be checked. +In this option, MESSAGE is piped to cz check and will be checked. ### Commit Message File @@ -55,8 +55,8 @@ In this option, MESSAGE is piped to cz check and would be checked. $ cz check --commit-msg-file COMMIT_MSG_FILE ``` -In this option, COMMIT_MSG_FILE is the path of the temporal file that contains the commit message. -This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hook. +In this option, COMMIT_MSG_FILE is the path of the temporary file that contains the commit message. +This argument can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hooks. ### Allow Abort @@ -69,8 +69,8 @@ permit them. Since `git commit` accepts an `--allow-empty-message` flag (primari ### Allowed Prefixes -If the commit message starts by some specific prefixes, `cz check` returns `True` without checkign the regex. -By default, the the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`. +If the commit message starts with some specific prefixes, `cz check` returns `True` without checking the regex. +By default, the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`. ```bash cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' @@ -78,10 +78,10 @@ cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' ### Commit message length limit -The argument `-l` (or `--message-length-limmit`) followed by a positive number, can limit the length of commit messages. +The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages. For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESSAGE` is more than 3 characters long. By default, the limit is set to 0, which means no limit on the length. -**Note that the limit applies only to the first line of the message.*** +**Note that the limit applies only to the first line of the message.** Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, -while the body, and the footer are not counted. +while the body and the footer are not counted. diff --git a/docs/commands/commit.md b/docs/commands/commit.md index 7760a2b88e..ea033cc411 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -45,7 +45,7 @@ a new commit message to be prompted. The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages. An exception would be raised when the message length exceeds the limit. For example, `cz commit -l 72` will limit the length of commit messages to 72 characters. -By default the limit is set to 0, which means no limit on the length. +By default, the limit is set to 0, which means no limit on the length. **Note that the limit applies only to the first line of the message.** Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, diff --git a/docs/config.md b/docs/config.md index a522312743..b6dd794fb3 100644 --- a/docs/config.md +++ b/docs/config.md @@ -341,7 +341,7 @@ commitizen: ## Version providers Commitizen can read and write version from different sources. -By default, it use the `commitizen` one which is using the `version` field from the commitizen settings. +By default, it uses the `commitizen` one which is using the `version` field from the commitizen settings. But you can use any `commitizen.provider` entrypoint as value for `version_provider`. Commitizen provides some version providers for some well known formats: @@ -369,9 +369,9 @@ version_provider = "pep621" ### Custom version provider -You can add you own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. +You can add your own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. -Here a quick example of a `my-provider` provider reading and writing version in a `VERSION` file. +Here is a quick example of a `my-provider` provider reading and writing version in a `VERSION` file. ```python title="my_provider.py" from pathlib import Path diff --git a/docs/contributing.md b/docs/contributing.md index 0da1707da6..1192e57c0a 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -8,25 +8,25 @@ If you're a first-time contributor, you can check the issues with [good first is ## Install before contributing -1. Install [poetry](https://python-poetry.org/) `>=2.0.0`, installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer) -2. Install [gpg](https://gnupg.org), installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you could try [homebrew](https://brew.sh/). +1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer). +2. Install [gpg](https://gnupg.org). See installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/). ## Before making a pull request 1. Fork [the repository](https://github.com/commitizen-tools/commitizen). -2. Clone the repository from your GitHub. -3. Setup development environment through [poetry](https://python-poetry.org/) (`poetry install`). -4. Setup [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`) -5. Check out a new branch and add your modification. -6. Add test cases for all your changes. +1. Clone the repository from your GitHub. +1. Set up development environment through [poetry](https://python-poetry.org/) (`poetry install`). +1. Set up [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`). +1. Checkout a new branch and add your modifications. +1. Add test cases for all your changes. (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.) -7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/). -8. Run `poetry all` to ensure you follow the coding style and the tests pass. -9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`). -9. **Do not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`. -10. **Do not** update the versions in the project, they will be automatically updated. -10. If your changes are about documentation. Run `poetry doc` to serve documentation locally and check whether there is any warning or error. -11. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 +1. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/). +1. Run `poetry all` to ensure you follow the coding style and the tests pass. +1. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`). +1. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`. +1. **Do not** update the versions in the project; they will be automatically updated. +1. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors. +1. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 ## Use of GitHub Labels @@ -45,8 +45,8 @@ If you're a first-time contributor, you can check the issues with [good first is * pr-status: wait-for-modification * pr-status: wait-for-response * pr-status: ready-to-merge -* needs: test-case *(pr only)* -* needs: documentation *(pr only)* +* needs: test-case *(PR only)* +* needs: documentation *(PR only)* * type: feature * type: bug * type: documentation diff --git a/docs/customization.md b/docs/customization.md index 31749d1c83..cef03469e0 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -170,7 +170,7 @@ commitizen: | Parameter | Type | Default | Description | | ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input` and etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | +| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | | `name` | `str` | `None` | The key for the value answered by user. It's used in `message_template` | | `message` | `str` | `None` | Detail description for the question. | | `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | @@ -192,10 +192,10 @@ To specify keyboard shortcuts for your custom choices, provide the shortcut usin The basic steps are: -1. Inheriting from `BaseCommitizen` +1. Inheriting from `BaseCommitizen`. 2. Give a name to your rules. -3. Create a python package using `setup.py`, `poetry`, etc -4. Expose the class as a `commitizen.plugin` entrypoint +3. Create a python package using `setup.py`, `poetry`, etc. +4. Expose the class as a `commitizen.plugin` entrypoint. Check an [example][convcomms] on how to configure `BaseCommitizen`. @@ -322,9 +322,9 @@ You can customize it of course, and this are the variables you need to add to yo | `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] | | `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern | | `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided | -| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | +| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | | `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | -| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich a releases before they are rendered. Must return the update release +| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release ```python from commitizen.cz.base import BaseCommitizen @@ -395,7 +395,7 @@ class NoSubjectProvidedException(CzException): Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. Migration should be straight-forward for legacy plugins: -- Remove the `discover_this` line from you plugin module +- Remove the `discover_this` line from your plugin module - Expose the plugin class under as a `commitizen.plugin` entrypoint. The name of the plugin is now determined by the name of the entrypoint. @@ -455,12 +455,12 @@ By default, the template used is the `CHANGELOG.md.j2` file from the commitizen ### Providing a template with your customization class -There is 3 parameters available to change the template rendering from your custom `BaseCommitizen`. +There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`. | Parameter | Type | Default | Description | | ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | -| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | -| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from you customization class) | +| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | +| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from your customization class) | | `template_extras` | `dict` | `None` | Provide some extra template parameters | Let's see an example. @@ -484,14 +484,14 @@ This snippet will: ### Providing a template from the current working directory -Users can provides their own template from their current working directory (your project root) by: +Users can provide their own template from their current working directory (your project root) by: - providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class) - setting your template path as `template` configuration - giving your template path as `--template` parameter to `bump` and `changelog` commands !!! Note - The path is relative to the current working directory, aka. your project root most of the time. + The path is relative to the current working directory, aka your project root most of the time. ### Template variables @@ -524,7 +524,7 @@ When using another template (either provided by a plugin or by yourself), you ca by: - defining them in your configuration with the [`extras` settings][extras-config] -- providing them on the commandline with the `--extra/-e` parameter to `bump` and `changelog` commands +- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands [template-config]: config.md#template [extras-config]: config.md#extras diff --git a/docs/faq.md b/docs/faq.md index 29d9f40512..0302efd267 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -52,7 +52,7 @@ It is not affiliated. Both are used for similar purposes, parsing commits, generating changelog and version we presume. This one is written in python to make integration easier for python projects and the other serves the JS packages. -They differ a bit in design, not sure if cz-js does any of this, but these are some of the stuff you can do with this repo (python's commitizen): +They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with this repo (python's commitizen): - create custom rules, version bumps and changelog generation, by default we use the popular conventional commits (I think cz-js allows this). - single package, install one thing and it will work (cz-js is a monorepo, but you have to install different dependencies AFAIK) @@ -83,14 +83,14 @@ More discussion can be found in issue [#318](https://github.com/commitizen-tools ## Why does commitizen not support CalVer? `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer -creates numerous edge cases that are difficult to maintain ([#385]) and more generally +creates numerous edge cases that are difficult to maintain ([#385]) and more generally, mixing the two version schemes may not be a good idea. If CalVer or other custom versioning scheme is needed, `commitizen` could still be used to standardize commits and create changelogs, but a separate package should be used for version increments. Mixing CalVer and SemVer is generally not recommended because each versioning scheme -serves a different purposes. Diverging from either specification can be confusing to -users and cause errors with third party tools that don't expect the non-standard format. +serves a different purpose. Diverging from either specification can be confusing to +users and cause errors with third-party tools that don't expect the non-standard format. In the future, `commitizen` may support some implementation of CalVer, but at the time of writing, there are no plans to implement the feature ([#173]). diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md index b6d598985f..e9eb822ed3 100644 --- a/docs/third-party-commitizen.md +++ b/docs/third-party-commitizen.md @@ -117,7 +117,7 @@ See the [README][1] for instructions on configuration ## Third-Party Commitizen Providers -Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`). +Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`). ### [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/) diff --git a/docs/tutorials/auto_check.md b/docs/tutorials/auto_check.md index 2fce57f9bd..d143528767 100644 --- a/docs/tutorials/auto_check.md +++ b/docs/tutorials/auto_check.md @@ -6,9 +6,9 @@ To automatically check a commit message prior to committing, you can use a [git ## How to -There are two common methods for installing the hook: +There are two common methods for installing the hooks: -### Method 1: Add git hook through [pre-commit](https://pre-commit.com/) +### Method 1: Add a git hook through [pre-commit](https://pre-commit.com/) - Step 1: Install [pre-commit](https://pre-commit.com/) @@ -16,7 +16,7 @@ There are two common methods for installing the hook: python -m pip install pre-commit ``` -- Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content +- Step 2: Create `.pre-commit-config.yaml` in your root directory with the following content ```yaml --- @@ -28,19 +28,19 @@ repos: stages: [commit-msg] ``` -- Step 3: Install the configuration into git hook through `pre-commit` +- Step 3: Install the configuration into the git hook through `pre-commit` ```bash pre-commit install --hook-type commit-msg ``` -### Method 2: Manually add git hook +### Method 2: Manually add a git hook -The command might be included inside of a Git hook (inside of `.git/hooks/` at the root of the project). +The command might be included inside a Git hook (inside `.git/hooks/` at the root of the project). The selected hook might be the file called commit-msg. -This example shows how to use the check command inside of commit-msg. +This example shows how to use the check command inside commit-msg. At the root of the project: @@ -62,7 +62,7 @@ Where `$1` is the name of the temporary file that contains the current commit me The `--commit-msg-file` flag is required, not optional. -Each time you create a commit, automatically, this hook will analyze it. -If the commit message is invalid, it'll be rejected. +Each time you create a commit, this hook will automatically analyze it. +If the commit message is invalid, it will be rejected. The commit should follow the given committing rules; otherwise, it won't be accepted. diff --git a/docs/tutorials/dev_releases.md b/docs/tutorials/dev_releases.md index 8142334754..e2b29fb191 100644 --- a/docs/tutorials/dev_releases.md +++ b/docs/tutorials/dev_releases.md @@ -5,9 +5,7 @@ To make use of a `.dev` suffix, as per [PEP440](https://peps.python.org/pep-0440/#developmental-releases). -If more than one active branch attempts to create a tag, relative to the main -branch, there is the possibility that each will attempt to create the _same_ -tag, resulting in a collision. +If multiple active branches attempt to create a tag relative to the main branch, there is a possibility that they will attempt to create the _same_ tag, resulting in a collision. Developmental releases aim to avoid this by including a `.dev` segment which includes a non-negative integer unique to that workflow: @@ -19,9 +17,7 @@ X.Y.devN !!! note As noted in [PEP440](https://peps.python.org/pep-0440/#developmental-releases), - although developmental releases are useful in avoiding the situation - described above, depending on the value passed as the developmental - release, they can be _"difficult to parse for human readers"_. + while developmental releases help avoid the situation described above, they can be _"difficult to parse for human readers"_ depending on the value passed as the developmental release. ## How to @@ -64,8 +60,7 @@ Equally, as the developmental release needs only a non-negative integer, it is possible to use the Unix time (i.e. the number of seconds since 1st January 1970 UTC). -This would create the possibility of a collision if two builds occur at -precisely the same second but this may be sufficient for many cases: +This approach could potentially create a collision if two builds occur at precisely the same second, but it may be sufficient for many use cases: ```sh --devrelease $(date +%s) diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index 7a98abe2be..bcb3fda22c 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -51,7 +51,7 @@ where to output the content of the changelog for the newly created version. And then add a step using a github action to create the release: `softprops/action-gh-release` The commitizen action creates an env variable called `REVISION`, containing the -newely created version. +newly created version. ```yaml - name: Create bump and changelog @@ -119,7 +119,7 @@ jobs: ./scripts/publish ``` -Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and the publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) +Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package. Push the changes and that's it. diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md index 85abb3fe6d..85b6a615c3 100644 --- a/docs/tutorials/gitlab_ci.md +++ b/docs/tutorials/gitlab_ci.md @@ -38,7 +38,7 @@ The latest step is to create a `deploy key.` To do this, we should create it und If you have more projects under the same organization, you can reuse the deploy key created before, but you will have to repeat the step where we have created the environment variables (ssh key, email, and username). -tip: If the CI raise some errors, try to unprotected the private key. +Tip: If the CI raise some errors, try to unprotect the private key. ### Defining GitLab CI Pipeline @@ -105,9 +105,9 @@ auto-bump: - variables ``` -So, every time that a developer push to any branch, the `test` job is executed. If the branch is `master` and the test jobs success, the `auto-bump` takes place. -To be able to push using the Gitlab runner, we have to set the ssh key, configure git, and finally execute the auto bump. +So, every time that a developer pushes to any branch, the `test` job is executed. If the branch is `master` and the test jobs succeed, the `auto-bump` takes place. +To be able to push using the GitLab runner, we have to set the SSH key, configure git, and finally execute the auto bump. -After merging the new changed into master, we have the final result: +After merging the new changes into master, we have the final result: ![gitlab final ci result](../images/gitlab_ci/gitlab_final_ci_result.png) diff --git a/docs/tutorials/tag_format.md b/docs/tutorials/tag_format.md index 59c42bea13..8408b4c801 100644 --- a/docs/tutorials/tag_format.md +++ b/docs/tutorials/tag_format.md @@ -10,7 +10,7 @@ tag_format: $version version_scheme: pep440 ``` -As this is the default value so you don't have to specify it. +As this is the default value, you don't have to specify it. This setting means that: @@ -52,7 +52,7 @@ You will obviously want to keep all those features working as expected. Commitizen can deal with it as long as you provide the legacy tag format in the configuration. -Using the previous example, let say you want to move from `v${version}` to `component-${version}`. +Using the previous example, let's say you want to move from `v${version}` to `component-${version}`. Then `component-${version}` will be the new tag format and `v${version}` the legacy one. ```yaml @@ -62,14 +62,14 @@ legacy_tag_formats: - v${version} ``` -This way, you won't loose your version history, you'll still be able to generate you changelog properly -and on the next version bump, your last version in the form `v${version}` will be properly recognizef if you use the `scm` version provider. +This way, you won't lose your version history, you'll still be able to generate your changelog properly, +and on the next version bump, your last version in the form `v${version}` will be properly recognized if you use the `scm` version provider. Your new tag will be in the form `component-${version}`. ## Known tags to ignore -Now let's say you have some known tags you want to ignore, either because they are not versions, either because they are not versions of the component you are dealing with. -As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format: +Now let's say you have some known tags you want to ignore, either because they are not versions, or because they are not versions of the component you are dealing with. +As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format. Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config.md#ignored_tag_formats) setting: @@ -93,7 +93,7 @@ This will ignore: It will match any string from any length. This allows to exclude by prefix, whether it is followed by a version or not. !!! tip - If you don't want to be warned when Commitizen detect an unknown tag, you can by setting: + If you don't want to be warned when Commitizen detects an unknown tag, you can do so by setting: ``` [tool.commitizen] ignored_tag_formats = ["*"] diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md index 9ba151cc37..d1b2c6645d 100644 --- a/docs/tutorials/writing_commits.md +++ b/docs/tutorials/writing_commits.md @@ -1,21 +1,21 @@ For this project to work well in your pipeline, a commit convention must be followed. -By default commitizen uses the known [conventional commits][conventional_commits], but -you can create your own following the docs information over at +By default, commitizen uses the known [conventional commits][conventional_commits], but +you can create your own following the documentation information over at [customization][customization]. ## Conventional commits If you are using [conventional commits][conventional_commits], the most important thing to know is that you must begin your commits with at least one of these tags: -`fix`, `feat`. And if you introduce a breaking change, then, you must +`fix`, `feat`. And if you introduce a breaking change, then you must add to your commit body the following `BREAKING CHANGE`. -Using these 3 keywords will allow the proper identification of the semantic version. +Using these three keywords will allow the proper identification of the semantic version. Of course, there are other keywords, but I'll leave it to the reader to explore them. ## Writing commits -Now to the important part, when writing commits, it's important to think about: +Now to the important part: when writing commits, it's important to think about: - Your future self - Your colleagues @@ -23,16 +23,16 @@ Now to the important part, when writing commits, it's important to think about: You may think this is trivial, but it's not. It's important for the reader to understand what happened. -Emojis may be added as well (e.g. see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration]. +Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration]. ### Recommendations - **Keep the message short**: Makes the list of commits more readable (~50 chars). - **Talk imperative**: Follow this rule: `If applied, this commit will ` -- **Think about the CHANGELOG**: Your commits will probably end up in the changelog +- **Think about the CHANGELOG**: Your commits will probably end up in the changelog, so try writing for it, but also keep in mind that you can skip sending commits to the CHANGELOG by using different keywords (like `build`). -- **Use a commit per new feature**: if you introduce multiple things related to the same +- **Use a commit per new feature**: If you introduce multiple things related to the same commit, squash them. This is useful for auto-generating CHANGELOG. | Do's | Don'ts | From c3d4152fb736969b83be7b555ea44b025ffc6c7e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 17:19:32 +0800 Subject: [PATCH 019/275] docs: fix typo issues in cli.py, README.md, and contributing.md --- commitizen/cli.py | 2 +- docs/README.md | 18 +++++++++--------- docs/contributing.md | 30 +++++++++++++++--------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index d08afc6706..f0df2f4242 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -105,7 +105,7 @@ def __call__( "name": ["-nr", "--no-raise"], "type": str, "required": False, - "help": "comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/", + "help": "comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/", }, ], "subcommands": { diff --git a/docs/README.md b/docs/README.md index 128602dfb3..ee50ad4582 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,16 +18,16 @@ ## About -Commitizen is release management tool designed for teams. +Commitizen is a release management tool designed for teams. Commitizen assumes your team uses a standard way of committing rules and from that foundation, it can bump your project's version, create the changelog, and update files. By default, commitizen uses [conventional commits][conventional_commits], but you -can build your own set of rules, and publish them. +can build your own set of rules and publish them. -Using a standardized set of rules to write commits, makes commits easier to read, and enforces writing +Using a standardized set of rules to write commits makes commits easier to read and enforces writing descriptive commits. ### Features @@ -55,7 +55,7 @@ pipx install commitizen pipx upgrade commitizen ``` -Install commitizen using `pip` with `--user` flag: +Install commitizen using `pip` with the `--user` flag: ```bash pip install --user -U commitizen @@ -63,7 +63,7 @@ pip install --user -U commitizen ### Python project -You can add it to your local project using one of the following. +You can add it to your local project using one of the following methods. With `pip`: @@ -99,7 +99,7 @@ brew install commitizen ## Usage -Most of the time this is the only command you'll run: +Most of the time, this is the only command you'll run: ```sh cz bump @@ -120,7 +120,7 @@ $ cz --help usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... Commitizen is a cli tool to generate conventional commits. -For more information about the topic go to https://conventionalcommits.org/ +For more information about the topic, go to https://conventionalcommits.org/ optional arguments: -h, --help show this help message and exit @@ -128,7 +128,7 @@ optional arguments: --debug use debug mode -n NAME, --name NAME use the given commitizen (default: cz_conventional_commits) -nr NO_RAISE, --no-raise NO_RAISE - comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen- + comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ commands: @@ -147,7 +147,7 @@ commands: ## Setting up bash completion -When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this argcomplete needs to be enabled. +When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this, argcomplete needs to be enabled. argcomplete is installed when you install Commitizen since it's a dependency. diff --git a/docs/contributing.md b/docs/contributing.md index 1192e57c0a..0987771066 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,29 +4,29 @@ First of all, thank you for taking the time to contribute! 🎉 When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change. -If you're a first-time contributor, you can check the issues with [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. +If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. ## Install before contributing -1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer). -2. Install [gpg](https://gnupg.org). See installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/). +1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See the installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer). +2. Install [gpg](https://gnupg.org). See the installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/). ## Before making a pull request 1. Fork [the repository](https://github.com/commitizen-tools/commitizen). -1. Clone the repository from your GitHub. -1. Set up development environment through [poetry](https://python-poetry.org/) (`poetry install`). -1. Set up [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`). -1. Checkout a new branch and add your modifications. -1. Add test cases for all your changes. +2. Clone the repository from your GitHub. +3. Set up the development environment through [poetry](https://python-poetry.org/) (`poetry install`). +4. Set up the [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`). +5. Checkout a new branch and add your modifications. +6. Add test cases for all your changes. (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.) -1. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/). -1. Run `poetry all` to ensure you follow the coding style and the tests pass. -1. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`). -1. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`. -1. **Do not** update the versions in the project; they will be automatically updated. -1. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors. -1. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 +7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to make git commits. We follow [conventional commits](https://www.conventionalcommits.org/). +8. Run `poetry all` to ensure you follow the coding style and the tests pass. +9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (by running `poetry doc:screenshots`). +10. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`. +11. **Do not** update the versions in the project; they will be automatically updated. +12. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors. +13. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 ## Use of GitHub Labels From 23bd8936a448c15e36f775496221731fb40df0ad Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 22:53:40 +0800 Subject: [PATCH 020/275] docs(README.md): sync help section with the latest `cz --help` command output --- docs/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index ee50ad4582..bcc206642d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -115,21 +115,22 @@ Read more in the section [Getting Started](./getting_started.md). ### Help + + ```sh $ cz --help -usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... Commitizen is a cli tool to generate conventional commits. -For more information about the topic, go to https://conventionalcommits.org/ +For more information about the topic go to https://conventionalcommits.org/ -optional arguments: +options: -h, --help show this help message and exit - --config the path of configuration file + --config CONFIG the path of configuration file --debug use debug mode - -n NAME, --name NAME use the given commitizen (default: cz_conventional_commits) - -nr NO_RAISE, --no-raise NO_RAISE - comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen- - tools.github.io/commitizen/exit_codes/ + -n, --name NAME use the given commitizen (default: cz_conventional_commits) + -nr, --no-raise NO_RAISE + comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/ commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} From 3fcefbd591aa1c14c40730d9deae82f7918f8b2a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 23:08:34 +0800 Subject: [PATCH 021/275] docs(README.md): make it easier for readers to understand what Commitizen does --- docs/README.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index bcc206642d..616e7610cc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,17 +18,26 @@ ## About -Commitizen is a release management tool designed for teams. +Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. -Commitizen assumes your team uses a standard way of committing rules -and from that foundation, it can bump your project's version, create -the changelog, and update files. +### What Commitizen Does -By default, commitizen uses [conventional commits][conventional_commits], but you -can build your own set of rules and publish them. +By enforcing standardized commit conventions (defaulting to [Conventional Commits][conventional_commits]), Commitizen helps teams: -Using a standardized set of rules to write commits makes commits easier to read and enforces writing -descriptive commits. +- Write clear, structured commit messages +- Automatically manage version numbers using semantic versioning +- Generate and maintain changelogs +- Streamline the release process + +### Key Benefits + +With just a simple `cz bump` command, Commitizen handles: + +1. **Version Management**: Automatically bumps version numbers and updates version files based on your commit history +2. **Changelog Generation**: Creates and updates changelogs following the [Keep a changelog][keepchangelog] format +3. **Commit Standardization**: Enforces consistent commit message formats across your team + +This standardization makes your commit history more readable and meaningful, while the automation reduces manual work and potential errors in the release process. ### Features From 889198d5688e78793e4099e7493b030a652bb670 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 23:38:41 +0800 Subject: [PATCH 022/275] docs(README.md): move Getting Started to README and improve clarity of the section Closes #1405 --- docs/README.md | 137 ++++++++++++++++++++++++++++++++-------- docs/getting_started.md | 119 ---------------------------------- mkdocs.yml | 1 - 3 files changed, 112 insertions(+), 145 deletions(-) delete mode 100644 docs/getting_started.md diff --git a/docs/README.md b/docs/README.md index 616e7610cc..2a9e79bbfb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -48,81 +48,168 @@ This standardization makes your commit history more readable and meaningful, whi - Display information about your commit rules (commands: schema, example, info) - Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md) -## Requirements +## Getting Started -[Python](https://www.python.org/downloads/) `3.9+` +### Requirements -[Git][gitscm] `1.8.5.2+` +Before installing Commitizen, ensure you have: -## Installation +- [Python](https://www.python.org/downloads/) `3.9+` +- [Git][gitscm] `1.8.5.2+` -Install commitizen in your system using `pipx` (Recommended, ): +### Installation + +#### Global Installation (Recommended) + +The recommended way to install Commitizen is using `pipx`, which ensures a clean, isolated installation: ```bash +# Install pipx if you haven't already pipx ensurepath + +# Install Commitizen pipx install commitizen + +# Keep it updated pipx upgrade commitizen ``` -Install commitizen using `pip` with the `--user` flag: +If you're on macOS, you can also install Commitizen using Homebrew: ```bash -pip install --user -U commitizen +brew install commitizen ``` -### Python project +#### Project-Specific Installation -You can add it to your local project using one of the following methods. +You can add Commitizen to your Python project using any of these package managers: -With `pip`: +**Using pip:** ```bash pip install -U commitizen ``` -With `conda`: +**Using conda:** ```bash conda install -c conda-forge commitizen ``` -With Poetry >= 1.2.0: +**Using Poetry:** ```bash +# For Poetry >= 1.2.0 poetry add commitizen --group dev + +# For Poetry < 1.2.0 +poetry add commitizen --dev ``` -With Poetry < 1.2.0: +### Basic Commands -```bash -poetry add commitizen --dev +#### Initialize Commitizen + +To get started, you'll need to set up your configuration. You have two options: + +1. Use the interactive setup: +```sh +cz init ``` -### macOS +2. Manually create a configuration file (`.cz.toml` or `cz.toml`): +```toml +[tool.commitizen] +version = "0.1.0" +update_changelog_on_bump = true +``` -via [homebrew](https://formulae.brew.sh/formula/commitizen): +#### Create Commits -```bash -brew install commitizen +Create standardized commits using: +```sh +cz commit +# or use the shortcut +cz c ``` -## Usage +To sign off your commits: +```sh +cz commit -- --signoff +# or use the shortcut +cz commit -- -s +``` -Most of the time, this is the only command you'll run: +For more commit options, run `cz commit --help`. +#### Version Management + +The most common command you'll use is: ```sh cz bump ``` -On top of that, you can use commitizen to assist you with the creation of commits: +This command: +- Bumps your project's version +- Creates a git tag +- Updates the changelog (if `update_changelog_on_bump` is enabled) +- Updates version files + +You can customize: +- [Version files](./commands/bump.md#version_files) +- [Version scheme](./commands/bump.md#version_scheme) +- [Version provider](./config.md#version-providers) + +For all available options, see the [bump command documentation](./commands/bump.md). + +### Advanced Usage + +#### Get Project Version +To get your project's version (instead of Commitizen's version): ```sh -cz commit +cz version -p +``` + +This is particularly useful for automation. For example, to preview changelog changes for Slack: +```sh +cz changelog --dry-run "$(cz version -p)" +``` + +#### Pre-commit Integration + +Commitizen can automatically validate your commit messages using pre-commit hooks. + +1. Add to your `.pre-commit-config.yaml`: +```yaml +--- +repos: + - repo: https://github.com/commitizen-tools/commitizen + rev: master # Replace with latest tag + hooks: + - id: commitizen + - id: commitizen-branch + stages: [pre-push] +``` + +2. Install the hooks: +```sh +pre-commit install --hook-type commit-msg --hook-type pre-push ``` -Read more in the section [Getting Started](./getting_started.md). +| Hook | Recommended Stage | +| ----------------- | ----------------- | +| commitizen | commit-msg | +| commitizen-branch | pre-push | + +> **Note**: Replace `master` with the [latest tag](https://github.com/commitizen-tools/commitizen/tags) to avoid warnings. You can automatically update this with: +> ```sh +> pre-commit autoupdate +> ``` -### Help +For more details about commit validation, see the [check command documentation](commands/check.md). + +## Usage diff --git a/docs/getting_started.md b/docs/getting_started.md deleted file mode 100644 index 3c6257c363..0000000000 --- a/docs/getting_started.md +++ /dev/null @@ -1,119 +0,0 @@ -## Initialize commitizen - -If it's your first time, you'll need to create a commitizen configuration file. - -The assistant utility will help you set up everything - -```sh -cz init -``` - -Alternatively, create a file `.cz.toml` or `cz.toml` in your project's directory. - -```toml -[tool.commitizen] -version = "0.1.0" -update_changelog_on_bump = true -``` - -## Usage - -### Bump version - -```sh -cz bump -``` - -This command will bump your project's version, and it will create a tag. - -Because of the setting `update_changelog_on_bump`, bump will also create the **changelog**. -You can also [update files](./commands/bump.md#version_files). -You can configure the [version scheme](./commands/bump.md#version_scheme) and [version provider](./config.md#version-providers). - -There are many more options available, please read the docs for the [bump command](./commands/bump.md). - -### Committing - -Run in your terminal - -```bash -cz commit -``` - -or the shortcut - -```bash -cz c -``` - -#### Sign off the commit - -Run in the terminal - -```bash -cz commit -- --signoff -``` - -or the shortcut - -```bash -cz commit -- -s -``` - -### Get project version - -Running `cz version` will return the version of commitizen, but if you want -your project's version you can run: - -```sh -cz version -p -``` - -This can be useful in many situations, where otherwise, you would require a way -to parse the version of your project. Maybe it's simple if you use a `VERSION` file, -but once you start working with many different projects, it becomes tricky. - -A common example is, when you need to send to slack, the changes for the version that you -just created: - -```sh -cz changelog --dry-run "$(cz version -p)" -``` - -### Integration with Pre-commit - -Commitizen can lint your commit message for you with `cz check`. - -You can integrate this in your [pre-commit](https://pre-commit.com/) config with: - -```yaml ---- -repos: - - repo: https://github.com/commitizen-tools/commitizen - rev: master - hooks: - - id: commitizen - - id: commitizen-branch - stages: [pre-push] -``` - -After the configuration is added, you'll need to run: - -```sh -pre-commit install --hook-type commit-msg --hook-type pre-push -``` - -If you aren't using both hooks, you needn't install both stages. - -| Hook | Recommended Stage | -| ----------------- | ----------------- | -| commitizen | commit-msg | -| commitizen-branch | pre-push | - -Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with: - -```sh -pre-commit autoupdate -``` - -Read more about the `check` command [here](commands/check.md). diff --git a/mkdocs.yml b/mkdocs.yml index 6a642161d2..a8a2fe2d44 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,7 +31,6 @@ edit_uri: "" nav: - Introduction: "README.md" - - Getting Started: "getting_started.md" - Commands: - init: "commands/init.md" - commit: "commands/commit.md" From 36c7ae576fa4ffae3abcd16f95d5caf066fd7911 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 11 May 2025 23:55:40 +0800 Subject: [PATCH 023/275] docs(README.md): improve clarity of README reference section --- docs/README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/docs/README.md b/docs/README.md index 2a9e79bbfb..22d22f42c9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -209,7 +209,11 @@ pre-commit install --hook-type commit-msg --hook-type pre-push For more details about commit validation, see the [check command documentation](commands/check.md). -## Usage +## Help & Reference + +### Command Line Interface + +Commitizen provides a comprehensive CLI with various commands. Here's the complete reference: @@ -242,31 +246,85 @@ commands: version get the version of the installed commitizen or the current project (default: installed commitizen) ``` +### Quick Reference + +| Command | Description | Alias | +|---------|-------------|-------| +| `cz init` | Initialize Commitizen configuration | - | +| `cz commit` | Create a new commit | `cz c` | +| `cz bump` | Bump version and update changelog | - | +| `cz changelog` | Generate changelog | `cz ch` | +| `cz check` | Validate commit messages | - | +| `cz version` | Show version information | - | + +### Additional Resources + +- [Conventional Commits Specification][conventional_commits] +- [Exit Codes Reference](./exit_codes.md) +- [Configuration Guide](./config.md) +- [Command Documentation](./commands/init.md) + +### Getting Help + +For each command, you can get detailed help by adding `--help`: + +```sh +cz commit --help +cz bump --help +cz changelog --help +``` + +For more detailed documentation, visit our [documentation site](https://commitizen-tools.github.io/commitizen/). + ## Setting up bash completion -When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this, argcomplete needs to be enabled. +Commitizen supports command-line completion through [argcomplete](https://kislyuk.github.io/argcomplete/), which is automatically installed as a dependency. This feature provides intelligent auto-completion for all Commitizen commands and options. + +### Supported Shells + +- **Bash**: Full support +- **Zsh**: Limited support +- **Fish**: Limited support +- **Tcsh**: Limited support -argcomplete is installed when you install Commitizen since it's a dependency. +### Installation Methods + +#### Global Installation (Recommended) -If Commitizen is installed globally, global activation can be executed: +If you installed Commitizen globally (e.g., using `pipx` or `brew`), you can enable global completion: ```bash +# Enable global completion for all Python applications sudo activate-global-python-argcomplete ``` -For permanent (but not global) Commitizen activation, use: +#### User-Specific Installation + +For a user-specific installation that persists across sessions: ```bash +# Add to your shell's startup file (e.g., ~/.bashrc, ~/.zshrc) register-python-argcomplete cz >> ~/.bashrc ``` -For one-time activation of argcomplete for Commitizen only, use: +#### Temporary Installation + +For one-time activation in your current shell session: ```bash +# Activate completion for current session only eval "$(register-python-argcomplete cz)" ``` -For further information on activation, please visit the [argcomplete website](https://kislyuk.github.io/argcomplete/). +### Verification + +After installation, you can verify the completion is working by: + +1. Opening a new terminal session +2. Typing `cz` followed by a space and pressing `TAB` twice +3. You should see a list of available commands + +For more detailed information about argcomplete configuration and troubleshooting, visit the [argcomplete documentation](https://kislyuk.github.io/argcomplete/). ## Sponsors From 37a4a3ae4b0f9347f3cb4445931f027fee0ea9c2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 12 May 2025 00:58:47 +0800 Subject: [PATCH 024/275] docs(cli): align cli output with docs --- commitizen/cli.py | 5 ++--- docs/README.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index f0df2f4242..cb834c5d6f 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -86,9 +86,8 @@ def __call__( data = { "prog": "cz", "description": ( - "Commitizen is a cli tool to generate conventional commits.\n" - "For more information about the topic go to " - "https://conventionalcommits.org/" + "Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.\n" + "For more information, please visit https://commitizen-tools.github.io/commitizen" ), "formatter_class": argparse.RawDescriptionHelpFormatter, "arguments": [ diff --git a/docs/README.md b/docs/README.md index 22d22f42c9..144c253a81 100644 --- a/docs/README.md +++ b/docs/README.md @@ -221,8 +221,8 @@ Commitizen provides a comprehensive CLI with various commands. Here's the comple $ cz --help usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... -Commitizen is a cli tool to generate conventional commits. -For more information about the topic go to https://conventionalcommits.org/ +Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. +For more information, please visit https://commitizen-tools.github.io/commitizen options: -h, --help show this help message and exit From 4cd6d78a6bfc603497f9cb2c1b14c38616218b9f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 12 May 2025 01:56:29 +0800 Subject: [PATCH 025/275] docs(contributing.md): improve readability of contributing docs --- docs/contributing.md | 97 ++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 0987771066..4ca12765aa 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,32 +1,78 @@ ## Contributing to commitizen -First of all, thank you for taking the time to contribute! 🎉 +First, thank you for taking the time to contribute! 🎉 When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change. If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. -## Install before contributing - -1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See the installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer). -2. Install [gpg](https://gnupg.org). See the installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/). - -## Before making a pull request - -1. Fork [the repository](https://github.com/commitizen-tools/commitizen). -2. Clone the repository from your GitHub. -3. Set up the development environment through [poetry](https://python-poetry.org/) (`poetry install`). -4. Set up the [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`). -5. Checkout a new branch and add your modifications. -6. Add test cases for all your changes. - (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.) -7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to make git commits. We follow [conventional commits](https://www.conventionalcommits.org/). -8. Run `poetry all` to ensure you follow the coding style and the tests pass. -9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (by running `poetry doc:screenshots`). -10. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`. -11. **Do not** update the versions in the project; they will be automatically updated. -12. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors. -13. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 +## Prerequisites & Setup + +### Required Tools + +1. **Python Environment** + - Python `>=3.9` + - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0` +2. **Version Control & Security** + - Git + - Commitizen + - [GPG](https://gnupg.org) for commit signing + - [Installation page](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation) + - For Mac users: `brew install gnupg` + - For Windows users: Download from [Gpg4win](https://www.gpg4win.org/) + - For Linux users: Use your distribution's package manager (e.g., `apt install gnupg` for Ubuntu) + +### Getting Started + +1. Fork [Commitizen](https://github.com/commitizen-tools/commitizen) +2. Clone your fork: + ```bash + git clone https://github.com/YOUR_USERNAME/commitizen.git + cd commitizen + ``` +3. Add the upstream repository: + ```bash + git remote add upstream https://github.com/commitizen-tools/commitizen.git + ``` +4. Set up the development environment: + ```bash + poetry install + ``` +5. Set up pre-commit hooks: + ```bash + poetry setup-pre-commit + ``` + +## Development Workflow + +1. **Create a New Branch** + ```bash + git switch -c feature/your-feature-name + # or + git switch -c fix/your-bug-fix + ``` +2. **Make Your Changes** + - Write your code + - Add tests for new functionalities or fixes + - Update documentation if needed + - Follow the existing code style +3. **Testing** + - Run the full test suite: `poetry all` + - Ensure test coverage doesn't drop (we use [CodeCov](https://codecov.io/)) + - For documentation changes, run `poetry doc` to check for warnings/errors +4. **Committing Changes** + - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/)) + - Example: `cz commit` +5. **Documentation** + - Update `docs/README.md` if needed + - For CLI help screenshots: `poetry doc:screenshots` + - **DO NOT** update `CHANGELOG.md` (automatically generated) + - **DO NOT** update version numbers (automatically handled) +6. **Pull Request** + - Push your changes: `git push origin your-branch-name` + - Create a pull request on GitHub + - Ensure CI checks pass + - Wait for review and address any feedback ## Use of GitHub Labels @@ -57,7 +103,7 @@ If you're a first-time contributor, you can check the issues with the [good firs * os: macOS -### Issue life cycle +## Issue life cycle ```mermaid graph TD @@ -75,7 +121,7 @@ graph TD close --> output[/close/] ``` -### Pull request life cycle +## Pull request life cycle ```mermaid flowchart TD @@ -103,6 +149,3 @@ flowchart TD --modification-received--> review ``` - - -[conventional-commits]: https://www.conventionalcommits.org/ From 5e14843ef8ac0c85724588e0f49317132c3cab0f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 12 May 2025 03:05:22 +0800 Subject: [PATCH 026/275] docs(pyproject.toml): fix typo --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b306e5e78c..a653ecdb5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -257,7 +257,7 @@ all.sequence = [ "check-commit", ] -"doc:screenshots".help = "Render documentation screeenshots" +"doc:screenshots".help = "Render documentation screenshots" "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots" "doc:build".help = "Build the documentation" From 614eaa3995cf45f55e8d1e7efdcf52e94946f2f8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 13 May 2025 00:29:29 +0800 Subject: [PATCH 027/275] docs(README): update get project version section --- docs/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index 144c253a81..721ab99791 100644 --- a/docs/README.md +++ b/docs/README.md @@ -166,16 +166,16 @@ For all available options, see the [bump command documentation](./commands/bump. #### Get Project Version -To get your project's version (instead of Commitizen's version): ```sh +# Get your project's version (instead of Commitizen's version) cz version -p -``` - -This is particularly useful for automation. For example, to preview changelog changes for Slack: -```sh +# Preview changelog changes cz changelog --dry-run "$(cz version -p)" ``` +This command is particularly useful for automation scripts and CI/CD pipelines. +For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack. + #### Pre-commit Integration Commitizen can automatically validate your commit messages using pre-commit hooks. From f893d082030527dc965348ec6dfb163130b75359 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 14 May 2025 23:00:31 +0800 Subject: [PATCH 028/275] docs(README): update installation methods --- docs/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/README.md b/docs/README.md index 721ab99791..e35cf5e358 100644 --- a/docs/README.md +++ b/docs/README.md @@ -61,12 +61,9 @@ Before installing Commitizen, ensure you have: #### Global Installation (Recommended) -The recommended way to install Commitizen is using `pipx`, which ensures a clean, isolated installation: - +The recommended way to install Commitizen is using [`pipx`](https://pipx.pypa.io/) or [`uv`](https://docs.astral.sh/uv/), which ensures a clean, isolated installation: +**Using pipx:** ```bash -# Install pipx if you haven't already -pipx ensurepath - # Install Commitizen pipx install commitizen @@ -74,8 +71,16 @@ pipx install commitizen pipx upgrade commitizen ``` -If you're on macOS, you can also install Commitizen using Homebrew: +**Using uv:** +```bash +# Install commitizen +uv tool install commitizen +# Keep it updated +uv tool upgrade commitizen +``` + +**(For macOS users) Using Homebrew:** ```bash brew install commitizen ``` @@ -85,19 +90,16 @@ brew install commitizen You can add Commitizen to your Python project using any of these package managers: **Using pip:** - ```bash pip install -U commitizen ``` **Using conda:** - ```bash conda install -c conda-forge commitizen ``` **Using Poetry:** - ```bash # For Poetry >= 1.2.0 poetry add commitizen --group dev From 6930aaaea170fba7fa1aca13cbff40b11d79564e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 20:54:23 +0800 Subject: [PATCH 029/275] docs(README): use cli screenshots Closes #1413 --- docs/README.md | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/docs/README.md b/docs/README.md index e35cf5e358..ed1aaef2a1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -217,36 +217,7 @@ For more details about commit validation, see the [check command documentation]( Commitizen provides a comprehensive CLI with various commands. Here's the complete reference: - - -```sh -$ cz --help -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... - -Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. -For more information, please visit https://commitizen-tools.github.io/commitizen - -options: - -h, --help show this help message and exit - --config CONFIG the path of configuration file - --debug use debug mode - -n, --name NAME use the given commitizen (default: cz_conventional_commits) - -nr, --no-raise NO_RAISE - comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/ - -commands: - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} - init init commitizen configuration - commit (c) create new commit - ls show available commitizens - example show commit example - info show information about the cz - schema show commit schema - bump bump semantic version based on the git log - changelog (ch) generate changelog (note that it will overwrite existing file) - check validates that a commit message matches the commitizen schema - version get the version of the installed commitizen or the current project (default: installed commitizen) -``` +![cz --help](images/cli_help/cz___help.svg) ### Quick Reference From ec57f7de0da977f43157b7235e7f817399bc10ed Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 21:13:54 +0800 Subject: [PATCH 030/275] docs: fix link warnings and minor grammar issues --- docs/commands/bump.md | 10 +++++----- docs/config.md | 24 ++++++++++++------------ docs/customization.md | 4 ++-- docs/tutorials/github_actions.md | 4 ++-- docs/tutorials/gitlab_ci.md | 4 ++-- docs/tutorials/jenkins_pipeline.md | 2 +- docs/tutorials/monorepo_guidance.md | 4 ++-- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index efdba76257..6eb5b55c54 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -72,7 +72,7 @@ cz bump --changelog The bump is a pre-release bump, meaning that in addition to a possible version bump the new version receives a pre-release segment compatible with the bump’s version scheme, where the segment consist of a _phase_ and a -non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or +non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or `rc` (release candidate). For more details, refer to the [Python Packaging User Guide](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases). @@ -387,7 +387,7 @@ to skip and why. Remember to document somewhere this, because you'll forget. For example if the system raises a `NoneIncrementExit` error, you look it up -on the list and then you can use the exit code: +on the list, and then you can use the exit code: ```sh cz -nr 21 bump @@ -403,7 +403,7 @@ These are used in: - `cz bump`: Find previous release tag (exact match) and generate new tag. - Find previous release tags in `cz changelog`. - - If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. + - If `--incremental`: Using the latest version found in the changelog, scan existing Git tags with 89\% similarity match. - `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. - If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: - If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix) @@ -477,7 +477,7 @@ in a line containing the `version` substring. Template used to specify the commit message generated when bumping. -defaults to: `bump: version $current_version → $new_version` +Defaults to: `bump: version $current_version → $new_version` | Variable | Description | | ------------------ | ----------------------------------- | @@ -499,7 +499,7 @@ bump_message = "release $current_version → $new_version [skip-ci]" When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time. -defaults to: `false` +Defaults to: `false` ```toml [tool.commitizen] diff --git a/docs/config.md b/docs/config.md index b6dd794fb3..1ea02d5f67 100644 --- a/docs/config.md +++ b/docs/config.md @@ -59,7 +59,7 @@ Default: `[ ]` Legacy git tag formats, useful for old projects that changed tag format. Tags matching those formats will be recognized as version tags and be included in the changelog. -Each entry use the the syntax as [`tag_format`](#tag_format). [Read more][tag_format] +Each entry uses the syntax as [`tag_format`](#tag_format). [Read more][tag_format] ### `ignored_tag_formats` @@ -68,7 +68,7 @@ Type: `list` Default: `[ ]` Tags matching those formats will be totally ignored and won't raise a warning. -Each entry use the the syntax as [`tag_format`](#tag_format) with the addition of `*` +Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*` that will match everything (non-greedy). [Read more][tag_format] ### `update_changelog_on_bump` @@ -101,7 +101,7 @@ Type: `str` Default: `None` -Create custom commit message, useful to skip ci. [Read more][bump_message] +Create custom commit message, useful to skip CI. [Read more][bump_message] ### `retry_after_failure` @@ -117,7 +117,7 @@ Type: `bool` Default: `false` -Disallow empty commit messages, useful in ci. [Read more][allow_abort] +Disallow empty commit messages, useful in CI. [Read more][allow_abort] ### `allowed_prefixes` @@ -195,7 +195,7 @@ Type: `bool` Default: `false` -When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] +When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero] ### `prerelease_offset` @@ -203,7 +203,7 @@ Type: `int` Default: `0` -In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset] +In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [Read more][prerelease-offset] ### `pre_bump_hooks` @@ -247,7 +247,7 @@ Provide extra variables to the changelog template. [Read more][template-customiz ## Configuration file -### pyproject.toml, .cz.toml or cz.toml +### `pyproject.toml`, `.cz.toml` or `cz.toml` Default and recommended configuration format for a project. For a **python** project, we recommend adding an entry to your `pyproject.toml`. @@ -278,7 +278,7 @@ style = [ ] ``` -### .cz.json or cz.json +### `.cz.json` or `cz.json` Commitizen has support for JSON configuration. Recommended for `NodeJS` projects. @@ -304,7 +304,7 @@ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects } ``` -### .cz.yaml or cz.yaml +### `.cz.yaml` or `cz.yaml` YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects. @@ -358,7 +358,7 @@ Commitizen provides some version providers for some well known formats: | `composer` | Get and set version from `composer.json` `project.version` field | !!! note -The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. + The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. An example in your `.cz.toml` or `cz.toml` would look like this: @@ -408,10 +408,10 @@ setup( [tag_format]: commands/bump.md#tag_format [bump_message]: commands/bump.md#bump_message [major-version-zero]: commands/bump.md#-major-version-zero -[prerelease-offset]: commands/bump.md#-prerelease_offset +[prerelease-offset]: commands/bump.md#prerelease_offset [retry_after_failure]: commands/commit.md#retry [allow_abort]: commands/check.md#allow-abort -[version-scheme]: commands/bump.md#version-scheme +[version-scheme]: commands/bump.md#-version-scheme [pre_bump_hooks]: commands/bump.md#pre_bump_hooks [post_bump_hooks]: commands/bump.md#post_bump_hooks [allowed_prefixes]: commands/check.md#allowed-prefixes diff --git a/docs/customization.md b/docs/customization.md index cef03469e0..805173e900 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -490,7 +490,7 @@ Users can provide their own template from their current working directory (your - setting your template path as `template` configuration - giving your template path as `--template` parameter to `bump` and `changelog` commands -!!! Note +!!! note The path is relative to the current working directory, aka your project root most of the time. ### Template variables @@ -514,7 +514,7 @@ Each `Change` has the following fields: | author | `str` | The commit author name | | author_email | `str` | The commit author email | -!!! Note +!!! note The field values depend on the customization class and/or the settings you provide The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index bcb3fda22c..4f50aaf4e7 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -41,14 +41,14 @@ jobs: Push to master and that's it. -### Creating a github release +### Creating a GitHub release You can modify the previous action. Add the variable `changelog_increment_filename` in the `commitizen-action`, specifying where to output the content of the changelog for the newly created version. -And then add a step using a github action to create the release: `softprops/action-gh-release` +And then add a step using a GitHub action to create the release: `softprops/action-gh-release` The commitizen action creates an env variable called `REVISION`, containing the newly created version. diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md index 85b6a615c3..6f6d53a57e 100644 --- a/docs/tutorials/gitlab_ci.md +++ b/docs/tutorials/gitlab_ci.md @@ -12,9 +12,9 @@ _Goal_: Bump a new version every time that a change occurs on the `master` branc 4. For simplification, we store the software version in a file called `VERSION`. You can use any file that you want as `commitizen` supports it. 5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../commands/bump.md) as well. -### Gitlab Configuration +### GitLab Configuration -To be able to change files and push new changes with `Gitlab CI` runners, we need to have a `ssh` key and configure a git user. +To be able to change files and push new changes with `GitLab CI` runners, we need to have a `ssh` key and configure a git user. First, let's create a `ssh key`. The only requirement is to create it without a passphrase: diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md index fb87820c4c..2b9ad173d3 100644 --- a/docs/tutorials/jenkins_pipeline.md +++ b/docs/tutorials/jenkins_pipeline.md @@ -47,7 +47,7 @@ def useCz(String authorName = 'Jenkins CI Server', String authorEmail = 'your-je ``` !!! warning - Using jenkins pipeline with any git plugin may require many different configurations, + Using jenkins pipeline with any git plugin may require many configurations, you'll have to tinker with it until your pipelines properly detects git events. Check your webhook in your git repository and check the "behaviors" and "build strategies" in your pipeline settings. diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md index 792c8c224f..434899f86f 100644 --- a/docs/tutorials/monorepo_guidance.md +++ b/docs/tutorials/monorepo_guidance.md @@ -56,9 +56,9 @@ In order to filter the correct commits for each component, you'll have to come u For example: - Trigger the pipeline based on the changed path, which can have some downsides, as you'll rely on the developer not including files from other files - - [github actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` + - [GitHub actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` - [Jenkins](https://www.jenkins.io/doc/book/pipeline/syntax/#built-in-conditions) uses `changeset` - - [Gitlab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes` + - [GitLab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes` - Filter certain pattern of the commit message (recommended) From 71c2e914d477ac7e45a05eddb9ad511f4fe0c4ca Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 21:22:04 +0800 Subject: [PATCH 031/275] docs(pull_request_template): add broken link check to the checklist --- .github/pull_request_template.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e28480e5b9..5686474709 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,6 +25,13 @@ Please fill in the following content to let us know better about this change. ### Documentation Changes - [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly + - [ ] Check if there are any broken links in the documentation + +> When running `poetry doc`, any broken internal documentation links will be reported in the console output like this: +> +> ```text +> INFO - Doc file 'config.md' contains a link 'commands/bump.md#-post_bump_hooks', but the doc 'commands/bump.md' does not contain an anchor '#-post_bump_hooks'. +> ``` ## Expected Behavior From 2a68e89aed582ef4d89c3d0799406f5cb6584c5a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 21:27:41 +0800 Subject: [PATCH 032/275] docs(contributing): update codecov link --- docs/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.md b/docs/contributing.md index 4ca12765aa..336f83352a 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -58,7 +58,7 @@ If you're a first-time contributor, you can check the issues with the [good firs - Follow the existing code style 3. **Testing** - Run the full test suite: `poetry all` - - Ensure test coverage doesn't drop (we use [CodeCov](https://codecov.io/)) + - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen)) - For documentation changes, run `poetry doc` to check for warnings/errors 4. **Committing Changes** - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/)) From 1c58b1c272c4ab9ee2ce93907280d5ac3bde10a6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 21:44:23 +0800 Subject: [PATCH 033/275] docs(README): update project specific installation methods --- docs/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/README.md b/docs/README.md index ed1aaef2a1..e0c3fed747 100644 --- a/docs/README.md +++ b/docs/README.md @@ -108,6 +108,16 @@ poetry add commitizen --group dev poetry add commitizen --dev ``` +**Using uv:** +```bash +uv add commitizen +``` + +**Using pdm:** +```bash +pdm add -d commitizen +``` + ### Basic Commands #### Initialize Commitizen From 70af1a6dc947cb78d10de546d6441b89ccd5bcd8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 15 May 2025 22:03:19 +0800 Subject: [PATCH 034/275] docs: capitalize commitizen for consistency --- docs/README.md | 2 +- docs/commands/bump.md | 26 +++++++++---------- docs/commands/commit.md | 10 +++---- docs/commands/init.md | 2 +- docs/commands/version.md | 2 +- docs/config.md | 6 ++--- docs/contributing.md | 2 +- docs/customization.md | 12 ++++----- docs/exit_codes.md | 2 +- docs/external_links.md | 2 +- docs/faq.md | 6 ++--- docs/third-party-commitizen.md | 4 +-- docs/tutorials/auto_prepare_commit_message.md | 8 +++--- docs/tutorials/github_actions.md | 4 +-- docs/tutorials/monorepo_guidance.md | 2 +- docs/tutorials/writing_commits.md | 2 +- 16 files changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/README.md b/docs/README.md index e0c3fed747..9bb1555721 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,7 @@ [![Codecov](https://img.shields.io/codecov/c/github/commitizen-tools/commitizen.svg?style=flat-square)](https://codecov.io/gh/commitizen-tools/commitizen) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?style=flat-square&logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) -![Using commitizen cli](images/demo.gif) +![Using Commitizen cli](images/demo.gif) --- diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 6eb5b55c54..ee3d77430a 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -116,7 +116,7 @@ Below are some examples that illustrate the difference in behavior: ### `--check-consistency` -Check whether the versions defined in `version_files` and the version in commitizen +Check whether the versions defined in `version_files` and the version in Commitizen configuration are consistent before bumping version. ```bash @@ -148,7 +148,7 @@ from setuptools import setup setup(..., version="1.0.5", ...) ``` -If `--check-consistency` is used, commitizen will check whether the current version in `pyproject.toml` +If `--check-consistency` is used, Commitizen will check whether the current version in `pyproject.toml` exists in all version_files and find out it does not exist in `setup.py` and fails. However, it will still update `pyproject.toml` and `src/__version__.py`. @@ -174,11 +174,11 @@ If `--local-version` is used, it will bump only the local version `0.1.0` and ke ### `--annotated-tag` -If `--annotated-tag` is used, commitizen will create annotated tags. Also available via configuration, in `pyproject.toml` or `.cz.toml`. +If `--annotated-tag` is used, Commitizen will create annotated tags. It is also available via configuration, in `pyproject.toml` or `.cz.toml`. ### `--annotated-tag-message` -If `--annotated-tag-message` is used, commitizen will create annotated tags with the given message. +If `--annotated-tag-message` is used, Commitizen will create annotated tags with the given message. ### `--changelog-to-stdout` @@ -332,11 +332,11 @@ cz bump --allow-no-commit 2.0.0 ## Avoid raising errors -Some situations from commitizen raise an exit code different than 0. -If the error code is different than 0, any CI or script running commitizen might be interrupted. +Some situations from Commitizen raise an exit code different from 0. +If the error code is different from 0, any CI or script running Commitizen might be interrupted. If you have a special use case, where you don't want to raise one of this error codes, you can -tell commitizen to not raise them. +tell Commitizen to not raise them. ### Recommended use case @@ -355,7 +355,7 @@ cz -nr 21 bump ### Easy way -Check which error code was raised by commitizen by running in the terminal +Check which error code was raised by Commitizen by running in the terminal ```sh echo $? @@ -367,13 +367,13 @@ The output should be an integer like this 3 ``` -And then you can tell commitizen to ignore it: +And then you can tell Commitizen to ignore it: ```sh cz --no-raise 3 ``` -You can tell commitizen to skip more than one if needed: +You can tell Commitizen to skip more than one if needed: ```sh cz --no-raise 3,4,5 @@ -510,7 +510,7 @@ update_changelog_on_bump = true ### `annotated_tag` -When set to `true` commitizen will create annotated tags. +When set to `true`, Commitizen will create annotated tags. ```toml [tool.commitizen] @@ -521,7 +521,7 @@ annotated_tag = true ### `gpg_sign` -When set to `true` commitizen will create gpg signed tags. +When set to `true`, Commitizen will create gpg signed tags. ```toml [tool.commitizen] @@ -532,7 +532,7 @@ gpg_sign = true ### `major_version_zero` -When set to `true` commitizen will keep the major version at zero. +When set to `true`, Commitizen will keep the major version at zero. Useful during the initial development stage of your project. Defaults to: `false` diff --git a/docs/commands/commit.md b/docs/commands/commit.md index ea033cc411..febaee3cf2 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -1,4 +1,4 @@ -![Using commitizen cli](../images/demo.gif) +![Using Commitizen cli](../images/demo.gif) ## About @@ -20,14 +20,14 @@ case for this is to [automatically prepare a commit message](../tutorials/auto_p ### git options -`git` command options that are not implemented by commitizen can be use via the `--` syntax for the `commit` command. -The syntax separates commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax: +`git` command options that are not implemented by Commitizen can be use via the `--` syntax for the `commit` command. +The syntax separates Commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax: ```sh cz commit -- # e.g., cz commit --dry-run -- -a -S ``` -For example, using the `-S` option on `git commit` to sign a commit is now commitizen compatible: `cz c -- -S` +For example, using the `-S` option on `git commit` to sign a commit is now Commitizen compatible: `cz c -- -S` !!! note Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`. @@ -37,7 +37,7 @@ For example, using the `-S` option on `git commit` to sign a commit is now commi You can use `cz commit --retry` to reuse the last commit message when the previous commit attempt failed. To automatically retry when running `cz commit`, you can set the `retry_after_failure` -configuration option to `true`. Running `cz commit --no-retry` makes commitizen ignore `retry_after_failure`, forcing +configuration option to `true`. Running `cz commit --no-retry` makes Commitizen ignore `retry_after_failure`, forcing a new commit message to be prompted. ### Commit message length limit diff --git a/docs/commands/init.md b/docs/commands/init.md index 01e1db6be8..a799c44810 100644 --- a/docs/commands/init.md +++ b/docs/commands/init.md @@ -4,7 +4,7 @@ ## Example -To start using commitizen, the recommended approach is to run +To start using Commitizen, the recommended approach is to run ```sh cz init diff --git a/docs/commands/version.md b/docs/commands/version.md index 9a8176b45f..4d2e6a0323 100644 --- a/docs/commands/version.md +++ b/docs/commands/version.md @@ -1,4 +1,4 @@ -Get the version of the installed commitizen or the current project (default: installed commitizen) +Get the version of the installed Commitizen or the current project (default: installed commitizen) ## Usage diff --git a/docs/config.md b/docs/config.md index 1ea02d5f67..5ca2c5d788 100644 --- a/docs/config.md +++ b/docs/config.md @@ -187,7 +187,7 @@ Type: `bool` Default: `false` -If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] +If enabled, Commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] ### `major_version_zero` @@ -341,14 +341,14 @@ commitizen: ## Version providers Commitizen can read and write version from different sources. -By default, it uses the `commitizen` one which is using the `version` field from the commitizen settings. +By default, it uses the `commitizen` one which is using the `version` field from the Commitizen settings. But you can use any `commitizen.provider` entrypoint as value for `version_provider`. Commitizen provides some version providers for some well known formats: | name | description | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `commitizen` | Default version provider: Fetch and set version in commitizen config. | +| `commitizen` | Default version provider: Fetch and set version in Commitizen config. | | `scm` | Fetch the version from git and does not need to set it back | | `pep621` | Get and set version from `pyproject.toml` `project.version` field | | `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field | diff --git a/docs/contributing.md b/docs/contributing.md index 336f83352a..e9e162d2df 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -61,7 +61,7 @@ If you're a first-time contributor, you can check the issues with the [good firs - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen)) - For documentation changes, run `poetry doc` to check for warnings/errors 4. **Committing Changes** - - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/)) + - Use Commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/)) - Example: `cz commit` 5. **Documentation** - Update `docs/README.md` if needed diff --git a/docs/customization.md b/docs/customization.md index 805173e900..e97558a308 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -1,4 +1,4 @@ -Customizing commitizen is not hard at all. +Customizing Commitizen is not hard at all. We have two different ways to do so. ## 1. Customize in configuration file @@ -6,7 +6,7 @@ We have two different ways to do so. The basic steps are: 1. Define your custom committing or bumping rules in the configuration file. -2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running commitizen. +2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen. Example: @@ -184,8 +184,8 @@ commitizen: #### Shortcut keys -When the [`use_shortcuts`](config.md#settings) config option is enabled, commitizen can show and use keyboard shortcuts to select items from lists directly. -For example, when using the `cz_conventional_commits` commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically. +When the [`use_shortcuts`](config.md#settings) config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly. +For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically. To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize. ## 2. Customize through customizing a class @@ -304,7 +304,7 @@ class StrangeCommitizen(BaseCommitizen): bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"} ``` -That's it, your commitizen now supports custom rules, and you can run. +That's it, your Commitizen now supports custom rules, and you can run. ```bash cz -n cz_strange bump @@ -451,7 +451,7 @@ Commitizen gives you the possibility to provide your own changelog template, by: - as `--template` parameter to both `bump` and `changelog` commands - either by providing a template with the same name as the default template -By default, the template used is the `CHANGELOG.md.j2` file from the commitizen repository. +By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository. ### Providing a template with your customization class diff --git a/docs/exit_codes.md b/docs/exit_codes.md index af9cb83627..fd92961d38 100644 --- a/docs/exit_codes.md +++ b/docs/exit_codes.md @@ -20,7 +20,7 @@ These exit codes can be found in `commitizen/exceptions.py::ExitCode`. | NoCommitBackupError | 10 | Commit back up file cannot be found | | NothingToCommitError | 11 | Nothing in staging to be committed | | CustomError | 12 | `CzException` raised | -| NoCommandFoundError | 13 | No command found when running commitizen cli (e.g., `cz --debug`) | +| NoCommandFoundError | 13 | No command found when running Commitizen cli (e.g., `cz --debug`) | | InvalidCommitMessageError | 14 | The commit message does not pass `cz check` | | MissingConfigError | 15 | Configuration missed for `cz_customize` | | NoRevisionError | 16 | No revision found | diff --git a/docs/external_links.md b/docs/external_links.md index 388bcc8dea..24e4127d86 100644 --- a/docs/external_links.md +++ b/docs/external_links.md @@ -1,4 +1,4 @@ -> If you have written over commitizen, make a PR and add the link here 💪 +> If you have written over Commitizen, make a PR and add the link here 💪 ## Talks diff --git a/docs/faq.md b/docs/faq.md index 0302efd267..ceabac2e10 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -63,7 +63,7 @@ Where do they cross paths? If you are using conventional commits in your git history, then you could swap one with the other in theory. -Regarding the name, [cz-js][cz-js] came first, they used the word commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen". +Regarding the name, [cz-js][cz-js] came first, they used the word Commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen". [cz-js]: https://github.com/commitizen/cz-cli @@ -80,7 +80,7 @@ This error was caused by a Python bug on Windows. It's been fixed by [this PR](h More discussion can be found in issue [#318](https://github.com/commitizen-tools/commitizen/issues/318). -## Why does commitizen not support CalVer? +## Why does Commitizen not support CalVer? `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer creates numerous edge cases that are difficult to maintain ([#385]) and more generally, @@ -117,7 +117,7 @@ New bumped tags will be in the new format but old ones will still work for: So given if you change from `myproject-$version` to `${version}` and then `v${version}`, -your commitizen configuration will look like this: +your Commitizen configuration will look like this: ```toml tag_format = "v${version}" diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md index e9eb822ed3..dc9b539c85 100644 --- a/docs/third-party-commitizen.md +++ b/docs/third-party-commitizen.md @@ -33,9 +33,9 @@ pip install conventional-JIRA ### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) -This plugin extends the commitizen tools by: +This plugin extends the Commitizen tools by: -- requiring a JIRA issue id in the commit message +- requiring a JIRA issue ID in the commit message - creating links to GitHub commits in the CHANGELOG.md - creating links to JIRA issues in the CHANGELOG.md diff --git a/docs/tutorials/auto_prepare_commit_message.md b/docs/tutorials/auto_prepare_commit_message.md index 7e8295b7c8..84ac62b689 100644 --- a/docs/tutorials/auto_prepare_commit_message.md +++ b/docs/tutorials/auto_prepare_commit_message.md @@ -2,7 +2,7 @@ ## About -It can be desirable to use commitizen for all types of commits (i.e. regular, merge, +It can be desirable to use Commitizen for all types of commits (i.e. regular, merge, squash) so that the complete git history adheres to the commit message convention without ever having to call `cz commit`. @@ -18,10 +18,10 @@ To automatically perform arbitrary cleanup steps after a successful commit you c > This hook is invoked by git-commit. It takes no parameters, and is invoked after a > commit is made. -A combination of these two hooks allows for enforcing the usage of commitizen so that -whenever a commit is about to be created, commitizen is used for creating the commit +A combination of these two hooks allows for enforcing the usage of Commitizen so that +whenever a commit is about to be created, Commitizen is used for creating the commit message. Running `git commit` or `git commit -m "..."` for example, would trigger -commitizen and use the generated commit message for the commit. +Commitizen and use the generated commit message for the commit. ## Installation diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index 4f50aaf4e7..bf08eb3bc5 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -50,7 +50,7 @@ where to output the content of the changelog for the newly created version. And then add a step using a GitHub action to create the release: `softprops/action-gh-release` -The commitizen action creates an env variable called `REVISION`, containing the +Commitizen action creates an env variable called `REVISION`, containing the newly created version. ```yaml @@ -119,7 +119,7 @@ jobs: ./scripts/publish ``` -Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) +Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [Commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package. Push the changes and that's it. diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md index 434899f86f..6f15a87247 100644 --- a/docs/tutorials/monorepo_guidance.md +++ b/docs/tutorials/monorepo_guidance.md @@ -1,4 +1,4 @@ -# Configuring commitizen in a monorepo +# Configuring Commitizen in a monorepo This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each other, it also assumes that conventional commits with scopes are in use. Some suggested layouts: diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md index d1b2c6645d..7d9139929c 100644 --- a/docs/tutorials/writing_commits.md +++ b/docs/tutorials/writing_commits.md @@ -1,6 +1,6 @@ For this project to work well in your pipeline, a commit convention must be followed. -By default, commitizen uses the known [conventional commits][conventional_commits], but +By default, Commitizen uses the known [conventional commits][conventional_commits], but you can create your own following the documentation information over at [customization][customization]. From f6280c88a14aabf30324a4d9901f4fee577debfc Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 18 May 2025 21:41:24 +0800 Subject: [PATCH 035/275] docs(README): update documentation site link text --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 9bb1555721..516b2a4863 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ --- -**Documentation:** [https://commitizen-tools.github.io/commitizen/](https://commitizen-tools.github.io/commitizen/) +[**Commitizen Documentation Site**](https://commitizen-tools.github.io/commitizen/) --- From ba1c9c0ce4cba699e61a2ab26d08d3159d0f3bdb Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 18 May 2025 21:46:08 +0800 Subject: [PATCH 036/275] docs(README): replace internal links with documentation site link --- docs/README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index 516b2a4863..078d6da06e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,11 +42,11 @@ This standardization makes your commit history more readable and meaningful, whi ### Features - Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits] -- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](./commands/bump.md) +- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](https://commitizen-tools.github.io/commitizen/commands/bump/) - Generate a changelog using [Keep a changelog][keepchangelog] - Update your project's version files automatically - Display information about your commit rules (commands: schema, example, info) -- Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md) +- Create your own set of rules and publish them to pip. Read more on [Customization](https://commitizen-tools.github.io/commitizen/customization/) ## Getting Started @@ -168,11 +168,11 @@ This command: - Updates version files You can customize: -- [Version files](./commands/bump.md#version_files) -- [Version scheme](./commands/bump.md#version_scheme) -- [Version provider](./config.md#version-providers) +- [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files) +- [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme) +- [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers) -For all available options, see the [bump command documentation](./commands/bump.md). +For all available options, see the [bump command documentation](https://commitizen-tools.github.io/commitizen/commands/bump/). ### Advanced Usage @@ -219,7 +219,7 @@ pre-commit install --hook-type commit-msg --hook-type pre-push > pre-commit autoupdate > ``` -For more details about commit validation, see the [check command documentation](commands/check.md). +For more details about commit validation, see the [check command documentation](https://commitizen-tools.github.io/commitizen/commands/check/). ## Help & Reference @@ -243,9 +243,9 @@ Commitizen provides a comprehensive CLI with various commands. Here's the comple ### Additional Resources - [Conventional Commits Specification][conventional_commits] -- [Exit Codes Reference](./exit_codes.md) -- [Configuration Guide](./config.md) -- [Command Documentation](./commands/init.md) +- [Exit Codes Reference](https://commitizen-tools.github.io/commitizen/exit_codes/) +- [Configuration Guide](https://commitizen-tools.github.io/commitizen/config/) +- [Command Documentation](https://commitizen-tools.github.io/commitizen/commands/init/) ### Getting Help @@ -257,7 +257,7 @@ cz bump --help cz changelog --help ``` -For more detailed documentation, visit our [documentation site](https://commitizen-tools.github.io/commitizen/). +For more details, visit our [documentation site](https://commitizen-tools.github.io/commitizen/). ## Setting up bash completion From bb1539deb206d58f7b94c3b8ece268f8f34f287d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 18 May 2025 21:57:02 +0800 Subject: [PATCH 037/275] docs(README): paraphase features --- docs/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index 078d6da06e..f730c17837 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,12 +41,13 @@ This standardization makes your commit history more readable and meaningful, whi ### Features -- Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits] -- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](https://commitizen-tools.github.io/commitizen/commands/bump/) -- Generate a changelog using [Keep a changelog][keepchangelog] -- Update your project's version files automatically -- Display information about your commit rules (commands: schema, example, info) -- Create your own set of rules and publish them to pip. Read more on [Customization](https://commitizen-tools.github.io/commitizen/customization/) +- Interactive CLI for standardized commits with default [Conventional Commits][conventional_commits] support +- Intelligent [version bumping](https://commitizen-tools.github.io/commitizen/commands/bump/) using [Semantic Versioning][semver] +- Automatic [keep a changelog][keepchangelog] generation +- Built-in commit validation with pre-commit hooks +- [Customizable](https://commitizen-tools.github.io/commitizen/customization/) commit rules and templates +- Multi-format version file support +- Custom rules and plugins via pip ## Getting Started From 8e65b88288328dd794e9f8046f5b1164c9afb569 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 May 2025 01:27:30 +0000 Subject: [PATCH 038/275] docs(cli/screenshots): update CLI screenshots [skip ci] --- docs/images/cli_help/cz___help.svg | 172 +++++++++++++++-------------- 1 file changed, 88 insertions(+), 84 deletions(-) diff --git a/docs/images/cli_help/cz___help.svg b/docs/images/cli_help/cz___help.svg index 098e7df70d..d580cfe619 100644 --- a/docs/images/cli_help/cz___help.svg +++ b/docs/images/cli_help/cz___help.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - - $ cz --help -usage: cz [-h][--config CONFIG][--debug][-n NAME][-nr NO_RAISE] -{init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} -... - -Commitizen is a cli tool to generate conventional commits. -For more information about the topic go to https://conventionalcommits.org/ - -options: -  -h, --help            show this help message and exit -  --config CONFIG       the path of configuration file -  --debug               use debug mode -  -n, --name NAME       use the given commitizen (default: -                        cz_conventional_commits) -  -nr, --no-raise NO_RAISE -                        comma separated error codes that won't rise error, -                        e.g: cz -nr 1,2,3 bump. See codes at -https://commitizen- -                        tools.github.io/commitizen/exit_codes/ - -commands: -{init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} -    init                init commitizen configuration -    commit (c)          create new commit -    ls                  show available commitizens -    example             show commit example -    info                show information about the cz -    schema              show commit schema -    bump                bump semantic version based on the git log -    changelog (ch)      generate changelog (note that it will overwrite -                        existing file) -    check               validates that a commit message matches the commitizen -                        schema -    version             get the version of the installed commitizen or the -                        current project (default: installed commitizen) - + + $ cz --help +usage: cz [-h][--config CONFIG][--debug][-n NAME][-nr NO_RAISE] +{init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} +... + +Commitizen is a powerful release management tool that helps teams maintain  +consistent and meaningful commit messages while automating version management. +For more information, please visit https://commitizen-tools.github.io/commitizen + +options: +  -h, --help            show this help message and exit +  --config CONFIG       the path of configuration file +  --debug               use debug mode +  -n, --name NAME       use the given commitizen (default: +                        cz_conventional_commits) +  -nr, --no-raise NO_RAISE +                        comma separated error codes that won't raise error, +                        e.g: cz -nr 1,2,3 bump. See codes at +https://commitizen- +                        tools.github.io/commitizen/exit_codes/ + +commands: +{init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} +    init                init commitizen configuration +    commit (c)          create new commit +    ls                  show available commitizens +    example             show commit example +    info                show information about the cz +    schema              show commit schema +    bump                bump semantic version based on the git log +    changelog (ch)      generate changelog (note that it will overwrite +                        existing file) +    check               validates that a commit message matches the commitizen +                        schema +    version             get the version of the installed commitizen or the +                        current project (default: installed commitizen) + From aaa64aca07db9acd52ee069fc4abb572524e0cf4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 24 May 2025 00:56:51 +0800 Subject: [PATCH 039/275] docs(faq): add features we wont add Closes #1129 --- docs/faq.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index ceabac2e10..920f533d73 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,3 +1,11 @@ +## Features we won't add + +For a comprehensive list of features that have been considered but won't be implemented, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22). + +- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955) +- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790) +- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597) + ## Support for PEP621 PEP621 establishes a `[project]` definition inside `pyproject.toml` From 3560156e518416846baec3cd7d3b772a7478ffb8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 24 May 2025 01:17:32 +0800 Subject: [PATCH 040/275] docs(bump): rewrite bump about section, fix incorrect version increment rules Closes #781 --- docs/commands/bump.md | 92 ++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index ee3d77430a..d399ec9eb8 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -2,51 +2,79 @@ ## About -`cz bump` **automatically** increases the version, based on the commits. +`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. It analyzes your commits to determine the appropriate version increment according to semantic versioning principles. -The commits should follow the rules established by the committer in order to be parsed correctly. +### Key Features -**prerelease** versions are supported (alpha, beta, release candidate). +- **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump +- **Manual Version Control**: Supports manual version specification when needed +- **Pre-release Support**: Handles alpha, beta, and release candidate versions +- **Multiple Version Schemes**: Supports both [PEP 0440][pep440] and [semantic versioning][semver] formats -The version can also be **manually** bumped. +### Version Increment Rules -The version format follows [PEP 0440][pep440] and [semantic versioning][semver]. - -This means `MAJOR.MINOR.PATCH` +The version follows the `MAJOR.MINOR.PATCH` format, with increments determined by your commit types: | Increment | Description | Conventional commit map | | --------- | --------------------------- | ----------------------- | -| `MAJOR` | Breaking changes introduced | `BREAKING CHANGE` | +| `MAJOR` | Breaking changes introduced | `BREAKING CHANGE`, bang (e.g. `feat!`)| | `MINOR` | New features | `feat` | -| `PATCH` | Fixes | `fix` + everything else | +| `PATCH` | Fixes and improvements | `fix`, `perf`, `refactor`| + +### Version Schemes -[PEP 0440][pep440] is the default, you can switch by using the setting `version_scheme` or the cli: +By default, Commitizen uses [PEP 0440][pep440] for version formatting. You can switch to semantic versioning using either: +1. Command line: ```sh cz bump --version-scheme semver ``` -Some examples of pep440: +2. Configuration file: +```toml +[tool.commitizen] +version_scheme = "semver" +``` -```bash -0.9.0 -0.9.1 -0.9.2 -0.9.10 -0.9.11 -1.0.0a0 # alpha -1.0.0a1 -1.0.0b0 # beta -1.0.0rc0 # release candidate -1.0.0rc1 -1.0.0 -1.0.1 -1.1.0 -2.0.0 -2.0.1a -``` - -`post` releases are not supported yet. +### PEP440 Version Examples + +Commitizen supports the [PEP 440][pep440] version format, which includes several version types. Here are examples of each: + +#### Standard Releases +```text +0.9.0 # Initial development release +0.9.1 # Patch release +0.9.2 # Another patch release +0.9.10 # Tenth patch release +0.9.11 # Eleventh patch release +1.0.0 # First stable release +1.0.1 # Patch release after stable +1.1.0 # Minor feature release +2.0.0 # Major version release +``` + +#### Pre-releases +```text +1.0.0a0 # Alpha release 0 +1.0.0a1 # Alpha release 1 +1.0.0b0 # Beta release 0 +1.0.0rc0 # Release candidate 0 +1.0.0rc1 # Release candidate 1 +``` + +#### Development Releases +```text +1.0.0.dev0 # Development release 0 +1.0.0.dev1 # Development release 1 +``` + +#### Combined Pre-release and Development +```text +1.0.0a1.dev0 # Development release 0 of alpha 1 +1.0.0b2.dev1 # Development release 1 of beta 2 +``` + +> **Note**: `post` releases (e.g., `1.0.0.post1`) are not currently supported. ## Usage @@ -97,7 +125,7 @@ by their precedence and showcase how a release might flow through a development By default, `--increment-mode` is set to `linear`, which ensures that bumping pre-releases _maintains linearity_: bumping of a pre-release with lower precedence than the current pre-release phase maintains the current phase of higher precedence. For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will -continue to bump the “beta” phase. +continue to bump the "beta" phase. Setting `--increment-mode` to `exact` instructs `cz bump` to instead apply the exact changes that have been specified with `--increment` or determined from the commit log. For example, @@ -223,7 +251,7 @@ If used together with a manual version the command also fails. We recommend setting `major_version_zero = true` in your configuration file while a project is in its initial development. Remove that configuration using a breaking-change commit to bump -your project’s major version to `v1.0.0` once your project has reached maturity. +your project's major version to `v1.0.0` once your project has reached maturity. ### `--version-scheme` From 08ced5dbfc6a3283be7cfac87afb86a652069622 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 28 May 2025 18:04:31 +0800 Subject: [PATCH 041/275] docs(README): fix broken bullet points --- docs/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/README.md b/docs/README.md index f730c17837..31f2a77d15 100644 --- a/docs/README.md +++ b/docs/README.md @@ -163,12 +163,14 @@ cz bump ``` This command: + - Bumps your project's version - Creates a git tag - Updates the changelog (if `update_changelog_on_bump` is enabled) - Updates version files You can customize: + - [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files) - [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme) - [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers) @@ -187,6 +189,7 @@ cz changelog --dry-run "$(cz version -p)" ``` This command is particularly useful for automation scripts and CI/CD pipelines. + For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack. #### Pre-commit Integration From 59e7532cccabf08a0a4b633a13ee174853f5c6f0 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 28 May 2025 18:31:53 +0800 Subject: [PATCH 042/275] docs(external_links): typo --- docs/external_links.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/external_links.md b/docs/external_links.md index 24e4127d86..a90bbd085b 100644 --- a/docs/external_links.md +++ b/docs/external_links.md @@ -4,7 +4,7 @@ | Name | Speaker | Occasion | Language | Extra | | ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | -| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipey.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | +| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | | Automating release cycles | Santiago Fraire | PyAmsterdam June 24, 2020, Online | English | [slides](https://woile.github.io/commitizen-presentation/) | | [Automatizando Releases con Commitizen y Github Actions][automatizando] | Santiago Fraire | PyConAr 2020, Remote | Español | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/) | From 739abd50a12f996144fc73a5f5dc0fa668b252dc Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 28 May 2025 17:52:14 +0800 Subject: [PATCH 043/275] docs(init): rewrite commands init page --- docs/commands/init.md | 67 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/docs/commands/init.md b/docs/commands/init.md index a799c44810..4d92112d34 100644 --- a/docs/commands/init.md +++ b/docs/commands/init.md @@ -1,27 +1,66 @@ +The `cz init` command helps you set up Commitizen in your project by creating a configuration file with your preferred settings. + ## Usage ![cz init --help](../images/cli_help/cz_init___help.svg) -## Example - -To start using Commitizen, the recommended approach is to run +## Command ```sh cz init ``` +## Interactive Configuration + +When you run `cz init`, Commitizen will guide you through an interactive setup process: + ![init](../images/init.gif) -This command will ask you for information about the project and will -configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.). +## Configuration File + +The initialization process will create a configuration file in your project root. + +Choose the configuration file format based on your project type: + +- Use `pyproject.toml` for Python projects +- Use `.cz.toml`, `.cz.yaml`, `.cz.json`, etc. for other projects. + +## Configuration Options + +During the initialization process, you'll be prompted to configure the following settings: + +1. **Convention Rules**: Select the commit message convention to follow (e.g., conventional commits) +2. **Version Provider**: Choose how to manage versioning in your project. Commitizen supports multiple version management systems: + - `commitizen`: Uses Commitizen's built-in version management system + - `npm`: Manages version in `package.json` for Node.js projects + - `cargo`: Manages version in `Cargo.toml` for Rust projects + - `composer`: Manages version in `composer.json` for PHP projects + - `pep621`: Uses `pyproject.toml` with PEP 621 standard + - `poetry`: Uses `pyproject.toml` with Poetry configuration + - `uv`: Uses `pyproject.toml` and `uv.lock` for Python projects + - `scm`: Reads version directly from git tags without modifying files +3. **Project Version**: The current version of your project will be detected automatically +4. **Tag Format**: The format used for version tags in your repository +5. **Version Type**: Choose between: + - `semver` or `semver2`: Semantic Versioning (MAJOR.MINOR.PATCH) + - `pep440`: Python Package Versioning +6. **Changelog Generation**: Configure whether to automatically generate changelog during version bumps +7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software +8. **Pre-commit Hooks**: Set up Git pre-commit hooks for automated commit message validation + +## Example + +```sh +# Start the initialization process +cz init + +# Follow the interactive prompts to configure your project +``` + +## Next Steps -The `init` will help you with +After initialization, you can: -1. Choose a convention rules (`name`) -2. Choosing a version provider (`commitizen` or for example `Cargo.toml`) -3. Detecting your project's version -4. Detecting the tag format used -5. Choosing a version type (`semver` or `pep440`) -6. Whether to create the changelog automatically or not during bump -7. Whether you want to keep the major as zero while building alpha software. -8. Whether to setup pre-commit hooks. +1. Start using `cz commit` to create conventional commits +2. Use `cz bump` to manage versioning +3. Configure additional settings in your project's configuration file From ceb2b7afef13b48a1fe978de3e4cbba472fb6609 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 28 May 2025 18:25:30 +0800 Subject: [PATCH 044/275] docs: correct case GitHub --- docs/commands/bump.md | 2 +- docs/tutorials/github_actions.md | 2 +- mkdocs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index d399ec9eb8..8219cc3461 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -219,7 +219,7 @@ understand that the user wants to create a changelog. It is recommended to be explicit and use `--changelog` (or the setting `update_changelog_on_bump`). This command is useful to "transport" the newly created changelog. -It can be sent to an auditing system, or to create a Github Release. +It can be sent to an auditing system, or to create a GitHub Release. Example: diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index bf08eb3bc5..2cb58cfee6 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -1,4 +1,4 @@ -## Create a new release with Github Actions +## Create a new release with GitHub Actions ### Automatic bumping of version diff --git a/mkdocs.yml b/mkdocs.yml index a8a2fe2d44..7b1e0feb30 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -50,7 +50,7 @@ nav: - Auto check commits: "tutorials/auto_check.md" - Auto prepare commit message: "tutorials/auto_prepare_commit_message.md" - GitLab CI: "tutorials/gitlab_ci.md" - - Github Actions: "tutorials/github_actions.md" + - GitHub Actions: "tutorials/github_actions.md" - Jenkins pipeline: "tutorials/jenkins_pipeline.md" - Developmental releases: "tutorials/dev_releases.md" - Monorepo support: "tutorials/monorepo_guidance.md" From 4b6b3fb4f9782ed0d55db5518b1e1523e0779eea Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 28 May 2025 18:16:16 +0800 Subject: [PATCH 045/275] docs(commit): rewrite command commit page --- docs/commands/commit.md | 75 ++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/docs/commands/commit.md b/docs/commands/commit.md index febaee3cf2..5a073a2644 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -1,52 +1,65 @@ +## Usage + +![cz commit --help](../images/cli_help/cz_commit___help.svg) + +## Overview + ![Using Commitizen cli](../images/demo.gif) -## About +The `commit` command provides an interactive way to create structured commits. Use either: -In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git commit. +- `cz commit` +- `cz c` (shortcut) -You can run `cz commit --write-message-to-file COMMIT_MSG_FILE` to additionally save the -generated message to a file. This can be combined with the `--dry-run` flag to only -write the message to a file and not modify files and create a commit. A possible use -case for this is to [automatically prepare a commit message](../tutorials/auto_prepare_commit_message.md). +By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization.md) for details. +## Basic Usage -!!! note - To maintain platform compatibility, the `commit` command disable ANSI escaping in its output. - In particular pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417). +### Interactive Commit Creation -## Usage +Simply run `cz commit` in your terminal to start the interactive commit creation process. The command will guide you through creating a properly formatted commit message according to your configured rules. -![cz commit --help](../images/cli_help/cz_commit___help.svg) +### Writing Messages to File -### git options +You can save the generated commit message to a file using: +```sh +cz commit --write-message-to-file COMMIT_MSG_FILE +``` -`git` command options that are not implemented by Commitizen can be use via the `--` syntax for the `commit` command. -The syntax separates Commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax: +This can be combined with `--dry-run` to only write the message without creating a commit. This is particularly useful for [automatically preparing commit messages](../tutorials/auto_prepare_commit_message.md). + +## Advanced Features + +### Git Command Options + +You can pass any git commit options using the `--` syntax: ```sh cz commit -- -# e.g., cz commit --dry-run -- -a -S +# Examples: +cz c --dry-run -- -a -S # Stage all changes and sign the commit +cz c -a -- -n # Stage all changes and skip the pre-commit and commit-msg hooks ``` -For example, using the `-S` option on `git commit` to sign a commit is now Commitizen compatible: `cz c -- -S` -!!! note - Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`. - This syntax is now deprecated in favor of the new `cz commit -- -s` syntax. +!!! warning + The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated. ### Retry -You can use `cz commit --retry` to reuse the last commit message when the previous commit attempt failed. -To automatically retry when running `cz commit`, you can set the `retry_after_failure` -configuration option to `true`. Running `cz commit --no-retry` makes Commitizen ignore `retry_after_failure`, forcing -a new commit message to be prompted. +- Use `cz commit --retry` to reuse the last commit message after a failed commit attempt +- Set `retry_after_failure: true` in your configuration to automatically retry +- Use `cz commit --no-retry` to force a new commit message prompt + +### Message Length Control -### Commit message length limit +Control the length of your commit messages using the `-l` or `--message-length-limit` option: +```sh +cz commit -l 72 # Limits message length to 72 characters +``` + +!!! note + The length limit only applies to the first line of the commit message. For conventional commits, this means the limit applies from the type of change through the subject. The body and footer are not counted. -The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages. -An exception would be raised when the message length exceeds the limit. -For example, `cz commit -l 72` will limit the length of commit messages to 72 characters. -By default, the limit is set to 0, which means no limit on the length. +## Technical Notes -**Note that the limit applies only to the first line of the message.** -Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, -while the body and the footer are not counted. +For platform compatibility, the `commit` command disables ANSI escaping in its output. This means pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417). From 13e4aaacbde5bcfc4bd2aea1a1c060508b86dcec Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 14:03:53 +0800 Subject: [PATCH 046/275] docs(contributing): improve documentation for first-time contributors --- docs/contributing.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index e9e162d2df..8389e9370d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,10 +1,14 @@ -## Contributing to commitizen +## Contributing to Commitizen -First, thank you for taking the time to contribute! 🎉 +First, thank you for taking the time to contribute! 🎉 Your contributions help make Commitizen better for everyone. -When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change. +When contributing to Commitizen, we encourage you to: -If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. +1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](faq.md#features-we-wont-add) to see if there's already a discussion about the change you wish to make. +2. If there's no discussion, [create an issue](https://github.com/commitizen-tools/commitizen/issues/new) to discuss your proposed changes. +3. Follow our [development workflow](#development-workflow) and guidelines below. + +If you're a first-time contributor, please check out issues labeled [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - these are specifically chosen to be beginner-friendly. ## Prerequisites & Setup From 47beadc563d8272b64f88ddbf3e9a37351b8f535 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 14:15:29 +0800 Subject: [PATCH 047/275] docs(pull_request_template): add task to fix broken external links in the docs --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5686474709..e37148c0a1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,7 +25,7 @@ Please fill in the following content to let us know better about this change. ### Documentation Changes - [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly - - [ ] Check if there are any broken links in the documentation +- [ ] Check and fix any broken links (internal or external) in the documentation > When running `poetry doc`, any broken internal documentation links will be reported in the console output like this: > From 596038c68a51ad2485ca924d394ee1282eec9472 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 14:59:09 +0800 Subject: [PATCH 048/275] docs(feature_request): eliminate duplicated label --- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 4bd19b9b3a..da3bd5af8a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -26,6 +26,6 @@ body: - type: textarea id: related-issue attributes: - label: Additional context + label: Related issues description: | If applicable, add link to existing issue also help us know better. From a4c87bc0ecb71028d327f82bdba64798518eacc3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 31 May 2025 22:41:41 +0800 Subject: [PATCH 049/275] docs(contributing): add Contributing TL;DR page --- docs/contributing_tldr.md | 28 ++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 29 insertions(+) create mode 100644 docs/contributing_tldr.md diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md new file mode 100644 index 0000000000..278fa520a3 --- /dev/null +++ b/docs/contributing_tldr.md @@ -0,0 +1,28 @@ +Feel free to send a PR to update this file if you find anything useful. 🙇 + +## Environment + +- Python `>=3.9` +- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0` + +## Useful commands + +Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands. + +```bash +# Ensure you have the correct dependencies +poetry install + +# Make ruff happy +poetry format + +# Check if ruff and mypy are happy +poetry lint + +# Check if mypy is happy in python 3.9 +mypy --python-version 3.9 + +# Run tests in parallel. +pytest -n auto # This may take a while. +pytest -n auto +``` diff --git a/mkdocs.yml b/mkdocs.yml index 7b1e0feb30..dec8d3fd5d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -58,6 +58,7 @@ nav: - Exit Codes: "exit_codes.md" - Third-Party Commitizen Templates: "third-party-commitizen.md" - Contributing: "contributing.md" + - Contributing TL;DR: "contributing_tldr.md" - Resources: "external_links.md" markdown_extensions: From 195ed16275eb197fe7b2bd78f7120f59d0e84424 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 14:26:31 +0800 Subject: [PATCH 050/275] docs(contributing-tldr): add documentation changes section --- docs/contributing_tldr.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md index 278fa520a3..f5483cddcd 100644 --- a/docs/contributing_tldr.md +++ b/docs/contributing_tldr.md @@ -9,6 +9,8 @@ Feel free to send a PR to update this file if you find anything useful. 🙇 Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands. +### Code Changes + ```bash # Ensure you have the correct dependencies poetry install @@ -26,3 +28,10 @@ mypy --python-version 3.9 pytest -n auto # This may take a while. pytest -n auto ``` + +### Documentation Changes + +```bash +# Build the documentation locally and check for broken links +poetry doc +``` From c1884bddbeb6afa0af72c9526e18fac16c46e766 Mon Sep 17 00:00:00 2001 From: Anas Hasan Date: Sun, 1 Jun 2025 15:45:02 +0530 Subject: [PATCH 051/275] docs(faq): explain why Pydantic is not used --- docs/faq.md | 14 +++++++++++ poetry.lock | 68 +++++++++++++++++++++++++++++++++++++------------- pyproject.toml | 1 + 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 920f533d73..ee9d97068d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -82,6 +82,20 @@ git revert --no-commit git commit -m "revert: foo bar" ``` +## Why don't we use Pydantic? + +While Pydantic is a powerful and popular library for data validation, we intentionally avoid using it in this project to keep our dependency tree minimal and maintainable. + +Including Pydantic would increase the chances of version conflicts for users - especially with major changes introduced in Pydantic v3. Because we pin dependencies tightly, adding Pydantic could unintentionally restrict what other tools or libraries users can install alongside `commitizen`. + +Moreover we don't rely on the full feature set of Pydantic. Simpler alternatives like Python's built-in `TypedDict` offer sufficient type safety for our use cases, without the runtime overhead or dependency burden. + +In short, avoiding Pydantic helps us: +- Keep dependencies lightweight +- Reduce compatibility issues for users +- Maintain clarity about what contributors should and shouldn't use + + ## I got `Exception [WinError 995] The I/O operation ...` error This error was caused by a Python bug on Windows. It's been fixed by [this PR](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue. diff --git a/poetry.lock b/poetry.lock index cccd7a532a..64ec358bb3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "argcomplete" @@ -31,8 +31,8 @@ files = [ six = ">=1.12.0" [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\""] +test = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\"", "pytest"] [[package]] name = "babel" @@ -302,7 +302,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "decli" @@ -344,7 +344,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] name = "distlib" @@ -402,7 +402,7 @@ files = [ ] [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "filelock" @@ -419,7 +419,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "freezegun" @@ -491,7 +491,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, @@ -501,12 +501,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -781,7 +781,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -971,6 +971,18 @@ files = [ qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["docopt", "pytest"] +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -1032,6 +1044,26 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "poethepoet" +version = "0.34.0" +description = "A task runner that works well with poetry." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e"}, + {file = "poethepoet-0.34.0.tar.gz", hash = "sha256:86203acce555bbfe45cb6ccac61ba8b16a5784264484195874da457ddabf5850"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +pyyaml = ">=6.0.2,<7.0" +tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} + +[package.extras] +poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] + [[package]] name = "pre-commit" version = "4.1.0" @@ -1304,7 +1336,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "documentation", "linters", "test"] +groups = ["main", "dev", "documentation", "linters", "test"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1785,7 +1817,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1809,7 +1841,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchdog" @@ -1948,21 +1980,21 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "e15b424a0569f939e297c8abfcf09753f1fbcc5b4ad891163cc0982accd3b372" +content-hash = "2951e44d1ec238ddef2139c76de3c838976a018b0993f7b84c6fd686e26fc5d0" diff --git a/pyproject.toml b/pyproject.toml index a653ecdb5d..cff51a2094 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,6 +107,7 @@ packages = [{ include = "commitizen" }, { include = "commitizen/py.typed" }] [tool.poetry.group.dev.dependencies] ipython = "^8.0" tox = ">4" +poethepoet = "^0.34.0" [tool.poetry.group.test.dependencies] pytest = ">=7.2,<9.0" From 612ef79eec468c9cbd1b88a36aa45d036989f754 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 16 May 2025 02:03:30 +0800 Subject: [PATCH 052/275] refactor: improve readability and fix typos --- commitizen/bump.py | 18 ++++++++++-------- commitizen/changelog.py | 2 +- commitizen/cli.py | 8 ++------ commitizen/cz/utils.py | 11 +++++------ commitizen/defaults.py | 6 +++--- commitizen/tags.py | 6 +----- tests/test_changelog.py | 2 +- 7 files changed, 23 insertions(+), 30 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 76a8e15893..b8ae18ba64 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -76,7 +76,7 @@ def update_version_in_files( """ # TODO: separate check step and write step updated = [] - for path, regex in files_and_regexs(files, current_version): + for path, regex in _files_and_regexes(files, current_version): current_version_found, version_file = _bump_with_regex( path, current_version, @@ -99,7 +99,7 @@ def update_version_in_files( return updated -def files_and_regexs(patterns: list[str], version: str) -> list[tuple[str, str]]: +def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str]]: """ Resolve all distinct files with their regexp from a list of glob patterns with optional regexp """ @@ -128,13 +128,15 @@ def _bump_with_regex( pattern = re.compile(regex) with open(version_filepath, encoding=encoding) as f: for line in f: - if pattern.search(line): - bumped_line = line.replace(current_version, new_version) - if bumped_line != line: - current_version_found = True - lines.append(bumped_line) - else: + if not pattern.search(line): lines.append(line) + continue + + bumped_line = line.replace(current_version, new_version) + if bumped_line != line: + current_version_found = True + lines.append(bumped_line) + return current_version_found, "".join(lines) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 704efe6071..d8ab56069b 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -190,7 +190,7 @@ def process_commit_message( def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable: if len(set(change_type_order)) != len(change_type_order): raise InvalidConfigurationError( - f"Change types contain duplicates types ({change_type_order})" + f"Change types contain duplicated types ({change_type_order})" ) sorted_tree = [] diff --git a/commitizen/cli.py b/commitizen/cli.py index cb834c5d6f..b566fc596d 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -636,14 +636,10 @@ def main(): extra_args = " ".join(unknown_args[1:]) arguments["extra_cli_args"] = extra_args - if args.config: - conf = config.read_cfg(args.config) - else: - conf = config.read_cfg() - + conf = config.read_cfg(args.config) if args.name: conf.update({"name": args.name}) - elif not args.name and not conf.path: + elif not conf.path: conf.update({"name": "cz_conventional_commits"}) if args.debug: diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index 7bc89673c6..cb79d65d1a 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -5,6 +5,8 @@ from commitizen import git from commitizen.cz import exceptions +_RE_LOCAL_VERSION = re.compile(r"\+.+") + def required_validator(answer, msg=None): if not answer: @@ -17,16 +19,13 @@ def multiple_line_breaker(answer, sep="|"): def strip_local_version(version: str) -> str: - return re.sub(r"\+.+", "", version) + return _RE_LOCAL_VERSION.sub("", version) def get_backup_file_path() -> str: project_root = git.find_git_project_root() - - if project_root is None: - project = "" - else: - project = project_root.as_posix().replace("/", "%") + project = project_root.as_posix().replace("/", "%") if project_root else "" user = os.environ.get("USER", "") + return os.path.join(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup") diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 0b6c28e6a9..1885848618 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -141,7 +141,7 @@ class Settings(TypedDict, total=False): def get_tag_regexes( version_regex: str, ) -> dict[str, str]: - regexs = { + regexes = { "version": version_regex, "major": r"(?P\d+)", "minor": r"(?P\d+)", @@ -150,6 +150,6 @@ def get_tag_regexes( "devrelease": r"(?P\.dev\d+)?", } return { - **{f"${k}": v for k, v in regexs.items()}, - **{f"${{{k}}}": v for k, v in regexs.items()}, + **{f"${k}": v for k, v in regexes.items()}, + **{f"${{{k}}}": v for k, v in regexes.items()}, } diff --git a/commitizen/tags.py b/commitizen/tags.py index 2b9a4b091a..c5f06884fe 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -174,11 +174,7 @@ def include_in_changelog(self, tag: GitTag) -> bool: version = self.extract_version(tag) except InvalidVersion: return False - - if self.merge_prereleases and version.is_prerelease: - return False - - return True + return not (self.merge_prereleases and version.is_prerelease) def search_version(self, text: str, last: bool = False) -> VersionTag | None: """ diff --git a/tests/test_changelog.py b/tests/test_changelog.py index b1c7c802e1..511cd1a3df 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1236,7 +1236,7 @@ def test_order_changelog_tree_raises(): with pytest.raises(InvalidConfigurationError) as excinfo: changelog.order_changelog_tree(COMMITS_TREE, change_type_order) - assert "Change types contain duplicates types" in str(excinfo) + assert "Change types contain duplicated types" in str(excinfo) def test_render_changelog( From 33453b753676161db343c56dc4a9abda31c2d864 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 23:26:54 +0800 Subject: [PATCH 053/275] refactor(version_scheme): cleanup --- commitizen/version_schemes.py | 94 +++++++++++++++++------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 84ded9316e..d5370b2c6c 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -41,7 +41,9 @@ Increment: TypeAlias = Literal["MAJOR", "MINOR", "PATCH"] Prerelease: TypeAlias = Literal["alpha", "beta", "rc"] -DEFAULT_VERSION_PARSER = r"v?(?P([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)" +_DEFAULT_VERSION_PARSER = re.compile( + r"v?(?P([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)" +) @runtime_checkable @@ -156,7 +158,7 @@ class BaseVersion(_BaseVersion): A base class implementing the `VersionProtocol` for PEP440-like versions. """ - parser: ClassVar[re.Pattern] = re.compile(DEFAULT_VERSION_PARSER) + parser: ClassVar[re.Pattern] = _DEFAULT_VERSION_PARSER """Regex capturing this version scheme into a `version` group""" @property @@ -265,39 +267,35 @@ def bump( if self.local and is_local_version: local_version = self.scheme(self.local).bump(increment) return self.scheme(f"{self.public}+{local_version}") # type: ignore - else: - if not self.is_prerelease: - base = self.increment_base(increment) - elif exact_increment: - base = self.increment_base(increment) - else: - base = f"{self.major}.{self.minor}.{self.micro}" - if increment == PATCH: - pass - elif increment == MINOR: - if self.micro != 0: - base = self.increment_base(increment) - elif increment == MAJOR: - if self.minor != 0 or self.micro != 0: - base = self.increment_base(increment) - dev_version = self.generate_devrelease(devrelease) - - release = list(self.release) - if len(release) < 3: - release += [0] * (3 - len(release)) - current_base = ".".join(str(part) for part in release) - if base == current_base: - pre_version = self.generate_prerelease( - prerelease, offset=prerelease_offset - ) - else: - base_version = cast(BaseVersion, self.scheme(base)) - pre_version = base_version.generate_prerelease( - prerelease, offset=prerelease_offset - ) - build_metadata = self.generate_build_metadata(build_metadata) - # TODO: post version - return self.scheme(f"{base}{pre_version}{dev_version}{build_metadata}") # type: ignore + + base = self._get_increment_base(increment, exact_increment) + dev_version = self.generate_devrelease(devrelease) + + release = list(self.release) + if len(release) < 3: + release += [0] * (3 - len(release)) + current_base = ".".join(str(part) for part in release) + + pre_version = ( + self if base == current_base else cast(BaseVersion, self.scheme(base)) + ).generate_prerelease(prerelease, offset=prerelease_offset) + + # TODO: post version + return self.scheme( + f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}" + ) # type: ignore + + def _get_increment_base( + self, increment: Increment | None, exact_increment: bool + ) -> str: + if ( + not self.is_prerelease + or exact_increment + or (increment == MINOR and self.micro != 0) + or (increment == MAJOR and (self.minor != 0 or self.micro != 0)) + ): + return self.increment_base(increment) + return f"{self.major}.{self.minor}.{self.micro}" class Pep440(BaseVersion): @@ -316,7 +314,7 @@ class SemVer(BaseVersion): """ def __str__(self) -> str: - parts = [] + parts: list[str] = [] # Epoch if self.epoch != 0: @@ -364,7 +362,7 @@ def prerelease(self) -> str | None: return None def __str__(self) -> str: - parts = [] + parts: list[str] = [] # Epoch if self.epoch != 0: @@ -373,9 +371,19 @@ def __str__(self) -> str: # Release segment parts.append(".".join(str(x) for x in self.release)) + if prerelease := self._get_prerelease(): + parts.append(f"-{prerelease}") + + # Local version segment + if self.local: + parts.append(f"+{self.local}") + + return "".join(parts) + + def _get_prerelease(self) -> str: # Pre-release identifiers # See: https://semver.org/spec/v2.0.0.html#spec-item-9 - prerelease_parts = [] + prerelease_parts: list[str] = [] if self.prerelease: prerelease_parts.append(f"{self.prerelease}") @@ -387,15 +395,7 @@ def __str__(self) -> str: if self.dev is not None: prerelease_parts.append(f"dev.{self.dev}") - if prerelease_parts: - parts.append("-") - parts.append(".".join(prerelease_parts)) - - # Local version segment - if self.local: - parts.append(f"+{self.local}") - - return "".join(parts) + return ".".join(prerelease_parts) DEFAULT_SCHEME: VersionScheme = Pep440 From 8cfb859ffad0538cb17a1b1bb565df6c47e48ded Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 19:05:40 +0800 Subject: [PATCH 054/275] refactor(commit): simplify call --- commitizen/commands/commit.py | 64 +++++++++++++++-------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index cb34c41a50..eedf77e079 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -92,41 +92,36 @@ def manual_edit(self, message: str) -> str: os.unlink(file.name) return message - def __call__(self): - extra_args: str = self.arguments.get("extra_cli_args", "") + def _get_message(self) -> str: + if self.arguments.get("retry"): + m = self.read_backup_message() + if m is None: + raise NoCommitBackupError() + return m - allow_empty: bool = "--allow-empty" in extra_args + if self.config.settings.get("retry_after_failure") and not self.arguments.get( + "no_retry" + ): + return self.read_backup_message() or self.prompt_commit_questions() + return self.prompt_commit_questions() + def __call__(self): + extra_args: str = self.arguments.get("extra_cli_args", "") dry_run: bool = self.arguments.get("dry_run") write_message_to_file: bool = self.arguments.get("write_message_to_file") - manual_edit: bool = self.arguments.get("edit") + signoff: bool = self.arguments.get("signoff") - is_all: bool = self.arguments.get("all") - if is_all: - c = git.add("-u") + if self.arguments.get("all"): + git.add("-u") - if git.is_staging_clean() and not (dry_run or allow_empty): + if git.is_staging_clean() and not (dry_run or "--allow-empty" in extra_args): raise NothingToCommitError("No files added to staging!") if write_message_to_file is not None and write_message_to_file.is_dir(): raise NotAllowed(f"{write_message_to_file} is a directory") - retry: bool = self.arguments.get("retry") - no_retry: bool = self.arguments.get("no_retry") - retry_after_failure: bool = self.config.settings.get("retry_after_failure") - - if retry: - m = self.read_backup_message() - if m is None: - raise NoCommitBackupError() - elif retry_after_failure and not no_retry: - m = self.read_backup_message() - if m is None: - m = self.prompt_commit_questions() - else: - m = self.prompt_commit_questions() - - if manual_edit: + m = self._get_message() + if self.arguments.get("edit"): m = self.manual_edit(m) out.info(f"\n{m}\n") @@ -138,19 +133,15 @@ def __call__(self): if dry_run: raise DryRunExit() - always_signoff: bool = self.config.settings["always_signoff"] - signoff: bool = self.arguments.get("signoff") - if signoff: out.warn( "signoff mechanic is deprecated, please use `cz commit -- -s` instead." ) - if always_signoff or signoff: + if self.config.settings["always_signoff"] or signoff: extra_args = f"{extra_args} -s".strip() c = git.commit(m, args=extra_args) - if c.return_code != 0: out.error(c.err) @@ -160,11 +151,12 @@ def __call__(self): raise CommitError() - if "nothing added" in c.out or "no changes added to commit" in c.out: + if any(s in c.out for s in ("nothing added", "no changes added to commit")): out.error(c.out) - else: - with contextlib.suppress(FileNotFoundError): - os.remove(self.temp_file) - out.write(c.err) - out.write(c.out) - out.success("Commit successful!") + return + + with contextlib.suppress(FileNotFoundError): + os.remove(self.temp_file) + out.write(c.err) + out.write(c.out) + out.success("Commit successful!") From 471b778e18937cb0291bf266fa1cd2a58a53e081 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 19:43:14 +0800 Subject: [PATCH 055/275] test(commit): when nothing is added to commit --- tests/commands/test_commit_command.py | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 3a92f5af48..930e1a7a9b 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -523,3 +523,34 @@ def test_commit_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +@pytest.mark.usefixtures("staging_is_clean") +@pytest.mark.parametrize( + "out", ["no changes added to commit", "nothing added to commit"] +) +def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command( + out=out, + err="", + stdout=out.encode(), + stderr=b"", + return_code=0, + ) + error_mock = mocker.patch("commitizen.out.error") + + commands.Commit(config, {})() + + commit_mock.assert_called_once() + error_mock.assert_called_once_with(out) From 5b3b6efbbb30980616aa218af35e66587d79a6da Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 16 May 2025 02:18:37 +0800 Subject: [PATCH 056/275] refactor(git): code cleanup and better test coverage --- commitizen/git.py | 133 +++++++++++++++------------------------------- tests/test_git.py | 32 ++++++----- 2 files changed, 61 insertions(+), 104 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index 19ca46b6c3..d0cc901a61 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -1,34 +1,14 @@ from __future__ import annotations import os -from enum import Enum -from os import linesep from pathlib import Path from tempfile import NamedTemporaryFile from commitizen import cmd, out from commitizen.exceptions import GitCommandError -UNIX_EOL = "\n" -WINDOWS_EOL = "\r\n" - - -class EOLTypes(Enum): - """The EOL type from `git config core.eol`.""" - - LF = "lf" - CRLF = "crlf" - NATIVE = "native" - - def get_eol_for_open(self) -> str: - """Get the EOL character for `open()`.""" - map = { - EOLTypes.CRLF: WINDOWS_EOL, - EOLTypes.LF: UNIX_EOL, - EOLTypes.NATIVE: linesep, - } - - return map[self] +_UNIX_EOL = "\n" +_WINDOWS_EOL = "\r\n" class GitObject: @@ -37,9 +17,7 @@ class GitObject: date: str def __eq__(self, other) -> bool: - if not hasattr(other, "rev"): - return False - return self.rev == other.rev # type: ignore + return hasattr(other, "rev") and self.rev == other.rev class GitCommit(GitObject): @@ -63,6 +41,20 @@ def __init__( def message(self): return f"{self.title}\n\n{self.body}".strip() + @classmethod + def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit: + rev, parents, title, author, author_email, *body_list = rev_and_commit.split( + "\n" + ) + return cls( + rev=rev.strip(), + title=title.strip(), + body="\n".join(body_list).strip(), + author=author, + author_email=author_email, + parents=[p for p in parents.strip().split(" ") if p], + ) + def __repr__(self): return f"{self.title} ({self.rev})" @@ -101,13 +93,11 @@ def tag( # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging, # we're not able to create lightweight tag with message. # by adding message, we make it a annotated tags - c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') - return c + return cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') def add(*args: str) -> cmd.Command: - c = cmd.run(f"git add {' '.join(args)}") - return c + return cmd.run(f"git add {' '.join(args)}") def commit( @@ -140,24 +130,10 @@ def get_commits( ) -> list[GitCommit]: """Get the commits between start and end.""" git_log_entries = _get_log_as_str_list(start, end, args) - git_commits = [] - for rev_and_commit in git_log_entries: - if not rev_and_commit: - continue - rev, parents, title, author, author_email, *body_list = rev_and_commit.split( - "\n" - ) - if rev_and_commit: - git_commit = GitCommit( - rev=rev.strip(), - title=title.strip(), - body="\n".join(body_list).strip(), - author=author, - author_email=author_email, - parents=[p for p in parents.strip().split(" ") if p], - ) - git_commits.append(git_commit) - return git_commits + return [ + GitCommit.from_rev_and_commit(rev_and_commit) + for rev_and_commit in filter(None, git_log_entries) + ] def get_filenames_in_commit(git_reference: str = ""): @@ -170,8 +146,7 @@ def get_filenames_in_commit(git_reference: str = ""): c = cmd.run(f"git show --name-only --pretty=format: {git_reference}") if c.return_code == 0: return c.out.strip().split("\n") - else: - raise GitCommandError(c.err) + raise GitCommandError(c.err) def get_tags( @@ -197,16 +172,11 @@ def get_tags( if c.err: out.warn(f"Attempting to proceed after: {c.err}") - if not c.out: - return [] - - git_tags = [ + return [ GitTag.from_line(line=line, inner_delimiter=inner_delimiter) for line in c.out.split("\n")[:-1] ] - return git_tags - def tag_exist(tag: str) -> bool: c = cmd.run(f"git tag --list {tag}") @@ -231,18 +201,18 @@ def get_tag_message(tag: str) -> str | None: return c.out.strip() -def get_tag_names() -> list[str | None]: +def get_tag_names() -> list[str]: c = cmd.run("git tag --list") if c.err: return [] - return [tag.strip() for tag in c.out.split("\n") if tag.strip()] + return list(filter(None, (tag.strip() for tag in c.out.split("\n")))) def find_git_project_root() -> Path | None: c = cmd.run("git rev-parse --show-toplevel") - if not c.err: - return Path(c.out.strip()) - return None + if c.err: + return None + return Path(c.out.strip()) def is_staging_clean() -> bool: @@ -253,32 +223,19 @@ def is_staging_clean() -> bool: def is_git_project() -> bool: c = cmd.run("git rev-parse --is-inside-work-tree") - if c.out.strip() == "true": - return True - return False + return c.out.strip() == "true" -def get_eol_style() -> EOLTypes: +def get_eol_for_open() -> str: + # See: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol c = cmd.run("git config core.eol") eol = c.out.strip().lower() - # We enumerate the EOL types of the response of - # `git config core.eol`, and map it to our enumration EOLTypes. - # - # It is just like the variant of the "match" syntax. - map = { - "lf": EOLTypes.LF, - "crlf": EOLTypes.CRLF, - "native": EOLTypes.NATIVE, - } - - # If the response of `git config core.eol` is in the map: - if eol in map: - return map[eol] - else: - # The default value is "native". - # https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol - return map["native"] + if eol == "lf": + return _UNIX_EOL + if eol == "crlf": + return _WINDOWS_EOL + return os.linesep def get_core_editor() -> str | None: @@ -288,22 +245,18 @@ def get_core_editor() -> str | None: return None -def smart_open(*args, **kargs): +def smart_open(*args, **kwargs): """Open a file with the EOL style determined from Git.""" - return open(*args, newline=get_eol_style().get_eol_for_open(), **kargs) + return open(*args, newline=get_eol_for_open(), **kwargs) def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]: """Get string representation of each log entry""" delimiter = "----------commit-delimiter----------" log_format: str = "%H%n%P%n%s%n%an%n%ae%n%b" - git_log_cmd = ( - f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args}" - ) - if start: - command = f"{git_log_cmd} {start}..{end}" - else: - command = f"{git_log_cmd} {end}" + command_range = f"{start}..{end}" if start else end + command = f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args} {command_range}" + c = cmd.run(command) if c.return_code != 0: raise GitCommandError(c.err) diff --git a/tests/test_git.py b/tests/test_git.py index 8b2fc2b86e..4e4b61ffe2 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -79,8 +79,7 @@ def test_get_reachable_tags_with_commits( monkeypatch.setenv("LANGUAGE", f"{locale}.UTF-8") monkeypatch.setenv("LC_ALL", f"{locale}.UTF-8") with tmp_commitizen_project.as_cwd(): - tags = git.get_tags(reachable_only=True) - assert tags == [] + assert git.get_tags(reachable_only=True) == [] def test_get_tag_names(mocker: MockFixture): @@ -271,7 +270,7 @@ def test_get_commits_with_signature(): def test_get_tag_names_has_correct_arrow_annotation(): arrow_annotation = inspect.getfullargspec(git.get_tag_names).annotations["return"] - assert arrow_annotation == "list[str | None]" + assert arrow_annotation == "list[str]" def test_get_latest_tag_name(tmp_commitizen_project): @@ -317,24 +316,18 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project): assert git.is_staging_clean() is False -def test_git_eol_style(tmp_commitizen_project): +def test_get_eol_for_open(tmp_commitizen_project): with tmp_commitizen_project.as_cwd(): - assert git.get_eol_style() == git.EOLTypes.NATIVE + assert git.get_eol_for_open() == os.linesep cmd.run("git config core.eol lf") - assert git.get_eol_style() == git.EOLTypes.LF + assert git.get_eol_for_open() == "\n" cmd.run("git config core.eol crlf") - assert git.get_eol_style() == git.EOLTypes.CRLF + assert git.get_eol_for_open() == "\r\n" cmd.run("git config core.eol native") - assert git.get_eol_style() == git.EOLTypes.NATIVE - - -def test_eoltypes_get_eol_for_open(): - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.NATIVE) == os.linesep - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.LF) == "\n" - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n" + assert git.get_eol_for_open() == os.linesep def test_get_core_editor(mocker): @@ -401,3 +394,14 @@ def test_commit_with_spaces_in_path(mocker, file_path, expected_cmd): mock_run.assert_called_once_with(expected_cmd) mock_unlink.assert_called_once_with(file_path) + + +def test_get_filenames_in_commit_error(mocker: MockFixture): + """Test that GitCommandError is raised when git command fails.""" + mocker.patch( + "commitizen.cmd.run", + return_value=FakeCommand(out="", err="fatal: bad object HEAD", return_code=1), + ) + with pytest.raises(exceptions.GitCommandError) as excinfo: + git.get_filenames_in_commit() + assert str(excinfo.value) == "fatal: bad object HEAD" From 755a04113f1a32256de86ea73d3301ba5875bd8f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 16:04:08 +0800 Subject: [PATCH 057/275] test(git): add test for from_rev_and_commit --- tests/test_git.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_git.py b/tests/test_git.py index 4e4b61ffe2..cb4ea93375 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -405,3 +405,47 @@ def test_get_filenames_in_commit_error(mocker: MockFixture): with pytest.raises(exceptions.GitCommandError) as excinfo: git.get_filenames_in_commit() assert str(excinfo.value) == "fatal: bad object HEAD" + + +def test_git_commit_from_rev_and_commit(): + # Test data with all fields populated + rev_and_commit = ( + "abc123\n" # rev + "def456 ghi789\n" # parents + "feat: add new feature\n" # title + "John Doe\n" # author + "john@example.com\n" # author_email + "This is a detailed description\n" # body + "of the new feature\n" + "with multiple lines" + ) + + commit = git.GitCommit.from_rev_and_commit(rev_and_commit) + + assert commit.rev == "abc123" + assert commit.title == "feat: add new feature" + assert ( + commit.body + == "This is a detailed description\nof the new feature\nwith multiple lines" + ) + assert commit.author == "John Doe" + assert commit.author_email == "john@example.com" + assert commit.parents == ["def456", "ghi789"] + + # Test with minimal data + minimal_commit = ( + "abc123\n" # rev + "\n" # no parents + "feat: minimal commit\n" # title + "John Doe\n" # author + "john@example.com\n" # author_email + ) + + commit = git.GitCommit.from_rev_and_commit(minimal_commit) + + assert commit.rev == "abc123" + assert commit.title == "feat: minimal commit" + assert commit.body == "" + assert commit.author == "John Doe" + assert commit.author_email == "john@example.com" + assert commit.parents == [] From 1deffcab679bcecc619652d6175d6ef97167ba25 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 16:04:38 +0800 Subject: [PATCH 058/275] docs(git): from_rev_and_commit docstring --- commitizen/git.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/commitizen/git.py b/commitizen/git.py index d0cc901a61..d80545bc44 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -43,6 +43,48 @@ def message(self): @classmethod def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit: + """Create a GitCommit instance from a formatted commit string. + + This method parses a multi-line string containing commit information in the following format: + ``` + + + + + + + + ... + ``` + + Args: + rev_and_commit (str): A string containing commit information with fields separated by newlines. + - rev: The commit hash/revision + - parents: Space-separated list of parent commit hashes + - title: The commit title/message + - author: The commit author's name + - author_email: The commit author's email + - body: Optional multi-line commit body + + Returns: + GitCommit: A new GitCommit instance with the parsed information. + + Example: + >>> commit_str = '''abc123 + ... def456 ghi789 + ... feat: add new feature + ... John Doe + ... john@example.com + ... This is a detailed description + ... of the new feature''' + >>> commit = GitCommit.from_rev_and_commit(commit_str) + >>> commit.rev + 'abc123' + >>> commit.title + 'feat: add new feature' + >>> commit.parents + ['def456', 'ghi789'] + """ rev, parents, title, author, author_email, *body_list = rev_and_commit.split( "\n" ) From 1eb35c80ca731518d6f0194904ac390a5fcad021 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 18 May 2025 19:11:52 +0800 Subject: [PATCH 059/275] refactor(EOLType): add eol enum back and reorganize methods --- commitizen/git.py | 48 ++++++++++++++++++++++++++++++++--------------- tests/test_git.py | 8 ++++---- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index d80545bc44..ef4e0452e3 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -1,14 +1,44 @@ from __future__ import annotations import os +from enum import Enum +from functools import lru_cache from pathlib import Path from tempfile import NamedTemporaryFile from commitizen import cmd, out from commitizen.exceptions import GitCommandError -_UNIX_EOL = "\n" -_WINDOWS_EOL = "\r\n" + +class EOLType(Enum): + """The EOL type from `git config core.eol`.""" + + LF = "lf" + CRLF = "crlf" + NATIVE = "native" + + @classmethod + def for_open(cls) -> str: + c = cmd.run("git config core.eol") + eol = c.out.strip().upper() + return cls._char_for_open()[cls._safe_cast(eol)] + + @classmethod + def _safe_cast(cls, eol: str) -> EOLType: + try: + return cls[eol] + except KeyError: + return cls.NATIVE + + @classmethod + @lru_cache + def _char_for_open(cls) -> dict[EOLType, str]: + """Get the EOL character for `open()`.""" + return { + cls.LF: "\n", + cls.CRLF: "\r\n", + cls.NATIVE: os.linesep, + } class GitObject: @@ -268,18 +298,6 @@ def is_git_project() -> bool: return c.out.strip() == "true" -def get_eol_for_open() -> str: - # See: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol - c = cmd.run("git config core.eol") - eol = c.out.strip().lower() - - if eol == "lf": - return _UNIX_EOL - if eol == "crlf": - return _WINDOWS_EOL - return os.linesep - - def get_core_editor() -> str | None: c = cmd.run("git var GIT_EDITOR") if c.out: @@ -289,7 +307,7 @@ def get_core_editor() -> str | None: def smart_open(*args, **kwargs): """Open a file with the EOL style determined from Git.""" - return open(*args, newline=get_eol_for_open(), **kwargs) + return open(*args, newline=EOLType.for_open(), **kwargs) def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]: diff --git a/tests/test_git.py b/tests/test_git.py index cb4ea93375..de3130412b 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -318,16 +318,16 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project): def test_get_eol_for_open(tmp_commitizen_project): with tmp_commitizen_project.as_cwd(): - assert git.get_eol_for_open() == os.linesep + assert git.EOLType.for_open() == os.linesep cmd.run("git config core.eol lf") - assert git.get_eol_for_open() == "\n" + assert git.EOLType.for_open() == "\n" cmd.run("git config core.eol crlf") - assert git.get_eol_for_open() == "\r\n" + assert git.EOLType.for_open() == "\r\n" cmd.run("git config core.eol native") - assert git.get_eol_for_open() == os.linesep + assert git.EOLType.for_open() == os.linesep def test_get_core_editor(mocker): From f67b29b0970bc72d36e27c6d9a50dc68d21759fd Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 18 May 2025 19:26:22 +0800 Subject: [PATCH 060/275] refactor(git): refactor get_tag_names --- commitizen/git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/git.py b/commitizen/git.py index ef4e0452e3..fa59e34d48 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -277,7 +277,7 @@ def get_tag_names() -> list[str]: c = cmd.run("git tag --list") if c.err: return [] - return list(filter(None, (tag.strip() for tag in c.out.split("\n")))) + return [tag for raw in c.out.split("\n") if (tag := raw.strip())] def find_git_project_root() -> Path | None: From febd0d6296430e7c49f13e4522092741a102cbed Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 20 May 2025 00:46:16 +0800 Subject: [PATCH 061/275] refactor(changelog): minor cleanup --- commitizen/commands/changelog.py | 45 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 0e4efabfa1..9ab8fdc37c 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -2,10 +2,10 @@ import os import os.path +from collections.abc import Generator from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path -from typing import Callable, cast from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format @@ -32,9 +32,10 @@ def __init__(self, config: BaseConfig, args): if not git.is_git_project(): raise NotAGitProjectError() - self.config: BaseConfig = config - changelog_file_name = args.get("file_name") or cast( - str, self.config.settings.get("changelog_file") + self.config = config + + changelog_file_name = args.get("file_name") or self.config.settings.get( + "changelog_file" ) if not isinstance(changelog_file_name, str): raise NotAllowed( @@ -114,28 +115,28 @@ def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str: on our experience. """ SIMILARITY_THRESHOLD = 0.89 - tag_ratio = map( - lambda tag: ( - SequenceMatcher( + scores_and_tag_names: Generator[tuple[float, str]] = ( + ( + score, + tag.name, + ) + for tag in tags + if ( + score := SequenceMatcher( None, latest_version, strip_local_version(tag.name) - ).ratio(), - tag, - ), - tags, + ).ratio() + ) + >= SIMILARITY_THRESHOLD ) try: - score, tag = max(tag_ratio, key=itemgetter(0)) + _, start_rev = max(scores_and_tag_names, key=itemgetter(0)) except ValueError: raise NoRevisionError() - if score < SIMILARITY_THRESHOLD: - raise NoRevisionError() - start_rev = tag.name return start_rev def write_changelog( self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata ): - changelog_hook: Callable | None = self.cz.changelog_hook with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file: partial_changelog: str | None = None if self.incremental: @@ -145,8 +146,8 @@ def write_changelog( changelog_out = "".join(new_lines) partial_changelog = changelog_out - if changelog_hook: - changelog_out = changelog_hook(changelog_out, partial_changelog) + if self.cz.changelog_hook: + changelog_out = self.cz.changelog_hook(changelog_out, partial_changelog) changelog_file.write(changelog_out) @@ -221,14 +222,12 @@ def __call__(self): extras.update(self.extras) changelog_out = changelog.render_changelog( tree, loader=self.cz.template_loader, template=self.template, **extras - ) - changelog_out = changelog_out.lstrip("\n") + ).lstrip("\n") # Dry_run is executed here to avoid checking and reading the files if self.dry_run: - changelog_hook: Callable | None = self.cz.changelog_hook - if changelog_hook: - changelog_out = changelog_hook(changelog_out, "") + if self.cz.changelog_hook: + changelog_out = self.cz.changelog_hook(changelog_out, "") out.write(changelog_out) raise DryRunExit() From f3fa22cefe0ac681e2c6e1f80ad119d1df135903 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 20 May 2025 21:23:30 +0800 Subject: [PATCH 062/275] refactor(BaseConfig): use setter --- commitizen/config/base_config.py | 12 +++++++++--- commitizen/config/json_config.py | 2 +- commitizen/config/toml_config.py | 2 +- commitizen/config/yaml_config.py | 2 +- tests/commands/test_init_command.py | 2 +- tests/test_changelog.py | 4 +++- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 478691aa14..fd034412fe 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -19,6 +19,15 @@ def settings(self) -> Settings: def path(self) -> Path | None: return self._path + @path.setter + def path(self, path: str | Path) -> None: + """ + mypy does not like this until 1.16 + See https://github.com/python/mypy/pull/18510 + TODO: remove "type: ignore" from the call sites when 1.16 is available + """ + self._path = Path(path) + def set_key(self, key, value): """Set or update a key in the conf. @@ -30,8 +39,5 @@ def set_key(self, key, value): def update(self, data: Settings) -> None: self._settings.update(data) - def add_path(self, path: str | Path) -> None: - self._path = Path(path) - def _parse_setting(self, data: bytes | str) -> None: raise NotImplementedError() diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index b6a07f4ced..d413d73383 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -13,7 +13,7 @@ class JsonConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.add_path(path) + self.path = path # type: ignore self._parse_setting(data) def init_empty_config_content(self): diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 813389cbcf..e2cfcc9340 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -14,7 +14,7 @@ class TomlConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.add_path(path) + self.path = path # type: ignore self._parse_setting(data) def init_empty_config_content(self): diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index 2bb6fe3af8..c5721c8d4b 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -14,7 +14,7 @@ class YAMLConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.add_path(path) + self.path = path # type: ignore self._parse_setting(data) def init_empty_config_content(self): diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index f617c51d8f..3f12d0bd7f 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -86,7 +86,7 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) def test_init_when_config_already_exists(config, capsys): # Set config path path = os.sep.join(["tests", "pyproject.toml"]) - config.add_path(path) + config.path = path commands.Init(config)() captured = capsys.readouterr() diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 511cd1a3df..d42176f09a 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1639,7 +1639,9 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture): def test_changelog_file_name_from_args_and_config(): mock_config = Mock(spec=BaseConfig) - mock_config.path.parent = "/my/project" + mock_path = Mock(spec=Path) + mock_path.parent = Path("/my/project") + mock_config.path = mock_path mock_config.settings = { "name": "cz_conventional_commits", "changelog_file": "CHANGELOG.md", From c939df1f46e8aa9c28e1ddff60d523ff540cfab5 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 20 May 2025 23:34:37 +0800 Subject: [PATCH 063/275] build(poetry): upgrade mypy version to ^1.15.0 --- poetry.lock | 1086 +++++++++++++++++++++++------------------------- pyproject.toml | 14 +- 2 files changed, 513 insertions(+), 587 deletions(-) diff --git a/poetry.lock b/poetry.lock index 64ec358bb3..ceccd3da1c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,60 +17,76 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\""] -test = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\"", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "backrefs" +version = "5.8" +description = "A wrapper around re and regex that adds additional back references." +optional = false +python-versions = ">=3.9" +groups = ["documentation"] +files = [ + {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, + {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, + {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"}, + {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"}, + {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"}, + {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"}, ] [package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +extras = ["regex"] [[package]] name = "cachetools" -version = "5.5.1" +version = "6.0.0" description = "Extensible memoizing collections and decorators" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, - {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, + {file = "cachetools-6.0.0-py3-none-any.whl", hash = "sha256:82e73ba88f7b30228b5507dce1a1f878498fc669d972aef2dde4f3a3c24f103e"}, + {file = "cachetools-6.0.0.tar.gz", hash = "sha256:f225782b84438f828328fc2ad74346522f27e5b1440f4e9fd18b20ebfd1aa2cf"}, ] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.4.26" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" groups = ["documentation"] files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, + {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, ] [[package]] @@ -99,116 +115,116 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["documentation"] files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -228,74 +244,79 @@ files = [ [[package]] name = "coverage" -version = "7.6.8" +version = "7.8.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, - {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, - {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, - {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, - {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, - {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, - {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, - {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, - {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, - {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, - {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, - {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, - {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, - {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, - {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, - {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, + {file = "coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a"}, + {file = "coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be"}, + {file = "coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3"}, + {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6"}, + {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622"}, + {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c"}, + {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3"}, + {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404"}, + {file = "coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7"}, + {file = "coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347"}, + {file = "coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9"}, + {file = "coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879"}, + {file = "coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a"}, + {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5"}, + {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11"}, + {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a"}, + {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb"}, + {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54"}, + {file = "coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a"}, + {file = "coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975"}, + {file = "coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53"}, + {file = "coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c"}, + {file = "coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1"}, + {file = "coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279"}, + {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99"}, + {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20"}, + {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2"}, + {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57"}, + {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f"}, + {file = "coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8"}, + {file = "coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223"}, + {file = "coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f"}, + {file = "coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca"}, + {file = "coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d"}, + {file = "coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85"}, + {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257"}, + {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108"}, + {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0"}, + {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050"}, + {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48"}, + {file = "coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7"}, + {file = "coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3"}, + {file = "coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7"}, + {file = "coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008"}, + {file = "coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36"}, + {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46"}, + {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be"}, + {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740"}, + {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625"}, + {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b"}, + {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199"}, + {file = "coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8"}, + {file = "coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d"}, + {file = "coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b"}, + {file = "coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a"}, + {file = "coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d"}, + {file = "coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca"}, + {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d"}, + {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787"}, + {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7"}, + {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3"}, + {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7"}, + {file = "coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a"}, + {file = "coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e"}, + {file = "coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837"}, + {file = "coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32"}, + {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"}, ] [package.dependencies] @@ -306,26 +327,26 @@ toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "decli" -version = "0.6.2" +version = "0.6.3" description = "Minimal, easy-to-use, declarative cli tool" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed"}, - {file = "decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f"}, + {file = "decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3"}, + {file = "decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656"}, ] [[package]] name = "decorator" -version = "5.1.1" +version = "5.2.1" description = "Decorators for Humans" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] [[package]] @@ -360,17 +381,20 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev", "test"] markers = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] @@ -391,14 +415,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "executing" -version = "2.1.0" +version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, - {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, + {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, + {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] [package.extras] @@ -406,31 +430,31 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.16.1" +version = "3.18.0" description = "A platform independent file lock." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "linters"] files = [ - {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, - {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, + {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, + {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "freezegun" -version = "1.5.1" +version = "1.5.2" description = "Let your Python tests travel through time" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["test"] files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.5.2-py3-none-any.whl", hash = "sha256:5aaf3ba229cda57afab5bd311f0108d86b6fb119ae89d2cd9c43ec8c1733c85b"}, + {file = "freezegun-1.5.2.tar.gz", hash = "sha256:a54ae1d2f9c02dbf42e02c18a3ab95ab4295818b549a34dac55592d72a905181"}, ] [package.dependencies] @@ -456,14 +480,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.3" +version = "2.6.12" description = "File identification library for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, - {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, + {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, + {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, ] [package.extras] @@ -486,15 +510,15 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.6.1" +version = "8.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] markers = "python_version == \"3.9\"" files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] [package.dependencies] @@ -511,14 +535,14 @@ type = ["pytest-mypy"] [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["test"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] @@ -581,14 +605,14 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, - {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -599,21 +623,21 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" -version = "3.7" +version = "3.8" description = "Python implementation of John Gruber's Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, + {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"}, + {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"}, ] [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] @@ -803,27 +827,27 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.50" +version = "9.6.14" description = "Documentation that simply works" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"}, - {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"}, + {file = "mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b"}, + {file = "mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754"}, ] [package.dependencies] babel = ">=2.10,<3.0" +backrefs = ">=5.7.post1,<6.0" colorama = ">=0.4,<1.0" -jinja2 = ">=3.0,<4.0" +jinja2 = ">=3.1,<4.0" markdown = ">=3.2,<4.0" mkdocs = ">=1.6,<2.0" mkdocs-material-extensions = ">=1.3,<2.0" paginate = ">=0.5,<1.0" pygments = ">=2.16,<3.0" pymdown-extensions = ">=10.2,<11.0" -regex = ">=2022.4" requests = ">=2.26,<3.0" [package.extras] @@ -845,54 +869,49 @@ files = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.16.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"}, + {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"}, + {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"}, + {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"}, + {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"}, + {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"}, + {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"}, + {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"}, + {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"}, + {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"}, + {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"}, + {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"}, + {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"}, + {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"}, + {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"}, + {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"}, + {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"}, + {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"}, + {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"}, + {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"}, + {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"}, + {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"}, + {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"}, + {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"}, + {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"}, + {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"}, + {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"}, + {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"}, + {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"}, + {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"}, + {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"}, + {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"}, ] [package.dependencies] mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" @@ -905,14 +924,14 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" groups = ["linters"] files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] @@ -929,14 +948,14 @@ files = [ [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev", "documentation", "test"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -989,7 +1008,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["documentation"] +groups = ["documentation", "linters"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1013,36 +1032,36 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "documentation", "linters"] files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "test"] files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "poethepoet" @@ -1066,14 +1085,14 @@ poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] [[package]] name = "pre-commit" -version = "4.1.0" +version = "4.2.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, - {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, + {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, + {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, ] [package.dependencies] @@ -1085,14 +1104,14 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.48" +version = "3.0.51" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, + {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, + {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, ] [package.dependencies] @@ -1128,14 +1147,14 @@ tests = ["pytest"] [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev", "documentation", "script"] +groups = ["dev", "documentation", "script", "test"] files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -1143,14 +1162,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.12" +version = "10.15" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "pymdown_extensions-10.12-py3-none-any.whl", hash = "sha256:49f81412242d3527b8b4967b990df395c89563043bc51a3d2d7d500e52123b77"}, - {file = "pymdown_extensions-10.12.tar.gz", hash = "sha256:b0ee1e0b2bef1071a47891ab17003bfe5bf824a398e13f49f8ed653b699369a7"}, + {file = "pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f"}, + {file = "pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7"}, ] [package.dependencies] @@ -1158,61 +1177,62 @@ markdown = ">=3.6" pyyaml = "*" [package.extras] -extra = ["pygments (>=2.12)"] +extra = ["pygments (>=2.19.1)"] [[package]] name = "pyproject-api" -version = "1.9.0" +version = "1.9.1" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pyproject_api-1.9.0-py3-none-any.whl", hash = "sha256:326df9d68dea22d9d98b5243c46e3ca3161b07a1b9b18e213d1e24fd0e605766"}, - {file = "pyproject_api-1.9.0.tar.gz", hash = "sha256:7e8a9854b2dfb49454fae421cb86af43efbb2b2454e5646ffb7623540321ae6e"}, + {file = "pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948"}, + {file = "pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335"}, ] [package.dependencies] -packaging = ">=24.2" +packaging = ">=25" tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "setuptools (>=75.8)"] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3.2)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "pytest-mock (>=3.14)", "setuptools (>=80.3.1)"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.4.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"}, + {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "6.0.0" +version = "6.1.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, - {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, + {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, + {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, ] [package.dependencies] @@ -1224,18 +1244,22 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-datadir" -version = "1.5.0" +version = "1.7.2" description = "pytest plugin for test data directories and files" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"}, - {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"}, + {file = "pytest_datadir-1.7.2-py3-none-any.whl", hash = "sha256:8392ba0e9eaf37030e663dcd91cc5123dec99c44300f0c5eac44f35f13f0e086"}, + {file = "pytest_datadir-1.7.2.tar.gz", hash = "sha256:15f5228a35d0a3205e4968e75d3b9cca91762424e1eafc21eb637d380a48443e"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=7.0" + +[package.extras] +dev = ["pre-commit", "pytest-datadir[testing]"] +testing = ["pytest", "tox"] [[package]] name = "pytest-freezer" @@ -1255,14 +1279,14 @@ pytest = ">=3.6" [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.14.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, + {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, ] [package.dependencies] @@ -1273,19 +1297,19 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-regressions" -version = "2.7.0" +version = "2.8.0" description = "Easy to use fixtures to write regression tests." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_regressions-2.7.0-py3-none-any.whl", hash = "sha256:69f5e3f03493cf0ef84d96d23e50a546617c198b1d7746f2e2b9e441cbab4847"}, - {file = "pytest_regressions-2.7.0.tar.gz", hash = "sha256:4c30064e0923929012c94f5d6f35205be06fd8709c7f0dba0228e05c460af05e"}, + {file = "pytest_regressions-2.8.0-py3-none-any.whl", hash = "sha256:2926f37efa5fd6793806b10352e274c5284a5469a845aeab6243e86f9214766f"}, + {file = "pytest_regressions-2.8.0.tar.gz", hash = "sha256:775044e17117f5427df2caad3ab1c66889abe770a0ce2bc3f24fdeac99af76fe"}, ] [package.dependencies] pytest = ">=6.2.0" -pytest-datadir = ">=1.2.0" +pytest-datadir = ">=1.7.0" pyyaml = "*" [package.extras] @@ -1296,14 +1320,14 @@ num = ["numpy", "pandas"] [[package]] name = "pytest-xdist" -version = "3.6.1" +version = "3.7.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0"}, + {file = "pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126"}, ] [package.dependencies] @@ -1395,14 +1419,14 @@ files = [ [[package]] name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, ] [package.dependencies] @@ -1423,110 +1447,6 @@ files = [ [package.dependencies] prompt_toolkit = ">=2.0,<4.0" -[[package]] -name = "regex" -version = "2024.11.6" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.8" -groups = ["documentation"] -files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, -] - [[package]] name = "requests" version = "2.32.3" @@ -1571,42 +1491,42 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.9.4" +version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["linters"] files = [ - {file = "ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706"}, - {file = "ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf"}, - {file = "ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0"}, - {file = "ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402"}, - {file = "ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e"}, - {file = "ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41"}, - {file = "ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7"}, + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, ] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["dev", "documentation", "test"] +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["documentation", "test"] files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -1689,30 +1609,30 @@ markers = {dev = "python_version < \"3.11\"", linters = "python_version < \"3.11 [[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] [[package]] name = "tox" -version = "4.24.1" +version = "4.26.0" description = "tox is a generic virtualenv management and test command line tool" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tox-4.24.1-py3-none-any.whl", hash = "sha256:57ba7df7d199002c6df8c2db9e6484f3de6ca8f42013c083ea2d4d1e5c6bdc75"}, - {file = "tox-4.24.1.tar.gz", hash = "sha256:083a720adbc6166fff0b7d1df9d154f9d00bfccb9403b8abf6bc0ee435d6a62e"}, + {file = "tox-4.26.0-py3-none-any.whl", hash = "sha256:75f17aaf09face9b97bd41645028d9f722301e912be8b4c65a3f938024560224"}, + {file = "tox-4.26.0.tar.gz", hash = "sha256:a83b3b67b0159fa58e44e646505079e35a43317a62d2ae94725e0586266faeca"}, ] [package.dependencies] -cachetools = ">=5.5" +cachetools = ">=5.5.1" chardet = ">=5.2" colorama = ">=0.4.6" filelock = ">=3.16.1" @@ -1720,12 +1640,12 @@ packaging = ">=24.2" platformdirs = ">=4.3.6" pluggy = ">=1.5" pyproject-api = ">=1.8" -tomli = {version = ">=2.1", markers = "python_version < \"3.11\""} +tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} -virtualenv = ">=20.27.1" +virtualenv = ">=20.31" [package.extras] -test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"] +test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"] [[package]] name = "traitlets" @@ -1745,38 +1665,38 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "types-deprecated" -version = "1.2.15.20241117" +version = "1.2.15.20250304" description = "Typing stubs for Deprecated" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types-Deprecated-1.2.15.20241117.tar.gz", hash = "sha256:924002c8b7fddec51ba4949788a702411a2e3636cd9b2a33abd8ee119701d77e"}, - {file = "types_Deprecated-1.2.15.20241117-py3-none-any.whl", hash = "sha256:a0cc5e39f769fc54089fd8e005416b55d74aa03f6964d2ed1a0b0b2e28751884"}, + {file = "types_deprecated-1.2.15.20250304-py3-none-any.whl", hash = "sha256:86a65aa550ea8acf49f27e226b8953288cd851de887970fbbdf2239c116c3107"}, + {file = "types_deprecated-1.2.15.20250304.tar.gz", hash = "sha256:c329030553029de5cc6cb30f269c11f4e00e598c4241290179f63cda7d33f719"}, ] [[package]] name = "types-python-dateutil" -version = "2.9.0.20241206" +version = "2.9.0.20250516" description = "Typing stubs for python-dateutil" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, - {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, + {file = "types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93"}, + {file = "types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20241230" +version = "6.0.12.20250516" description = "Typing stubs for PyYAML" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, - {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, + {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"}, + {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"}, ] [[package]] @@ -1793,27 +1713,27 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.14.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" -groups = ["main", "dev", "linters", "script"] +python-versions = ">=3.9" +groups = ["main", "dev", "linters", "script", "test"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, + {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] -markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" -version = "2.2.3" +version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, + {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] @@ -1824,14 +1744,14 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.27.1" +version = "20.31.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev", "linters"] files = [ - {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, - {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, + {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, + {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, ] [package.dependencies] @@ -1841,7 +1761,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchdog" @@ -1900,90 +1820,104 @@ files = [ [[package]] name = "wrapt" -version = "1.17.0" +version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"}, + {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"}, + {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, + {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, + {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, + {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, + {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, + {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, + {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, + {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, + {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"}, + {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"}, + {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"}, + {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"}, + {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"}, + {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, + {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, ] [[package]] name = "zipp" -version = "3.21.0" +version = "3.22.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] markers = "python_version == \"3.9\"" files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, + {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"}, + {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"}, ] [package.extras] @@ -1991,10 +1925,10 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \" cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib_resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "2951e44d1ec238ddef2139c76de3c838976a018b0993f7b84c6fd686e26fc5d0" +content-hash = "7375bf9072066831425c1b72fec016c42791695338c061324fb47420dafd2c8d" diff --git a/pyproject.toml b/pyproject.toml index cff51a2094..8215595737 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,7 +121,7 @@ deprecated = "^1.2.13" [tool.poetry.group.linters.dependencies] ruff = ">=0.5.0,<0.10.0" pre-commit = ">=2.18,<5.0" -mypy = "^1.4" +mypy = "^1.15.0" types-deprecated = "^1.2.9.2" types-python-dateutil = "^2.8.19.13" types-PyYAML = ">=5.4.3,<7.0.0" @@ -251,12 +251,7 @@ cover.help = "Run the test suite with coverage" cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen" all.help = "Run all tasks" -all.sequence = [ - "format", - "lint", - "cover", - "check-commit", -] +all.sequence = ["format", "lint", "cover", "check-commit"] "doc:screenshots".help = "Render documentation screenshots" "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots" @@ -268,10 +263,7 @@ doc.help = "Live documentation server" doc.cmd = "mkdocs serve" ci.help = "Run all tasks in CI" -ci.sequence = [ - { cmd = "pre-commit run --all-files" }, - "cover", -] +ci.sequence = [{ cmd = "pre-commit run --all-files" }, "cover"] ci.env = { SKIP = "no-commit-to-branch" } setup-pre-commit.help = "Install pre-commit hooks" From 18705031c303d3264e7402fb71c31170a7db23bb Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 22:08:12 +0800 Subject: [PATCH 064/275] refactor(bump): add type for out, replace function with re escape --- commitizen/bump.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index b8ae18ba64..2fa814f7c8 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -103,13 +103,13 @@ def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str """ Resolve all distinct files with their regexp from a list of glob patterns with optional regexp """ - out = [] + out: list[tuple[str, str]] = [] for pattern in patterns: drive, tail = os.path.splitdrive(pattern) path, _, regex = tail.partition(":") filepath = drive + path if not regex: - regex = _version_to_regex(version) + regex = re.escape(version) for path in iglob(filepath): out.append((path, regex)) @@ -140,10 +140,6 @@ def _bump_with_regex( return current_version_found, "".join(lines) -def _version_to_regex(version: str) -> str: - return version.replace(".", r"\.").replace("+", r"\+") - - def create_commit_message( current_version: Version | str, new_version: Version | str, From 69a220c362c8a0dab364299a053dcb111c57157e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 03:09:58 +0800 Subject: [PATCH 065/275] refactor(bump): clean up --- commitizen/commands/bump.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 0a2bbe37fc..d022599c76 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -105,19 +105,18 @@ def is_initial_tag( self, current_tag: git.GitTag | None, is_yes: bool = False ) -> bool: """Check if reading the whole git tree up to HEAD is needed.""" - is_initial = False - if not current_tag: - if is_yes: - is_initial = True - else: - out.info("No tag matching configuration could not be found.") - out.info( - "Possible causes:\n" - "- version in configuration is not the current version\n" - "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n" - ) - is_initial = questionary.confirm("Is this the first tag created?").ask() - return is_initial + if current_tag: + return False + if is_yes: + return True + + out.info("No tag matching configuration could not be found.") + out.info( + "Possible causes:\n" + "- version in configuration is not the current version\n" + "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n" + ) + return bool(questionary.confirm("Is this the first tag created?").ask()) def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: # Update the bump map to ensure major version doesn't increment. @@ -134,10 +133,7 @@ def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: raise NoPatternMapError( f"'{self.config.settings['name']}' rule does not support bump" ) - increment = bump.find_increment( - commits, regex=bump_pattern, increments_map=bump_map - ) - return increment + return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map) def __call__(self) -> None: # noqa: C901 """Steps executed to bump.""" @@ -148,7 +144,7 @@ def __call__(self) -> None: # noqa: C901 except TypeError: raise NoVersionSpecifiedError() - bump_commit_message: str = self.bump_settings["bump_message"] + bump_commit_message: str | None = self.bump_settings["bump_message"] version_files: list[str] = self.bump_settings["version_files"] major_version_zero: bool = self.bump_settings["major_version_zero"] prerelease_offset: int = self.bump_settings["prerelease_offset"] From b0f0d40ece88ca14c05b16d8fe760269096d7ca4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 03:14:56 +0800 Subject: [PATCH 066/275] test(bump): improve test coverage --- commitizen/commands/bump.py | 4 +-- tests/commands/test_bump_command.py | 55 +++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index d022599c76..a2d98c2451 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -65,7 +65,7 @@ def __init__(self, config: BaseConfig, arguments: dict): "template", "file_name", ] - if arguments[key] is not None + if arguments.get(key) is not None }, } self.cz = factory.committer_factory(self.config) @@ -110,7 +110,7 @@ def is_initial_tag( if is_yes: return True - out.info("No tag matching configuration could not be found.") + out.info("No tag matching configuration could be found.") out.info( "Possible causes:\n" "- version in configuration is not the current version\n" diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index e15539d8a7..2af7bec121 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -12,8 +12,9 @@ from pytest_mock import MockFixture import commitizen.commands.bump as bump -from commitizen import cli, cmd, git, hooks +from commitizen import cli, cmd, defaults, git, hooks from commitizen.changelog_formats import ChangelogFormat +from commitizen.config.base_config import BaseConfig from commitizen.cz.base import BaseCommitizen from commitizen.exceptions import ( BumpTagFailedError, @@ -41,8 +42,8 @@ "fix(user): username exception", "refactor: remove ini configuration support", "refactor(config): remove ini configuration support", - "perf: update to use multiproess", - "perf(worker): update to use multiproess", + "perf: update to use multiprocess", + "perf(worker): update to use multiprocess", ), ) @pytest.mark.usefixtures("tmp_commitizen_project") @@ -1688,3 +1689,51 @@ def test_bump_warn_but_dont_fail_on_invalid_tags( assert err.count("Invalid version tag: '0.4.3.deadbeaf'") == 1 assert git.tag_exist("0.4.3") + + +def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): + """Test the is_initial_tag method behavior.""" + # Create a commit but no tags + create_file_and_commit("feat: initial commit") + + # Initialize Bump with minimal config + config = BaseConfig() + config.settings.update( + { + "name": defaults.DEFAULT_SETTINGS["name"], + "encoding": "utf-8", + "pre_bump_hooks": [], + "post_bump_hooks": [], + } + ) + + # Initialize with required arguments + arguments = { + "changelog": False, + "changelog_to_stdout": False, + "git_output_to_stderr": False, + "no_verify": False, + "check_consistency": False, + "retry": False, + "version_scheme": None, + "file_name": None, + "template": None, + "extras": None, + } + + bump_cmd = bump.Bump(config, arguments) + + # Test case 1: No current tag, not yes mode + mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True)) + assert bump_cmd.is_initial_tag(None, is_yes=False) is True + + # Test case 2: No current tag, yes mode + assert bump_cmd.is_initial_tag(None, is_yes=True) is True + + # Test case 3: Has current tag + mock_tag = mocker.Mock() + assert bump_cmd.is_initial_tag(mock_tag, is_yes=False) is False + + # Test case 4: No current tag, user denies + mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False)) + assert bump_cmd.is_initial_tag(None, is_yes=False) is False From 74d3bc53d10cde3de9f56fec429416ff47fbbd18 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 22 May 2025 22:55:50 +0800 Subject: [PATCH 067/275] fix(defaults): add non-capitalized default constants back and deprecated warning relates #1446 --- commitizen/defaults.py | 29 +++++++++++++++++++++++++++++ tests/test_defaults.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/test_defaults.py diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 1885848618..10d55b8621 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,6 +1,7 @@ from __future__ import annotations import pathlib +import warnings from collections import OrderedDict from collections.abc import Iterable, MutableMapping, Sequence from typing import Any, TypedDict @@ -153,3 +154,31 @@ def get_tag_regexes( **{f"${k}": v for k, v in regexes.items()}, **{f"${{{k}}}": v for k, v in regexes.items()}, } + + +def __getattr__(name: str) -> Any: + # PEP-562: deprecate module-level variable + + # {"deprecated key": (value, "new key")} + deprecated_vars = { + "bump_pattern": (BUMP_PATTERN, "BUMP_PATTERN"), + "bump_map": (BUMP_MAP, "BUMP_MAP"), + "bump_map_major_version_zero": ( + BUMP_MAP_MAJOR_VERSION_ZERO, + "BUMP_MAP_MAJOR_VERSION_ZERO", + ), + "bump_message": (BUMP_MESSAGE, "BUMP_MESSAGE"), + "change_type_order": (CHANGE_TYPE_ORDER, "CHANGE_TYPE_ORDER"), + "encoding": (ENCODING, "ENCODING"), + "name": (DEFAULT_SETTINGS["name"], "DEFAULT_SETTINGS['name']"), + } + if name in deprecated_vars: + value, replacement = deprecated_vars[name] + warnings.warn( + f"{name} is deprecated and will be removed in a future version. " + f"Use {replacement} instead.", + DeprecationWarning, + stacklevel=2, + ) + return value + raise AttributeError(f"{name} is not an attribute of {__name__}") diff --git a/tests/test_defaults.py b/tests/test_defaults.py new file mode 100644 index 0000000000..2298068662 --- /dev/null +++ b/tests/test_defaults.py @@ -0,0 +1,31 @@ +import pytest + +from commitizen import defaults + + +def test_getattr_deprecated_vars(): + # Test each deprecated variable + with pytest.warns(DeprecationWarning) as record: + assert defaults.bump_pattern == defaults.BUMP_PATTERN + assert defaults.bump_map == defaults.BUMP_MAP + assert ( + defaults.bump_map_major_version_zero == defaults.BUMP_MAP_MAJOR_VERSION_ZERO + ) + assert defaults.bump_message == defaults.BUMP_MESSAGE + assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER + assert defaults.encoding == defaults.ENCODING + assert defaults.name == defaults.DEFAULT_SETTINGS["name"] + + # Verify warning messages + assert len(record) == 7 + for warning in record: + assert "is deprecated and will be removed in a future version" in str( + warning.message + ) + + +def test_getattr_non_existent(): + # Test non-existent attribute + with pytest.raises(AttributeError) as exc_info: + _ = defaults.non_existent_attribute + assert "is not an attribute of" in str(exc_info.value) From 815f514b00300e628fad49e0400da760ab517bfb Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 17 May 2025 20:42:34 +0800 Subject: [PATCH 068/275] refactor: misc cleanup --- .../conventional_commits.py | 21 +++++++------------ commitizen/cz/jira/jira.py | 6 ++---- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index af29a209fc..9a2b9016b7 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -41,7 +41,7 @@ class ConventionalCommitsCz(BaseCommitizen): changelog_pattern = defaults.BUMP_PATTERN def questions(self) -> Questions: - questions: Questions = [ + return [ { "type": "list", "name": "prefix", @@ -146,7 +146,6 @@ def questions(self) -> Questions: ), }, ] - return questions def message(self, answers: dict) -> str: prefix = answers["prefix"] @@ -165,9 +164,7 @@ def message(self, answers: dict) -> str: if footer: footer = f"\n\n{footer}" - message = f"{prefix}{scope}: {subject}{body}{footer}" - - return message + return f"{prefix}{scope}: {subject}{body}{footer}" def example(self) -> str: return ( @@ -188,25 +185,21 @@ def schema(self) -> str: ) def schema_pattern(self) -> str: - PATTERN = ( + return ( r"(?s)" # To explicitly make . match new line r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)" # type r"(\(\S+\))?!?:" # scope r"( [^\n\r]+)" # subject r"((\n\n.*)|(\s*))?$" ) - return PATTERN def info(self) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) filepath = os.path.join(dir_path, "conventional_commits_info.txt") with open(filepath, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content + return f.read() def process_commit(self, commit: str) -> str: - pat = re.compile(self.schema_pattern()) - m = re.match(pat, commit) - if m is None: - return "" - return m.group(3).strip() + if m := re.match(self.schema_pattern(), commit): + return m.group(3).strip() + return "" diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index b8fd056a71..f43de2177c 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -8,7 +8,7 @@ class JiraSmartCz(BaseCommitizen): def questions(self) -> Questions: - questions = [ + return [ { "type": "input", "name": "message", @@ -42,7 +42,6 @@ def questions(self) -> Questions: "filter": lambda x: "#comment " + x if x else "", }, ] - return questions def message(self, answers: dict) -> str: return " ".join( @@ -77,5 +76,4 @@ def info(self) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) filepath = os.path.join(dir_path, "jira_info.txt") with open(filepath, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content + return f.read() From 03eff5cd86399c3d1ca16c0bfdba43cd29b2f996 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 20 May 2025 00:06:03 +0800 Subject: [PATCH 069/275] refactor(git): extract _create_commit_cmd_string --- commitizen/git.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index fa59e34d48..ab2866ac48 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -181,19 +181,22 @@ def commit( f.write(message.encode("utf-8")) f.close() - command = f'git commit {args} -F "{f.name}"' - - if committer_date and os.name == "nt": # pragma: no cover - # Using `cmd /v /c "{command}"` sets environment variables only for that command - command = f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"' - elif committer_date: - command = f"GIT_COMMITTER_DATE={committer_date} {command}" - + command = _create_commit_cmd_string(args, committer_date, f.name) c = cmd.run(command) os.unlink(f.name) return c +def _create_commit_cmd_string(args: str, committer_date: str | None, name: str) -> str: + command = f'git commit {args} -F "{name}"' + if not committer_date: + return command + if os.name != "nt": + return f"GIT_COMMITTER_DATE={committer_date} {command}" + # Using `cmd /v /c "{command}"` sets environment variables only for that command + return f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"' + + def get_commits( start: str | None = None, end: str = "HEAD", From 3153e0795142f2b6d0f863d372cb547cfcdad57f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 23 May 2025 16:24:05 +0800 Subject: [PATCH 070/275] test(test_git): mock os --- tests/test_git.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_git.py b/tests/test_git.py index de3130412b..3fecaabafd 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -449,3 +449,27 @@ def test_git_commit_from_rev_and_commit(): assert commit.author == "John Doe" assert commit.author_email == "john@example.com" assert commit.parents == [] + + +@pytest.mark.parametrize( + "os_name,committer_date,expected_cmd", + [ + ( + "nt", + "2024-03-20", + 'cmd /v /c "set GIT_COMMITTER_DATE=2024-03-20&& git commit -F "temp.txt""', + ), + ( + "posix", + "2024-03-20", + 'GIT_COMMITTER_DATE=2024-03-20 git commit -F "temp.txt"', + ), + ("nt", None, 'git commit -F "temp.txt"'), + ("posix", None, 'git commit -F "temp.txt"'), + ], +) +def test_create_commit_cmd_string(mocker, os_name, committer_date, expected_cmd): + """Test the OS-specific behavior of _create_commit_cmd_string""" + mocker.patch("os.name", os_name) + result = git._create_commit_cmd_string("", committer_date, "temp.txt") + assert result == expected_cmd From 876de4f8c1b1c4c3d0d4222c9affc1f2ab5f29c4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 23 May 2025 22:20:58 +0800 Subject: [PATCH 071/275] refactor(cli): early return and improve test coverage --- commitizen/cli.py | 21 ++++++++--------- tests/test_cli.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index b566fc596d..deb5644d27 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -553,19 +553,20 @@ def commitizen_excepthook( type, value, traceback, debug=False, no_raise: list[int] | None = None ): traceback = traceback if isinstance(traceback, TracebackType) else None + if not isinstance(value, CommitizenException): + original_excepthook(type, value, traceback) + return + if not no_raise: no_raise = [] - if isinstance(value, CommitizenException): - if value.message: - value.output_method(value.message) - if debug: - original_excepthook(type, value, traceback) - exit_code = value.exit_code - if exit_code in no_raise: - exit_code = ExitCode.EXPECTED_EXIT - sys.exit(exit_code) - else: + if value.message: + value.output_method(value.message) + if debug: original_excepthook(type, value, traceback) + exit_code = value.exit_code + if exit_code in no_raise: + exit_code = ExitCode.EXPECTED_EXIT + sys.exit(exit_code) commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True) diff --git a/tests/test_cli.py b/tests/test_cli.py index a91e633128..31371caea4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,7 @@ import os import subprocess import sys +import types from functools import partial import pytest @@ -182,3 +183,59 @@ def test_unknown_args_before_double_dash_raises(mocker: MockFixture): assert "Invalid commitizen arguments were found before -- separator" in str( excinfo.value ) + + +def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture): + """Test that commitizen_excepthook delegates to original_excepthook for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + + # Create a regular exception + test_exception = ValueError("test error") + + # Call commitizen_excepthook with the regular exception + cli.commitizen_excepthook(ValueError, test_exception, None) + + # Verify original_excepthook was called with correct arguments + mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) + + +def test_commitizen_excepthook_non_commitizen_exception_with_traceback( + mocker: MockFixture, +): + """Test that commitizen_excepthook handles traceback correctly for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + + # Create a regular exception with a traceback + test_exception = ValueError("test error") + test_traceback = mocker.Mock(spec=types.TracebackType) + + # Call commitizen_excepthook with the regular exception and traceback + cli.commitizen_excepthook(ValueError, test_exception, test_traceback) + + # Verify original_excepthook was called with correct arguments including traceback + mock_original_excepthook.assert_called_once_with( + ValueError, test_exception, test_traceback + ) + + +def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback( + mocker: MockFixture, +): + """Test that commitizen_excepthook handles invalid traceback correctly for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + + # Create a regular exception with an invalid traceback + test_exception = ValueError("test error") + test_traceback = mocker.Mock() # Not a TracebackType + + # Call commitizen_excepthook with the regular exception and invalid traceback + cli.commitizen_excepthook(ValueError, test_exception, test_traceback) + + # Verify original_excepthook was called with None as traceback + mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) From 52e44e5b4bcf62286d6545b5e3218bb54bafd6aa Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 24 May 2025 00:25:11 +0800 Subject: [PATCH 072/275] build(termcolor): remove termcolor <3 restriction --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index ceccd3da1c..b5dbcd9898 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1551,14 +1551,14 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "termcolor" -version = "2.5.0" +version = "3.1.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, - {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, ] [package.extras] @@ -1931,4 +1931,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "7375bf9072066831425c1b72fec016c42791695338c061324fb47420dafd2c8d" +content-hash = "5e42ee6a3a2b13c6f277b29f063fd8d71bf89841b1564636dffe56f7c65c33b4" diff --git a/pyproject.toml b/pyproject.toml index 8215595737..ac1eab66ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "questionary (>=2.0,<3.0)", "decli (>=0.6.0,<1.0)", "colorama (>=0.4.1,<1.0)", - "termcolor (>=1.1,<3)", + "termcolor (>=1.1.0,<4.0.0)", "packaging>=19", "tomlkit (>=0.5.3,<1.0.0)", "jinja2>=2.10.3", From edb21df5703332ae7320f7266021ec26c89891aa Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 25 May 2025 23:58:56 +0800 Subject: [PATCH 073/275] build: specify importlib-metadata version to fix unit tests --- poetry.lock | 95 ++++++++++++++++++++++++++++++++++++++++++++++---- pyproject.toml | 4 +-- 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index b5dbcd9898..6a31d292fe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -222,6 +222,7 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["documentation"] +markers = "python_version == \"3.9\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -230,6 +231,22 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["documentation"] +markers = "python_version != \"3.9\"" +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -510,12 +527,37 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.6.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] markers = "python_version == \"3.9\"" +files = [ + {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, + {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version != \"3.9\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -552,6 +594,7 @@ description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.9" groups = ["dev"] +markers = "python_version == \"3.9\"" files = [ {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, @@ -583,6 +626,46 @@ qtconsole = ["qtconsole"] test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] +[[package]] +name = "ipython" +version = "8.37.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version != \"3.9\"" +files = [ + {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, + {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt_toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack_data = "*" +traitlets = ">=5.13.0" +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} + +[package.extras] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] +kernel = ["ipykernel"] +matplotlib = ["matplotlib"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] + [[package]] name = "jedi" version = "0.19.2" @@ -1021,7 +1104,7 @@ description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" groups = ["dev"] -markers = "sys_platform != \"win32\"" +markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1124,7 +1207,7 @@ description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" groups = ["dev"] -markers = "sys_platform != \"win32\"" +markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -1722,7 +1805,7 @@ files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] -markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.12\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" @@ -1914,11 +1997,11 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] -markers = "python_version == \"3.9\"" files = [ {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"}, {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"}, ] +markers = {documentation = "python_version == \"3.9\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] @@ -1931,4 +2014,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "5e42ee6a3a2b13c6f277b29f063fd8d71bf89841b1564636dffe56f7c65c33b4" +content-hash = "146cae2abe0cdb8a831b9ece0aa1ee98366e3e4e17ef734f0e7001c4eb7508b3" diff --git a/pyproject.toml b/pyproject.toml index ac1eab66ca..144868e197 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,8 +23,8 @@ dependencies = [ "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'", "charset-normalizer (>=2.1.0,<4)", # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility - "importlib_metadata (>=8.0.0,<9) ; python_version < '3.10'", - + "importlib-metadata >=8.0.0,!=8.7.0,<9.0.0 ; python_version == '3.9'", # importlib-metadata@8.7.0 + python3.9 breaks our unit test + "importlib-metadata >=8.0.0,<9.0.0 ; python_version != '3.9'", ] keywords = ["commitizen", "conventional", "commits", "git"] # See also: https://pypi.org/classifiers/ From 7750f420822ff4be81e103da34051c866c5a944f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 24 May 2025 15:50:21 +0800 Subject: [PATCH 074/275] refactor(changelog): better typing, yield --- commitizen/changelog.py | 33 +++++++++++++++++--------------- commitizen/commands/changelog.py | 4 +++- tests/test_changelog.py | 16 ++++++++-------- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index d8ab56069b..bac4d824f9 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -29,10 +29,10 @@ import re from collections import OrderedDict, defaultdict -from collections.abc import Iterable +from collections.abc import Generator, Iterable, Mapping from dataclasses import dataclass from datetime import date -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from jinja2 import ( BaseLoader, @@ -84,7 +84,7 @@ def generate_tree_from_commits( changelog_message_builder_hook: MessageBuilderHook | None = None, changelog_release_hook: ChangelogReleaseHook | None = None, rules: TagRules | None = None, -) -> Iterable[dict]: +) -> Generator[dict[str, Any], None, None]: pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL) @@ -187,24 +187,27 @@ def process_commit_message( changes[change_type].append(msg) -def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable: +def generate_ordered_changelog_tree( + tree: Iterable[Mapping[str, Any]], change_type_order: list[str] +) -> Generator[dict[str, Any], None, None]: if len(set(change_type_order)) != len(change_type_order): raise InvalidConfigurationError( f"Change types contain duplicated types ({change_type_order})" ) - sorted_tree = [] for entry in tree: - ordered_change_types = change_type_order + sorted( - set(entry["changes"].keys()) - set(change_type_order) - ) - changes = [ - (ct, entry["changes"][ct]) - for ct in ordered_change_types - if ct in entry["changes"] - ] - sorted_tree.append({**entry, **{"changes": OrderedDict(changes)}}) - return sorted_tree + yield { + **entry, + "changes": _calculate_sorted_changes(change_type_order, entry["changes"]), + } + + +def _calculate_sorted_changes( + change_type_order: list[str], changes: Mapping[str, Any] +) -> OrderedDict[str, Any]: + remaining_change_types = set(changes.keys()) - set(change_type_order) + sorted_change_types = change_type_order + sorted(remaining_change_types) + return OrderedDict((ct, changes[ct]) for ct in sorted_change_types if ct in changes) def get_changelog_template(loader: BaseLoader, template: str) -> Template: diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 9ab8fdc37c..7bf644a934 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -215,7 +215,9 @@ def __call__(self): rules=self.tag_rules, ) if self.change_type_order: - tree = changelog.order_changelog_tree(tree, self.change_type_order) + tree = changelog.generate_ordered_changelog_tree( + tree, self.change_type_order + ) extras = self.cz.template_extras.copy() extras.update(self.config.settings["extras"]) diff --git a/tests/test_changelog.py b/tests/test_changelog.py index d42176f09a..ed90ed08e4 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1215,26 +1215,26 @@ def test_generate_tree_from_commits_with_no_commits(tags): ), ), ) -def test_order_changelog_tree(change_type_order, expected_reordering): - tree = changelog.order_changelog_tree(COMMITS_TREE, change_type_order) +def test_generate_ordered_changelog_tree(change_type_order, expected_reordering): + tree = changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order) for index, entry in enumerate(tuple(tree)): - version = tree[index]["version"] + version = entry["version"] if version in expected_reordering: # Verify that all keys are present - assert [*tree[index].keys()] == [*COMMITS_TREE[index].keys()] + assert [*entry.keys()] == [*COMMITS_TREE[index].keys()] # Verify that the reorder only impacted the returned dict and not the original expected = expected_reordering[version] - assert [*tree[index]["changes"].keys()] == expected["sorted"] + assert [*entry["changes"].keys()] == expected["sorted"] assert [*COMMITS_TREE[index]["changes"].keys()] == expected["original"] else: - assert [*entry["changes"].keys()] == [*tree[index]["changes"].keys()] + assert [*entry["changes"].keys()] == [*entry["changes"].keys()] -def test_order_changelog_tree_raises(): +def test_generate_ordered_changelog_tree_raises(): change_type_order = ["BREAKING CHANGE", "feat", "refactor", "feat"] with pytest.raises(InvalidConfigurationError) as excinfo: - changelog.order_changelog_tree(COMMITS_TREE, change_type_order) + list(changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order)) assert "Change types contain duplicated types" in str(excinfo) From fcd609e72ba71ec14d83fb7a64e64503f268f314 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 13:28:42 +0800 Subject: [PATCH 075/275] build(deps-dev): bump mypy to 1.16.0 --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6a31d292fe..cbe0fb0e3c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2014,4 +2014,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "146cae2abe0cdb8a831b9ece0aa1ee98366e3e4e17ef734f0e7001c4eb7508b3" +content-hash = "6eea6e061a2ece897c2fb37b0b5116085057e6d97e1f22c0fcd4294fb0ad1dbf" diff --git a/pyproject.toml b/pyproject.toml index 144868e197..1a29ca6bf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,7 +121,7 @@ deprecated = "^1.2.13" [tool.poetry.group.linters.dependencies] ruff = ">=0.5.0,<0.10.0" pre-commit = ">=2.18,<5.0" -mypy = "^1.15.0" +mypy = "^1.16.0" types-deprecated = "^1.2.9.2" types-python-dateutil = "^2.8.19.13" types-PyYAML = ">=5.4.3,<7.0.0" From b4c8aa35e70e1ca6438497f658dedecfcbdc7656 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 13:28:57 +0800 Subject: [PATCH 076/275] refactor(mypy): remove `unused-ignore` --- commitizen/config/json_config.py | 2 +- commitizen/config/toml_config.py | 2 +- commitizen/config/yaml_config.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index d413d73383..28fac25662 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -13,7 +13,7 @@ class JsonConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.path = path # type: ignore + self.path = path self._parse_setting(data) def init_empty_config_content(self): diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index e2cfcc9340..a7050214ba 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -14,7 +14,7 @@ class TomlConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.path = path # type: ignore + self.path = path self._parse_setting(data) def init_empty_config_content(self): diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index c5721c8d4b..2c384cf17d 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -14,7 +14,7 @@ class YAMLConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): super().__init__() self.is_empty_config = False - self.path = path # type: ignore + self.path = path self._parse_setting(data) def init_empty_config_content(self): From 94b9796453e250c76f1bc4455c5542c2339cf7be Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 11:08:12 +0800 Subject: [PATCH 077/275] refactor(cli.py): add type hints --- commitizen/cli.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index deb5644d27..04a99e8c37 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -8,7 +8,7 @@ from functools import partial from pathlib import Path from types import TracebackType -from typing import Any +from typing import TYPE_CHECKING, Any, cast import argcomplete from decli import cli @@ -596,8 +596,33 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]: return no_raise_codes +if TYPE_CHECKING: + + class Args(argparse.Namespace): + config: str | None = None + debug: bool = False + name: str | None = None + no_raise: str | None = None + report: bool = False + project: bool = False + commitizen: bool = False + verbose: bool = False + func: type[ + commands.Init # init + | commands.Commit # commit (c) + | commands.ListCz # ls + | commands.Example # example + | commands.Info # info + | commands.Schema # schema + | commands.Bump # bump + | commands.Changelog # changelog (ch) + | commands.Check # check + | commands.Version # version + ] + + def main(): - parser = cli(data) + parser: argparse.ArgumentParser = cli(data) argcomplete.autocomplete(parser) # Show help if no arg provided if len(sys.argv) == 1: @@ -611,7 +636,7 @@ def main(): # https://github.com/commitizen-tools/commitizen/issues/429 # argparse raises TypeError when non exist command is provided on Python < 3.9 # but raise SystemExit with exit code == 2 on Python 3.9 - if isinstance(e, TypeError) or (isinstance(e, SystemExit) and e.code == 2): + if isinstance(e, TypeError) or e.code == 2: raise NoCommandFoundError() raise e @@ -638,6 +663,7 @@ def main(): arguments["extra_cli_args"] = extra_args conf = config.read_cfg(args.config) + args = cast("Args", args) if args.name: conf.update({"name": args.name}) elif not conf.path: From a5033ab42c75bc859be94872ff80d474425a109d Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 13:55:34 +0800 Subject: [PATCH 078/275] refactor: add comment clarifying `no_raise` parsing to `list[int]` --- commitizen/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 04a99e8c37..861ba578a9 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -602,7 +602,7 @@ class Args(argparse.Namespace): config: str | None = None debug: bool = False name: str | None = None - no_raise: str | None = None + no_raise: str | None = None # comma-separated string, later parsed as list[int] report: bool = False project: bool = False commitizen: bool = False From 8055c9509283227def93235561b8675f504f4c18 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 14:24:07 +0800 Subject: [PATCH 079/275] refactor: remove `TypeError` handling since `Python >=3.9` is required Since the project requires Python >=3.9, argparse only raises SystemExit when non-existent commands are provided, making TypeError handling unnecessary. --- commitizen/cli.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 861ba578a9..d245f731e3 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -632,11 +632,8 @@ def main(): # This is for the command required constraint in 2.0 try: args, unknown_args = parser.parse_known_args() - except (TypeError, SystemExit) as e: - # https://github.com/commitizen-tools/commitizen/issues/429 - # argparse raises TypeError when non exist command is provided on Python < 3.9 - # but raise SystemExit with exit code == 2 on Python 3.9 - if isinstance(e, TypeError) or e.code == 2: + except SystemExit as e: + if e.code == 2: raise NoCommandFoundError() raise e From 9f67a43286c28778458faa76e66f81e588523b39 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 01:16:21 +0800 Subject: [PATCH 080/275] fix(deprecated): mark deprecate in v5 --- commitizen/commands/bump.py | 2 +- commitizen/defaults.py | 2 +- commitizen/version_schemes.py | 2 +- docs/commands/commit.md | 2 +- tests/test_defaults.py | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index a2d98c2451..3f909d97cb 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -82,7 +82,7 @@ def __init__(self, config: BaseConfig, arguments: dict): if deprecated_version_type: warnings.warn( DeprecationWarning( - "`--version-type` parameter is deprecated and will be removed in commitizen 4. " + "`--version-type` parameter is deprecated and will be removed in v5. " "Please use `--version-scheme` instead" ) ) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 10d55b8621..adbb9e6d1a 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -175,7 +175,7 @@ def __getattr__(name: str) -> Any: if name in deprecated_vars: value, replacement = deprecated_vars[name] warnings.warn( - f"{name} is deprecated and will be removed in a future version. " + f"{name} is deprecated and will be removed in v5. " f"Use {replacement} instead.", DeprecationWarning, stacklevel=2, diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index d5370b2c6c..2a5f7dc6e0 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -419,7 +419,7 @@ def get_version_scheme(settings: Settings, name: str | None = None) -> VersionSc if deprecated_setting: warnings.warn( DeprecationWarning( - "`version_type` setting is deprecated and will be removed in commitizen 4. " + "`version_type` setting is deprecated and will be removed in v5. " "Please use `version_scheme` instead" ) ) diff --git a/docs/commands/commit.md b/docs/commands/commit.md index 5a073a2644..be9d193b97 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -42,7 +42,7 @@ cz c -a -- -n # Stage all changes and skip the pre-commit and commit- ``` !!! warning - The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated. + The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated and will be removed in v5. ### Retry diff --git a/tests/test_defaults.py b/tests/test_defaults.py index 2298068662..73cd35b80c 100644 --- a/tests/test_defaults.py +++ b/tests/test_defaults.py @@ -19,9 +19,7 @@ def test_getattr_deprecated_vars(): # Verify warning messages assert len(record) == 7 for warning in record: - assert "is deprecated and will be removed in a future version" in str( - warning.message - ) + assert "is deprecated and will be removed" in str(warning.message) def test_getattr_non_existent(): From 1f9578fcbc9c23d0ff221e3e61f96b7e69b3bb96 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 15:25:28 +0800 Subject: [PATCH 081/275] build(deps-dev): bump ruff --- poetry.lock | 40 ++++++++++++++++++++-------------------- pyproject.toml | 3 ++- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index cbe0fb0e3c..f9f74513a8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1574,30 +1574,30 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.9.10" +version = "0.11.13" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["linters"] files = [ - {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, - {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, - {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, - {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, - {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, - {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, - {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, + {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"}, + {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"}, + {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"}, + {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"}, + {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"}, + {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"}, + {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"}, ] [[package]] @@ -2014,4 +2014,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "6eea6e061a2ece897c2fb37b0b5116085057e6d97e1f22c0fcd4294fb0ad1dbf" +content-hash = "10e298e9b4d2f2d81a82b43649a68d557ed3e9824b3b210b5b6a607935f9fe9d" diff --git a/pyproject.toml b/pyproject.toml index 1a29ca6bf9..d1e89cbed0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,7 +119,7 @@ pytest-xdist = "^3.1.0" deprecated = "^1.2.13" [tool.poetry.group.linters.dependencies] -ruff = ">=0.5.0,<0.10.0" +ruff = "^0.11.5" pre-commit = ">=2.18,<5.0" mypy = "^1.16.0" types-deprecated = "^1.2.9.2" @@ -182,6 +182,7 @@ commands_pre = [["poetry", "install", "--only", "main,test"]] commands = [["pytest", { replace = "posargs", extend = true }]] [tool.ruff] +required-version = ">=0.11.5" line-length = 88 [tool.ruff.lint] From 82cdfff22fa1bdf7ed3bf2fb673e6a07d5adf33d Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 15:30:28 +0800 Subject: [PATCH 082/275] style(ruff): resolve F401 --- commitizen/version_schemes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 2a5f7dc6e0..96a68b05fd 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -19,7 +19,7 @@ else: import importlib_metadata as metadata -from packaging.version import InvalidVersion # noqa: F401: expose the common exception +from packaging.version import InvalidVersion # noqa: F401 (expose the common exception) from packaging.version import Version as _BaseVersion from commitizen.defaults import MAJOR, MINOR, PATCH, Settings From ac3318dd19a7569dc8e887b956d6ef447aec25c8 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 15:40:08 +0800 Subject: [PATCH 083/275] build(ruff): add RUF022 & RUF100 rules --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d1e89cbed0..757c72bc0a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -195,6 +195,10 @@ select = [ "UP", # isort "I", + # unsorted-dunder-all + "RUF022", + # unused-noqa + "RUF100", ] ignore = ["E501", "D1", "D415"] From 922bdc30cb9aa679f2be4fa19997d0d05b394fe9 Mon Sep 17 00:00:00 2001 From: gbaian10 Date: Sat, 31 May 2025 15:41:42 +0800 Subject: [PATCH 084/275] style(ruff): run `ruff check --fix` Auto fix RUF022 and RUF100 --- commitizen/commands/__init__.py | 4 ++-- commitizen/commands/bump.py | 2 +- commitizen/cz/conventional_commits/conventional_commits.py | 2 +- commitizen/cz/jira/jira.py | 2 +- commitizen/providers/__init__.py | 2 +- tests/conftest.py | 2 +- tests/test_bump_find_increment.py | 4 ++-- tests/test_cz_conventional_commits.py | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py index 806e384522..58b18297dc 100644 --- a/commitizen/commands/__init__.py +++ b/commitizen/commands/__init__.py @@ -11,13 +11,13 @@ __all__ = ( "Bump", + "Changelog", "Check", "Commit", - "Changelog", "Example", "Info", + "Init", "ListCz", "Schema", "Version", - "Init", ) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 3f909d97cb..9556492499 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -135,7 +135,7 @@ def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: ) return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map) - def __call__(self) -> None: # noqa: C901 + def __call__(self) -> None: """Steps executed to bump.""" provider = get_provider(self.config) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 9a2b9016b7..ff79119886 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -31,7 +31,7 @@ class ConventionalCommitsCz(BaseCommitizen): bump_pattern = defaults.BUMP_PATTERN bump_map = defaults.BUMP_MAP bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO - commit_parser = r"^((?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?|\w+!):\s(?P.*)?" # noqa + commit_parser = r"^((?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?|\w+!):\s(?P.*)?" change_type_map = { "feat": "Feat", "fix": "Fix", diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index f43de2177c..05e23e1690 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -67,7 +67,7 @@ def example(self) -> str: ) def schema(self) -> str: - return " # " # noqa + return " # " def schema_pattern(self) -> str: return r".*[A-Z]{2,}\-[0-9]+( #| .* #).+( #.+)*" diff --git a/commitizen/providers/__init__.py b/commitizen/providers/__init__.py index 9cf4ce5927..3e01fe22f8 100644 --- a/commitizen/providers/__init__.py +++ b/commitizen/providers/__init__.py @@ -21,7 +21,6 @@ from commitizen.providers.uv_provider import UvProvider __all__ = [ - "get_provider", "CargoProvider", "CommitizenProvider", "ComposerProvider", @@ -30,6 +29,7 @@ "PoetryProvider", "ScmProvider", "UvProvider", + "get_provider", ] PROVIDER_ENTRYPOINT = "commitizen.provider" diff --git a/tests/conftest.py b/tests/conftest.py index 60c586f2e6..1b49dcbfaa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -169,7 +169,7 @@ class SemverCommitizen(BaseCommitizen): "patch": "PATCH", } changelog_pattern = r"^(patch|minor|major)" - commit_parser = r"^(?Ppatch|minor|major)(?:\((?P[^()\r\n]*)\)|\()?:?\s(?P.+)" # noqa + commit_parser = r"^(?Ppatch|minor|major)(?:\((?P[^()\r\n]*)\)|\()?:?\s(?P.+)" change_type_map = { "major": "Breaking Changes", "minor": "Features", diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py index ff24ff17a7..77e11c78c7 100644 --- a/tests/test_bump_find_increment.py +++ b/tests/test_bump_find_increment.py @@ -32,14 +32,14 @@ MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [ "feat(cli): added version", "docs(README): motivation", - "BREAKING CHANGE: `extends` key in config file is now used for extending other config files", # noqa + "BREAKING CHANGE: `extends` key in config file is now used for extending other config files", "fix(setup.py): future is now required for every python version", ] MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC = [ "feat(cli): added version", "docs(README): motivation", - "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files", # noqa + "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files", "fix(setup.py): future is now required for every python version", ] diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 6d4e0f7435..7863e2f38c 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -89,7 +89,7 @@ def test_long_answer(config): message = conventional_commits.message(answers) assert ( message - == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24" # noqa + == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24" ) @@ -107,7 +107,7 @@ def test_breaking_change_in_footer(config): print(message) assert ( message - == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users" # noqa + == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users" ) From b6ad3d23cd3cd90f2f235f0fc7e501e551097087 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 12:10:17 +0800 Subject: [PATCH 085/275] refactor(BaseCommitizen): remove unused process_commit --- commitizen/cz/base.py | 7 ------ .../conventional_commits.py | 6 ----- tests/test_cz_base.py | 6 ----- tests/test_cz_conventional_commits.py | 23 ------------------- 4 files changed, 42 deletions(-) diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 43455a74ca..7463a67c24 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -99,10 +99,3 @@ def schema_pattern(self) -> str: def info(self) -> str: """Information about the standardized commit message.""" raise NotImplementedError("Not Implemented yet") - - def process_commit(self, commit: str) -> str: - """Process commit for changelog. - - If not overwritten, it returns the first line of commit. - """ - return commit.split("\n")[0] diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index ff79119886..320aa9b324 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,5 +1,4 @@ import os -import re from commitizen import defaults from commitizen.cz.base import BaseCommitizen @@ -198,8 +197,3 @@ def info(self) -> str: filepath = os.path.join(dir_path, "conventional_commits_info.txt") with open(filepath, encoding=self.config.settings["encoding"]) as f: return f.read() - - def process_commit(self, commit: str) -> str: - if m := re.match(self.schema_pattern(), commit): - return m.group(3).strip() - return "" diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py index 4ee1cc6eda..be93b4ca0f 100644 --- a/tests/test_cz_base.py +++ b/tests/test_cz_base.py @@ -42,9 +42,3 @@ def test_info(config): cz = DummyCz(config) with pytest.raises(NotImplementedError): cz.info() - - -def test_process_commit(config): - cz = DummyCz(config) - message = cz.process_commit("test(test_scope): this is test msg") - assert message == "test(test_scope): this is test msg" diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 7863e2f38c..89653f4c63 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -130,26 +130,3 @@ def test_info(config): conventional_commits = ConventionalCommitsCz(config) info = conventional_commits.info() assert isinstance(info, str) - - -@pytest.mark.parametrize( - ("commit_message", "expected_message"), - [ - ( - "test(test_scope): this is test msg", - "this is test msg", - ), - ( - "test(test_scope)!: this is test msg", - "this is test msg", - ), - ( - "test!(test_scope): this is test msg", - "", - ), - ], -) -def test_process_commit(commit_message, expected_message, config): - conventional_commits = ConventionalCommitsCz(config) - message = conventional_commits.process_commit(commit_message) - assert message == expected_message From 8c335705aef9acc83276d4be1c8798a1d2a0f1f6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 29 May 2025 22:13:11 +0800 Subject: [PATCH 086/275] style(tags): improve types --- commitizen/tags.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commitizen/tags.py b/commitizen/tags.py index c5f06884fe..4231e0053e 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -2,7 +2,7 @@ import re import warnings -from collections.abc import Sequence +from collections.abc import Iterable, Sequence from dataclasses import dataclass, field from functools import cached_property from string import Template @@ -89,14 +89,14 @@ class TagRules: merge_prereleases: bool = False @cached_property - def version_regexes(self) -> Sequence[re.Pattern]: + def version_regexes(self) -> list[re.Pattern]: """Regexes for all legit tag formats, current and legacy""" tag_formats = [self.tag_format, *self.legacy_tag_formats] regexes = (self._format_regex(p) for p in tag_formats) return [re.compile(r) for r in regexes] @cached_property - def ignored_regexes(self) -> Sequence[re.Pattern]: + def ignored_regexes(self) -> list[re.Pattern]: """Regexes for known but ignored tag formats""" regexes = (self._format_regex(p, star=True) for p in self.ignored_tag_formats) return [re.compile(r) for r in regexes] @@ -135,8 +135,8 @@ def is_ignored_tag(self, tag: str | GitTag) -> bool: return any(regex.match(tag) for regex in self.ignored_regexes) def get_version_tags( - self, tags: Sequence[GitTag], warn: bool = False - ) -> Sequence[GitTag]: + self, tags: Iterable[GitTag], warn: bool = False + ) -> list[GitTag]: """Filter in version tags and warn on unexpected tags""" return [tag for tag in tags if self.is_version_tag(tag, warn)] @@ -236,7 +236,7 @@ def normalize_tag( ) def find_tag_for( - self, tags: Sequence[GitTag], version: Version | str + self, tags: Iterable[GitTag], version: Version | str ) -> GitTag | None: """Find the first matching tag for a given version.""" version = self.scheme(version) if isinstance(version, str) else version From 28b3b9b8b553acb379a18f313ba8ec88e895ab6a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 29 May 2025 22:18:06 +0800 Subject: [PATCH 087/275] perf(tags): use set --- commitizen/tags.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commitizen/tags.py b/commitizen/tags.py index 4231e0053e..b19bb89e09 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -240,11 +240,11 @@ def find_tag_for( ) -> GitTag | None: """Find the first matching tag for a given version.""" version = self.scheme(version) if isinstance(version, str) else version - possible_tags = [ + possible_tags = set( self.normalize_tag(version, f) for f in (self.tag_format, *self.legacy_tag_formats) - ] - candidates = [t for t in tags if any(t.name == p for p in possible_tags)] + ) + candidates = [t for t in tags if t.name in possible_tags] if len(candidates) > 1: warnings.warn( UserWarning( From 898d054a443d6fd591e5a7e8c4156f7e95038287 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 29 May 2025 23:20:33 +0800 Subject: [PATCH 088/275] refactor: fix mypy output and better type --- commitizen/changelog.py | 10 ++++---- commitizen/cli.py | 14 +++++++---- commitizen/cmd.py | 5 ++-- commitizen/commands/changelog.py | 24 ++++++++++--------- commitizen/commands/check.py | 10 ++++---- commitizen/commands/commit.py | 14 +++++++---- commitizen/commands/info.py | 4 ++-- commitizen/commands/init.py | 8 +++---- commitizen/commands/list_cz.py | 4 ++-- commitizen/commands/version.py | 6 +++-- commitizen/config/base_config.py | 2 +- .../conventional_commits.py | 4 ++-- commitizen/cz/utils.py | 5 ++-- commitizen/exceptions.py | 7 +++--- commitizen/git.py | 20 +++++++++------- commitizen/out.py | 7 +++--- tests/test_git.py | 7 ++++++ 17 files changed, 90 insertions(+), 61 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index bac4d824f9..6526b4c540 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -29,7 +29,7 @@ import re from collections import OrderedDict, defaultdict -from collections.abc import Generator, Iterable, Mapping +from collections.abc import Generator, Iterable, Mapping, Sequence from dataclasses import dataclass from datetime import date from typing import TYPE_CHECKING, Any @@ -225,7 +225,7 @@ def render_changelog( tree: Iterable, loader: BaseLoader, template: str, - **kwargs, + **kwargs: Any, ) -> str: jinja_template = get_changelog_template(loader, template) changelog: str = jinja_template.render(tree=tree, **kwargs) @@ -282,7 +282,7 @@ def incremental_build( def get_smart_tag_range( - tags: list[GitTag], newest: str, oldest: str | None = None + tags: Sequence[GitTag], newest: str, oldest: str | None = None ) -> list[GitTag]: """Smart because it finds the N+1 tag. @@ -308,10 +308,10 @@ def get_smart_tag_range( def get_oldest_and_newest_rev( - tags: list[GitTag], + tags: Sequence[GitTag], version: str, rules: TagRules, -) -> tuple[str | None, str | None]: +) -> tuple[str | None, str]: """Find the tags for the given version. `version` may come in different formats: diff --git a/commitizen/cli.py b/commitizen/cli.py index d245f731e3..9998691f41 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -50,7 +50,7 @@ def __call__( namespace: argparse.Namespace, kwarg: str | Sequence[Any] | None, option_string: str | None = None, - ): + ) -> None: if not isinstance(kwarg, str): return if "=" not in kwarg: @@ -550,8 +550,12 @@ def __call__( def commitizen_excepthook( - type, value, traceback, debug=False, no_raise: list[int] | None = None -): + type: type[BaseException], + value: BaseException, + traceback: TracebackType | None, + debug: bool = False, + no_raise: list[int] | None = None, +) -> None: traceback = traceback if isinstance(traceback, TracebackType) else None if not isinstance(value, CommitizenException): original_excepthook(type, value, traceback) @@ -581,7 +585,7 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]: represents the exit code found in exceptions. """ no_raise_items: list[str] = comma_separated_no_raise.split(",") - no_raise_codes = [] + no_raise_codes: list[int] = [] for item in no_raise_items: if item.isdecimal(): no_raise_codes.append(int(item)) @@ -621,7 +625,7 @@ class Args(argparse.Namespace): ] -def main(): +def main() -> None: parser: argparse.ArgumentParser = cli(data) argcomplete.autocomplete(parser) # Show help if no arg provided diff --git a/commitizen/cmd.py b/commitizen/cmd.py index ba48ac7881..9d6b2f6d2a 100644 --- a/commitizen/cmd.py +++ b/commitizen/cmd.py @@ -1,6 +1,7 @@ import os import subprocess -from typing import NamedTuple +from collections.abc import Mapping +from typing import NamedTuple, Union from charset_normalizer import from_bytes @@ -28,7 +29,7 @@ def _try_decode(bytes_: bytes) -> str: raise CharacterSetDecodeError() from e -def run(cmd: str, env=None) -> Command: +def run(cmd: str, env: Union[Mapping[str, str], None] = None) -> Command: if env is not None: env = {**os.environ, **env} process = subprocess.Popen( diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 7bf644a934..181531dee2 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -2,10 +2,11 @@ import os import os.path -from collections.abc import Generator +from collections.abc import Generator, Iterable, Mapping from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path +from typing import Any, cast from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format @@ -28,7 +29,7 @@ class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args): + def __init__(self, config: BaseConfig, args: Mapping[str, Any]): if not git.is_git_project(): raise NotAGitProjectError() @@ -76,10 +77,11 @@ def __init__(self, config: BaseConfig, args): self.change_type_map = ( self.config.settings.get("change_type_map") or self.cz.change_type_map ) - self.change_type_order = ( + self.change_type_order = cast( + list[str], self.config.settings.get("change_type_order") or self.cz.change_type_order - or defaults.CHANGE_TYPE_ORDER + or defaults.CHANGE_TYPE_ORDER, ) self.rev_range = args.get("rev_range") self.tag_format: str = ( @@ -102,7 +104,7 @@ def __init__(self, config: BaseConfig, args): self.extras = args.get("extras") or {} self.export_template_to = args.get("export_template") - def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str: + def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str: """Try to find the 'start_rev'. We use a similarity approach. We know how to parse the version from the markdown @@ -151,18 +153,18 @@ def write_changelog( changelog_file.write(changelog_out) - def export_template(self): + def export_template(self) -> None: tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) - src = Path(tpl.filename) - Path(self.export_template_to).write_text(src.read_text()) + src = Path(tpl.filename) # type: ignore + Path(self.export_template_to).write_text(src.read_text()) # type: ignore - def __call__(self): + def __call__(self) -> None: commit_parser = self.cz.commit_parser changelog_pattern = self.cz.changelog_pattern start_rev = self.start_rev unreleased_version = self.unreleased_version changelog_meta = changelog.Metadata() - change_type_map: dict | None = self.change_type_map + change_type_map: dict[str, str] | None = self.change_type_map # type: ignore changelog_message_builder_hook: MessageBuilderHook | None = ( self.cz.changelog_message_builder_hook ) @@ -190,7 +192,7 @@ def __call__(self): changelog_meta = self.changelog_format.get_metadata(self.file_name) if changelog_meta.latest_version: start_rev = self._find_incremental_rev( - strip_local_version(changelog_meta.latest_version_tag), tags + strip_local_version(changelog_meta.latest_version_tag or ""), tags ) if self.rev_range: start_rev, end_rev = changelog.get_oldest_and_newest_rev( diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 1e3b8464e1..6cf91bb926 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -17,7 +17,9 @@ class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd()): + def __init__( + self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd() + ): """Initial check command. Args: @@ -48,7 +50,7 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd( self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) - def _valid_command_argument(self): + def _valid_command_argument(self) -> None: num_exclusive_args_provided = sum( arg is not None for arg in (self.commit_msg_file, self.commit_msg, self.rev_range) @@ -61,7 +63,7 @@ def _valid_command_argument(self): "See 'cz check -h' for more information" ) - def __call__(self): + def __call__(self) -> None: """Validate if commit messages follows the conventional pattern. Raises: @@ -97,7 +99,7 @@ def _get_commit_message(self) -> str | None: # Get commit message from file (--commit-msg-file) return commit_file.read() - def _get_commits(self): + def _get_commits(self) -> list[git.GitCommit]: if (msg := self._get_commit_message()) is not None: return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))] diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index eedf77e079..3e7a850a05 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -5,6 +5,8 @@ import shutil import subprocess import tempfile +from pathlib import Path +from typing import Union, cast import questionary @@ -105,11 +107,13 @@ def _get_message(self) -> str: return self.read_backup_message() or self.prompt_commit_questions() return self.prompt_commit_questions() - def __call__(self): - extra_args: str = self.arguments.get("extra_cli_args", "") - dry_run: bool = self.arguments.get("dry_run") - write_message_to_file: bool = self.arguments.get("write_message_to_file") - signoff: bool = self.arguments.get("signoff") + def __call__(self) -> None: + extra_args = cast(str, self.arguments.get("extra_cli_args", "")) + dry_run = cast(bool, self.arguments.get("dry_run")) + write_message_to_file = cast( + Union[Path, None], self.arguments.get("write_message_to_file") + ) + signoff = cast(bool, self.arguments.get("signoff")) if self.arguments.get("all"): git.add("-u") diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py index abd4197e7f..5b649ceb5d 100644 --- a/commitizen/commands/info.py +++ b/commitizen/commands/info.py @@ -5,9 +5,9 @@ class Info: """Show in depth explanation of your rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object): self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.info()) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 0eb3d99d17..a7e1b56665 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -207,7 +207,7 @@ def _ask_tag(self) -> str: raise NoAnswersError("Tag is required!") return latest_tag - def _ask_tag_format(self, latest_tag) -> str: + def _ask_tag_format(self, latest_tag: str) -> str: is_correct_format = False if latest_tag.startswith("v"): tag_format = r"v$version" @@ -302,7 +302,7 @@ def _ask_update_changelog_on_bump(self) -> bool: ).unsafe_ask() return update_changelog_on_bump - def _exec_install_pre_commit_hook(self, hook_types: list[str]): + def _exec_install_pre_commit_hook(self, hook_types: list[str]) -> None: cmd_str = self._gen_pre_commit_cmd(hook_types) c = cmd.run(cmd_str) if c.return_code != 0: @@ -323,7 +323,7 @@ def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str: ) return cmd_str - def _install_pre_commit_hook(self, hook_types: list[str] | None = None): + def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: pre_commit_config_filename = ".pre-commit-config.yaml" cz_hook_config = { "repo": "https://github.com/commitizen-tools/commitizen", @@ -369,6 +369,6 @@ def _install_pre_commit_hook(self, hook_types: list[str] | None = None): self._exec_install_pre_commit_hook(hook_types) out.write("commitizen pre-commit hook is now installed in your '.git'\n") - def _update_config_file(self, values: dict[str, Any]): + def _update_config_file(self, values: dict[str, Any]) -> None: for key, value in values.items(): self.config.set_key(key, value) diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py index 99701865af..ff4e12ad44 100644 --- a/commitizen/commands/list_cz.py +++ b/commitizen/commands/list_cz.py @@ -6,8 +6,8 @@ class ListCz: """List currently installed rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object): self.config: BaseConfig = config - def __call__(self): + def __call__(self) -> None: out.write("\n".join(registry.keys())) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 45d553c710..55d0a950a3 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -1,5 +1,7 @@ import platform import sys +from collections.abc import Mapping +from typing import Any from commitizen import out from commitizen.__version__ import __version__ @@ -10,13 +12,13 @@ class Version: """Get the version of the installed commitizen or the current project.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: Mapping[str, Any]): self.config: BaseConfig = config self.parameter = args[0] self.operating_system = platform.system() self.python_version = sys.version - def __call__(self): + def __call__(self) -> None: if self.parameter.get("report"): out.write(f"Commitizen Version: {__version__}") out.write(f"Python Version: {self.python_version}") diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index fd034412fe..587b72aee6 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -6,7 +6,7 @@ class BaseConfig: - def __init__(self): + def __init__(self) -> None: self._settings: Settings = DEFAULT_SETTINGS.copy() self.encoding = self.settings["encoding"] self._path: Path | None = None diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 320aa9b324..5b4889114f 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -8,7 +8,7 @@ __all__ = ["ConventionalCommitsCz"] -def parse_scope(text): +def parse_scope(text: str) -> str: if not text: return "" @@ -19,7 +19,7 @@ def parse_scope(text): return "-".join(scope) -def parse_subject(text): +def parse_subject(text: str) -> str: if isinstance(text, str): text = text.strip(".").strip() diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index cb79d65d1a..430bda2d43 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -1,6 +1,7 @@ import os import re import tempfile +from typing import Union from commitizen import git from commitizen.cz import exceptions @@ -8,13 +9,13 @@ _RE_LOCAL_VERSION = re.compile(r"\+.+") -def required_validator(answer, msg=None): +def required_validator(answer: str, msg: Union[str, None] = None) -> str: if not answer: raise exceptions.AnswerRequiredError(msg) return answer -def multiple_line_breaker(answer, sep="|"): +def multiple_line_breaker(answer: str, sep: str = "|") -> str: return "\n".join(line.strip() for line in answer.split(sep) if line) diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 29733b624b..87efabe2ab 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -1,4 +1,5 @@ import enum +from typing import Any from commitizen import out @@ -40,7 +41,7 @@ class ExitCode(enum.IntEnum): class CommitizenException(Exception): - def __init__(self, *args, **kwargs): + def __init__(self, *args: str, **kwargs: Any): self.output_method = kwargs.get("output_method") or out.error self.exit_code: ExitCode = self.__class__.exit_code if args: @@ -50,14 +51,14 @@ def __init__(self, *args, **kwargs): else: self.message = "" - def __str__(self): + def __str__(self) -> str: return self.message class ExpectedExit(CommitizenException): exit_code = ExitCode.EXPECTED_EXIT - def __init__(self, *args, **kwargs): + def __init__(self, *args: str, **kwargs: Any): output_method = kwargs.get("output_method") or out.write kwargs["output_method"] = output_method super().__init__(*args, **kwargs) diff --git a/commitizen/git.py b/commitizen/git.py index ab2866ac48..48fa13ec52 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -46,15 +46,15 @@ class GitObject: name: str date: str - def __eq__(self, other) -> bool: + def __eq__(self, other: object) -> bool: return hasattr(other, "rev") and self.rev == other.rev class GitCommit(GitObject): def __init__( self, - rev, - title, + rev: str, + title: str, body: str = "", author: str = "", author_email: str = "", @@ -68,7 +68,7 @@ def __init__( self.parents = parents or [] @property - def message(self): + def message(self) -> str: return f"{self.title}\n\n{self.body}".strip() @classmethod @@ -127,23 +127,27 @@ def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit: parents=[p for p in parents.strip().split(" ") if p], ) - def __repr__(self): + def __repr__(self) -> str: return f"{self.title} ({self.rev})" class GitTag(GitObject): - def __init__(self, name, rev, date): + def __init__(self, name: str, rev: str, date: str): self.rev = rev.strip() self.name = name.strip() self._date = date.strip() - def __repr__(self): + def __repr__(self) -> str: return f"GitTag('{self.name}', '{self.rev}', '{self.date}')" @property - def date(self): + def date(self) -> str: return self._date + @date.setter + def date(self, value: str) -> None: + self._date = value + @classmethod def from_line(cls, line: str, inner_delimiter: str) -> GitTag: name, objectname, date, obj = line.split(inner_delimiter) diff --git a/commitizen/out.py b/commitizen/out.py index 40342e9de5..1bbfe4329d 100644 --- a/commitizen/out.py +++ b/commitizen/out.py @@ -1,5 +1,6 @@ import io import sys +from typing import Any from termcolor import colored @@ -8,12 +9,12 @@ sys.stdout.reconfigure(encoding="utf-8") -def write(value: str, *args) -> None: +def write(value: str, *args: object) -> None: """Intended to be used when value is multiline.""" print(value, *args) -def line(value: str, *args, **kwargs) -> None: +def line(value: str, *args: object, **kwargs: Any) -> None: """Wrapper in case I want to do something different later.""" print(value, *args, **kwargs) @@ -33,7 +34,7 @@ def info(value: str) -> None: line(message) -def diagnostic(value: str): +def diagnostic(value: str) -> None: line(value, file=sys.stderr) diff --git a/tests/test_git.py b/tests/test_git.py index 3fecaabafd..e242b3a2ae 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -18,6 +18,13 @@ ) +@pytest.mark.parametrize("date", ["2020-01-21", "1970-01-01"]) +def test_git_tag_date(date: str): + git_tag = git.GitTag(rev="sha1-code", name="0.0.1", date="2025-05-30") + git_tag.date = date + assert git_tag.date == date + + def test_git_object_eq(): git_commit = git.GitCommit( rev="sha1-code", title="this is title", body="this is body" From 1647841d83c8047421f5bc75edadd55fd74ae795 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 01:25:06 +0800 Subject: [PATCH 089/275] refactor(conventional_commits): remove unnecessary checks --- .../conventional_commits.py | 22 +++----- tests/test_cz_conventional_commits.py | 54 +++++++++---------- 2 files changed, 30 insertions(+), 46 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 5b4889114f..912770a726 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -8,22 +8,12 @@ __all__ = ["ConventionalCommitsCz"] -def parse_scope(text: str) -> str: - if not text: - return "" +def _parse_scope(text: str) -> str: + return "-".join(text.strip().split()) - scope = text.strip().split() - if len(scope) == 1: - return scope[0] - return "-".join(scope) - - -def parse_subject(text: str) -> str: - if isinstance(text, str): - text = text.strip(".").strip() - - return required_validator(text, msg="Subject is required.") +def _parse_subject(text: str) -> str: + return required_validator(text.strip(".").strip(), msg="Subject is required.") class ConventionalCommitsCz(BaseCommitizen): @@ -112,12 +102,12 @@ def questions(self) -> Questions: "message": ( "What is the scope of this change? (class or file name): (press [enter] to skip)\n" ), - "filter": parse_scope, + "filter": _parse_scope, }, { "type": "input", "name": "subject", - "filter": parse_subject, + "filter": _parse_subject, "message": ( "Write a short and imperative summary of the code changes: (lower case and no period)\n" ), diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 89653f4c63..c96e036707 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -2,48 +2,42 @@ from commitizen.cz.conventional_commits.conventional_commits import ( ConventionalCommitsCz, - parse_scope, - parse_subject, + _parse_scope, + _parse_subject, ) from commitizen.cz.exceptions import AnswerRequiredError -valid_scopes = ["", "simple", "dash-separated", "camelCaseUPPERCASE"] -scopes_transformations = [["with spaces", "with-spaces"], [None, ""]] - -valid_subjects = ["this is a normal text", "aword"] - -subjects_transformations = [["with dot.", "with dot"]] - -invalid_subjects = ["", " ", ".", " .", "", None] - - -def test_parse_scope_valid_values(): - for valid_scope in valid_scopes: - assert valid_scope == parse_scope(valid_scope) +@pytest.mark.parametrize( + "valid_scope", ["", "simple", "dash-separated", "camelCaseUPPERCASE"] +) +def test_parse_scope_valid_values(valid_scope): + assert valid_scope == _parse_scope(valid_scope) -def test_scopes_transformations(): - for scopes_transformation in scopes_transformations: - invalid_scope, transformed_scope = scopes_transformation - assert transformed_scope == parse_scope(invalid_scope) +@pytest.mark.parametrize( + "scopes_transformation", [["with spaces", "with-spaces"], ["", ""]] +) +def test_scopes_transformations(scopes_transformation): + invalid_scope, transformed_scope = scopes_transformation + assert transformed_scope == _parse_scope(invalid_scope) -def test_parse_subject_valid_values(): - for valid_subject in valid_subjects: - assert valid_subject == parse_subject(valid_subject) +@pytest.mark.parametrize("valid_subject", ["this is a normal text", "aword"]) +def test_parse_subject_valid_values(valid_subject): + assert valid_subject == _parse_subject(valid_subject) -def test_parse_subject_invalid_values(): - for valid_subject in invalid_subjects: - with pytest.raises(AnswerRequiredError): - parse_subject(valid_subject) +@pytest.mark.parametrize("invalid_subject", ["", " ", ".", " .", "\t\t."]) +def test_parse_subject_invalid_values(invalid_subject): + with pytest.raises(AnswerRequiredError): + _parse_subject(invalid_subject) -def test_subject_transformations(): - for subject_transformation in subjects_transformations: - invalid_subject, transformed_subject = subject_transformation - assert transformed_subject == parse_subject(invalid_subject) +@pytest.mark.parametrize("subject_transformation", [["with dot.", "with dot"]]) +def test_subject_transformations(subject_transformation): + invalid_subject, transformed_subject = subject_transformation + assert transformed_subject == _parse_subject(invalid_subject) def test_questions(config): From 37eac1f292a6593935dfe32c0389f016947116ff Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 14:25:50 +0800 Subject: [PATCH 090/275] refactor: make methods protected, better type --- commitizen/commands/bump.py | 8 ++++---- commitizen/commands/changelog.py | 8 ++++---- commitizen/commands/commit.py | 10 +++++----- commitizen/commands/init.py | 4 ++-- commitizen/config/base_config.py | 3 +++ tests/commands/test_bump_command.py | 10 +++++----- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 9556492499..0607c7ef73 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -101,7 +101,7 @@ def __init__(self, config: BaseConfig, arguments: dict): ) self.extras = arguments["extras"] - def is_initial_tag( + def _is_initial_tag( self, current_tag: git.GitTag | None, is_yes: bool = False ) -> bool: """Check if reading the whole git tree up to HEAD is needed.""" @@ -118,7 +118,7 @@ def is_initial_tag( ) return bool(questionary.confirm("Is this the first tag created?").ask()) - def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: + def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None: # Update the bump map to ensure major version doesn't increment. is_major_version_zero: bool = self.bump_settings["major_version_zero"] # self.cz.bump_map = defaults.bump_map_major_version_zero @@ -227,7 +227,7 @@ def __call__(self) -> None: current_tag, "name", rules.normalize_tag(current_version) ) - is_initial = self.is_initial_tag(current_tag, is_yes) + is_initial = self._is_initial_tag(current_tag, is_yes) if manual_version: try: @@ -255,7 +255,7 @@ def __call__(self) -> None: "[NO_COMMITS_FOUND]\nNo new commits found." ) - increment = self.find_increment(commits) + increment = self._find_increment(commits) # It may happen that there are commits, but they are not eligible # for an increment, this generates a problem when using prerelease (#281) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 181531dee2..11f72cba18 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -136,7 +136,7 @@ def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> raise NoRevisionError() return start_rev - def write_changelog( + def _write_changelog( self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata ): with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file: @@ -153,7 +153,7 @@ def write_changelog( changelog_file.write(changelog_out) - def export_template(self) -> None: + def _export_template(self) -> None: tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) src = Path(tpl.filename) # type: ignore Path(self.export_template_to).write_text(src.read_text()) # type: ignore @@ -173,7 +173,7 @@ def __call__(self) -> None: ) if self.export_template_to: - return self.export_template() + return self._export_template() if not changelog_pattern or not commit_parser: raise NoPatternMapError( @@ -240,4 +240,4 @@ def __call__(self) -> None: with open(self.file_name, encoding=self.encoding) as changelog_file: lines = changelog_file.readlines() - self.write_changelog(changelog_out, lines, changelog_meta) + self._write_changelog(changelog_out, lines, changelog_meta) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 3e7a850a05..22ad642c43 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -41,7 +41,7 @@ def __init__(self, config: BaseConfig, arguments: dict): self.arguments = arguments self.temp_file: str = get_backup_file_path() - def read_backup_message(self) -> str | None: + def _read_backup_message(self) -> str | None: # Check the commit backup file exists if not os.path.isfile(self.temp_file): return None @@ -50,7 +50,7 @@ def read_backup_message(self) -> str | None: with open(self.temp_file, encoding=self.encoding) as f: return f.read().strip() - def prompt_commit_questions(self) -> str: + def _prompt_commit_questions(self) -> str: # Prompt user for the commit message cz = self.cz questions = cz.questions() @@ -96,7 +96,7 @@ def manual_edit(self, message: str) -> str: def _get_message(self) -> str: if self.arguments.get("retry"): - m = self.read_backup_message() + m = self._read_backup_message() if m is None: raise NoCommitBackupError() return m @@ -104,8 +104,8 @@ def _get_message(self) -> str: if self.config.settings.get("retry_after_failure") and not self.arguments.get( "no_retry" ): - return self.read_backup_message() or self.prompt_commit_questions() - return self.prompt_commit_questions() + return self._read_backup_message() or self._prompt_commit_questions() + return self._prompt_commit_questions() def __call__(self) -> None: extra_args = cast(str, self.arguments.get("extra_cli_args", "")) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index a7e1b56665..e03103d4cb 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -85,7 +85,7 @@ def __init__(self, config: BaseConfig, *args): self.cz = factory.committer_factory(self.config) self.project_info = ProjectInfo() - def __call__(self): + def __call__(self) -> None: if self.config.path: out.line(f"Config file {self.config.path} already exists") return @@ -120,7 +120,7 @@ def __call__(self): self.config = JsonConfig(data="{}", path=config_path) elif "yaml" in config_path: self.config = YAMLConfig(data="", path=config_path) - values_to_add = {} + values_to_add: dict[str, Any] = {} values_to_add["name"] = cz_name values_to_add["tag_format"] = tag_format values_to_add["version_scheme"] = version_scheme diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 587b72aee6..2c7bce206d 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -41,3 +41,6 @@ def update(self, data: Settings) -> None: def _parse_setting(self, data: bytes | str) -> None: raise NotImplementedError() + + def init_empty_config_content(self) -> None: + raise NotImplementedError() diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 2af7bec121..41da985704 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1692,7 +1692,7 @@ def test_bump_warn_but_dont_fail_on_invalid_tags( def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): - """Test the is_initial_tag method behavior.""" + """Test the _is_initial_tag method behavior.""" # Create a commit but no tags create_file_and_commit("feat: initial commit") @@ -1725,15 +1725,15 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): # Test case 1: No current tag, not yes mode mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True)) - assert bump_cmd.is_initial_tag(None, is_yes=False) is True + assert bump_cmd._is_initial_tag(None, is_yes=False) is True # Test case 2: No current tag, yes mode - assert bump_cmd.is_initial_tag(None, is_yes=True) is True + assert bump_cmd._is_initial_tag(None, is_yes=True) is True # Test case 3: Has current tag mock_tag = mocker.Mock() - assert bump_cmd.is_initial_tag(mock_tag, is_yes=False) is False + assert bump_cmd._is_initial_tag(mock_tag, is_yes=False) is False # Test case 4: No current tag, user denies mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False)) - assert bump_cmd.is_initial_tag(None, is_yes=False) is False + assert bump_cmd._is_initial_tag(None, is_yes=False) is False From c62aac03ee55c8d616ba078357dbbfe973cf5567 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 14:30:57 +0800 Subject: [PATCH 091/275] style: type untyped methods --- commitizen/changelog.py | 6 +++--- commitizen/commands/changelog.py | 2 +- commitizen/commands/example.py | 4 ++-- commitizen/commands/init.py | 2 +- commitizen/commands/schema.py | 4 ++-- commitizen/config/base_config.py | 12 +++++++++++- commitizen/config/json_config.py | 14 ++++++++++++-- commitizen/config/toml_config.py | 18 ++++++++++++++---- commitizen/config/yaml_config.py | 14 ++++++++++++-- commitizen/cz/base.py | 4 ++-- commitizen/git.py | 4 ++-- commitizen/hooks.py | 5 +++-- commitizen/providers/base_provider.py | 10 +++++----- commitizen/providers/cargo_provider.py | 2 +- commitizen/providers/commitizen_provider.py | 2 +- commitizen/providers/poetry_provider.py | 2 +- commitizen/providers/scm_provider.py | 2 +- 17 files changed, 74 insertions(+), 33 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 6526b4c540..ba6fbbc6b3 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -63,7 +63,7 @@ class Metadata: latest_version_position: int | None = None latest_version_tag: str | None = None - def __post_init__(self): + def __post_init__(self) -> None: if self.latest_version and not self.latest_version_tag: # Test syntactic sugar # latest version tag is optional if same as latest version @@ -169,8 +169,8 @@ def process_commit_message( commit: GitCommit, changes: dict[str | None, list], change_type_map: dict[str, str] | None = None, -): - message: dict = { +) -> None: + message: dict[str, Any] = { "sha1": commit.rev, "parents": commit.parents, "author": commit.author, diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 11f72cba18..5a1270dfd8 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -138,7 +138,7 @@ def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> def _write_changelog( self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata - ): + ) -> None: with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file: partial_changelog: str | None = None if self.incremental: diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py index a28ad85f16..818e217530 100644 --- a/commitizen/commands/example.py +++ b/commitizen/commands/example.py @@ -5,9 +5,9 @@ class Example: """Show an example so people understands the rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object): self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.example()) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index e03103d4cb..ca6426b0bd 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -79,7 +79,7 @@ def is_pre_commit_installed(self) -> bool: class Init: - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object): self.config: BaseConfig = config self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py index 4af5679cf5..5c77898d8a 100644 --- a/commitizen/commands/schema.py +++ b/commitizen/commands/schema.py @@ -5,9 +5,9 @@ class Schema: """Show structure of the rule.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object): self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.schema()) diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 2c7bce206d..10a26e3f0c 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -1,9 +1,19 @@ from __future__ import annotations from pathlib import Path +from typing import TYPE_CHECKING, Any from commitizen.defaults import DEFAULT_SETTINGS, Settings +if TYPE_CHECKING: + import sys + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class BaseConfig: def __init__(self) -> None: @@ -28,7 +38,7 @@ def path(self, path: str | Path) -> None: """ self._path = Path(path) - def set_key(self, key, value): + def set_key(self, key: str, value: Any) -> Self: """Set or update a key in the conf. For now only strings are supported. diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 28fac25662..83e0a928a0 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -2,12 +2,22 @@ import json from pathlib import Path +from typing import TYPE_CHECKING, Any from commitizen.exceptions import InvalidConfigurationError from commitizen.git import smart_open from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class JsonConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): @@ -16,11 +26,11 @@ def __init__(self, *, data: bytes | str, path: Path | str): self.path = path self._parse_setting(data) - def init_empty_config_content(self): + def init_empty_config_content(self) -> None: with smart_open(self.path, "a", encoding=self.encoding) as json_file: json.dump({"commitizen": {}}, json_file) - def set_key(self, key, value): + def set_key(self, key: str, value: Any) -> Self: """Set or update a key in the conf. For now only strings are supported. diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index a7050214ba..9bab994fd5 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -2,6 +2,7 @@ import os from pathlib import Path +from typing import TYPE_CHECKING, Any from tomlkit import exceptions, parse, table @@ -9,6 +10,15 @@ from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class TomlConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): @@ -17,7 +27,7 @@ def __init__(self, *, data: bytes | str, path: Path | str): self.path = path self._parse_setting(data) - def init_empty_config_content(self): + def init_empty_config_content(self) -> None: if os.path.isfile(self.path): with open(self.path, "rb") as input_toml_file: parser = parse(input_toml_file.read()) @@ -27,10 +37,10 @@ def init_empty_config_content(self): with open(self.path, "wb") as output_toml_file: if parser.get("tool") is None: parser["tool"] = table() - parser["tool"]["commitizen"] = table() + parser["tool"]["commitizen"] = table() # type: ignore output_toml_file.write(parser.as_string().encode(self.encoding)) - def set_key(self, key, value): + def set_key(self, key: str, value: Any) -> Self: """Set or update a key in the conf. For now only strings are supported. @@ -39,7 +49,7 @@ def set_key(self, key, value): with open(self.path, "rb") as f: parser = parse(f.read()) - parser["tool"]["commitizen"][key] = value + parser["tool"]["commitizen"][key] = value # type: ignore with open(self.path, "wb") as f: f.write(parser.as_string().encode(self.encoding)) return self diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index 2c384cf17d..8eac9bb785 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from typing import TYPE_CHECKING, Any import yaml @@ -9,6 +10,15 @@ from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class YAMLConfig(BaseConfig): def __init__(self, *, data: bytes | str, path: Path | str): @@ -17,7 +27,7 @@ def __init__(self, *, data: bytes | str, path: Path | str): self.path = path self._parse_setting(data) - def init_empty_config_content(self): + def init_empty_config_content(self) -> None: with smart_open(self.path, "a", encoding=self.encoding) as json_file: yaml.dump({"commitizen": {}}, json_file, explicit_start=True) @@ -41,7 +51,7 @@ def _parse_setting(self, data: bytes | str) -> None: except (KeyError, TypeError): self.is_empty_config = True - def set_key(self, key, value): + def set_key(self, key: str, value: Any) -> Self: """Set or update a key in the conf. For now only strings are supported. diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 7463a67c24..9e803c3d01 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -76,13 +76,13 @@ def message(self, answers: dict) -> str: """Format your git message.""" @property - def style(self): + def style(self) -> Style: return merge_styles( [ Style(BaseCommitizen.default_style_config), Style(self.config.settings["style"]), ] - ) + ) # type: ignore[return-value] def example(self) -> str: """Example of the commit message.""" diff --git a/commitizen/git.py b/commitizen/git.py index 48fa13ec52..54d57f69bb 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -215,7 +215,7 @@ def get_commits( ] -def get_filenames_in_commit(git_reference: str = ""): +def get_filenames_in_commit(git_reference: str = "") -> list[str]: """Get the list of files that were committed in the requested git reference. :param git_reference: a git reference as accepted by `git show`, default: the last commit @@ -312,7 +312,7 @@ def get_core_editor() -> str | None: return None -def smart_open(*args, **kwargs): +def smart_open(*args, **kwargs): # type: ignore[no-untyped-def,unused-ignore] """Open a file with the EOL style determined from Git.""" return open(*args, newline=EOLType.for_open(), **kwargs) diff --git a/commitizen/hooks.py b/commitizen/hooks.py index f5505d0e82..f60bd9b43e 100644 --- a/commitizen/hooks.py +++ b/commitizen/hooks.py @@ -1,12 +1,13 @@ from __future__ import annotations import os +from collections.abc import Mapping from commitizen import cmd, out from commitizen.exceptions import RunHookError -def run(hooks, _env_prefix="CZ_", **env): +def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None: if isinstance(hooks, str): hooks = [hooks] @@ -24,7 +25,7 @@ def run(hooks, _env_prefix="CZ_", **env): raise RunHookError(f"Running hook '{hook}' failed") -def _format_env(prefix: str, env: dict[str, str]) -> dict[str, str]: +def _format_env(prefix: str, env: Mapping[str, object]) -> dict[str, str]: """_format_env() prefixes all given environment variables with the given prefix so it can be passed directly to cmd.run().""" penv = dict(os.environ) diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 34048318e2..9c1e0f7a98 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -29,7 +29,7 @@ def get_version(self) -> str: """ @abstractmethod - def set_version(self, version: str): + def set_version(self, version: str) -> None: """ Set the new current version """ @@ -58,7 +58,7 @@ def get_version(self) -> str: document = json.loads(self.file.read_text()) return self.get(document) - def set_version(self, version: str): + def set_version(self, version: str) -> None: document = json.loads(self.file.read_text()) self.set(document, version) self.file.write_text(json.dumps(document, indent=self.indent) + "\n") @@ -66,7 +66,7 @@ def set_version(self, version: str): def get(self, document: dict[str, Any]) -> str: return document["version"] # type: ignore - def set(self, document: dict[str, Any], version: str): + def set(self, document: dict[str, Any], version: str) -> None: document["version"] = version @@ -79,7 +79,7 @@ def get_version(self) -> str: document = tomlkit.parse(self.file.read_text()) return self.get(document) - def set_version(self, version: str): + def set_version(self, version: str) -> None: document = tomlkit.parse(self.file.read_text()) self.set(document, version) self.file.write_text(tomlkit.dumps(document)) @@ -87,5 +87,5 @@ def set_version(self, version: str): def get(self, document: tomlkit.TOMLDocument) -> str: return document["project"]["version"] # type: ignore - def set(self, document: tomlkit.TOMLDocument, version: str): + def set(self, document: tomlkit.TOMLDocument, version: str) -> None: document["project"]["version"] = version # type: ignore diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 2e73ff35a1..87e45cd71c 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -28,7 +28,7 @@ def get(self, document: tomlkit.TOMLDocument) -> str: ... return document["workspace"]["package"]["version"] # type: ignore - def set(self, document: tomlkit.TOMLDocument, version: str): + def set(self, document: tomlkit.TOMLDocument, version: str) -> None: try: document["workspace"]["package"]["version"] = version # type: ignore return diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py index a1da25ff72..7ce177a604 100644 --- a/commitizen/providers/commitizen_provider.py +++ b/commitizen/providers/commitizen_provider.py @@ -11,5 +11,5 @@ class CommitizenProvider(VersionProvider): def get_version(self) -> str: return self.config.settings["version"] # type: ignore - def set_version(self, version: str): + def set_version(self, version: str) -> None: self.config.set_key("version", version) diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py index 7aa28f56d9..1dd33f053e 100644 --- a/commitizen/providers/poetry_provider.py +++ b/commitizen/providers/poetry_provider.py @@ -15,5 +15,5 @@ class PoetryProvider(TomlProvider): def get(self, pyproject: tomlkit.TOMLDocument) -> str: return pyproject["tool"]["poetry"]["version"] # type: ignore - def set(self, pyproject: tomlkit.TOMLDocument, version: str): + def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None: pyproject["tool"]["poetry"]["version"] = version # type: ignore diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py index cb575148cb..3085b16efa 100644 --- a/commitizen/providers/scm_provider.py +++ b/commitizen/providers/scm_provider.py @@ -23,6 +23,6 @@ def get_version(self) -> str: return "0.0.0" return str(versions[-1]) - def set_version(self, version: str): + def set_version(self, version: str) -> None: # Not necessary pass From 2e65d17cd086443f9f848755dd6d673052bb3189 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 18:48:20 +0800 Subject: [PATCH 092/275] style: remove Union --- commitizen/cmd.py | 6 ++++-- commitizen/cz/utils.py | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/commitizen/cmd.py b/commitizen/cmd.py index 9d6b2f6d2a..3f13087233 100644 --- a/commitizen/cmd.py +++ b/commitizen/cmd.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import os import subprocess from collections.abc import Mapping -from typing import NamedTuple, Union +from typing import NamedTuple from charset_normalizer import from_bytes @@ -29,7 +31,7 @@ def _try_decode(bytes_: bytes) -> str: raise CharacterSetDecodeError() from e -def run(cmd: str, env: Union[Mapping[str, str], None] = None) -> Command: +def run(cmd: str, env: Mapping[str, str] | None = None) -> Command: if env is not None: env = {**os.environ, **env} process = subprocess.Popen( diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index 430bda2d43..a6f687226c 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -1,7 +1,6 @@ import os import re import tempfile -from typing import Union from commitizen import git from commitizen.cz import exceptions @@ -9,7 +8,7 @@ _RE_LOCAL_VERSION = re.compile(r"\+.+") -def required_validator(answer: str, msg: Union[str, None] = None) -> str: +def required_validator(answer: str, msg: object = None) -> str: if not answer: raise exceptions.AnswerRequiredError(msg) return answer From 59531a6482330d96ebe58fc7eb286a53b52eca74 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 19:00:05 +0800 Subject: [PATCH 093/275] style: better type --- commitizen/cli.py | 4 ++-- commitizen/providers/base_provider.py | 5 +++-- commitizen/providers/npm_provider.py | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 9998691f41..fa05fb2f73 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -8,7 +8,7 @@ from functools import partial from pathlib import Path from types import TracebackType -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, cast import argcomplete from decli import cli @@ -48,7 +48,7 @@ def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - kwarg: str | Sequence[Any] | None, + kwarg: str | Sequence[object] | None, option_string: str | None = None, ) -> None: if not isinstance(kwarg, str): diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 9c1e0f7a98..7a7e1205b4 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -2,6 +2,7 @@ import json from abc import ABC, abstractmethod +from collections.abc import Mapping from pathlib import Path from typing import Any, ClassVar @@ -63,8 +64,8 @@ def set_version(self, version: str) -> None: self.set(document, version) self.file.write_text(json.dumps(document, indent=self.indent) + "\n") - def get(self, document: dict[str, Any]) -> str: - return document["version"] # type: ignore + def get(self, document: Mapping[str, str]) -> str: + return document["version"] def set(self, document: dict[str, Any], version: str) -> None: document["version"] = version diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py index 12900ff7de..3125447250 100644 --- a/commitizen/providers/npm_provider.py +++ b/commitizen/providers/npm_provider.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from collections.abc import Mapping from pathlib import Path from typing import Any, ClassVar @@ -58,8 +59,8 @@ def set_version(self, version: str) -> None: json.dumps(shrinkwrap_document, indent=self.indent) + "\n" ) - def get_package_version(self, document: dict[str, Any]) -> str: - return document["version"] # type: ignore + def get_package_version(self, document: Mapping[str, str]) -> str: + return document["version"] def set_package_version( self, document: dict[str, Any], version: str From e86d80ead8b1684c389f8ef76edb2723ee339e20 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 22:12:30 +0800 Subject: [PATCH 094/275] style: add `-> None` to __init__ --- commitizen/changelog_formats/__init__.py | 2 +- commitizen/changelog_formats/base.py | 2 +- commitizen/commands/bump.py | 2 +- commitizen/commands/changelog.py | 2 +- commitizen/commands/check.py | 2 +- commitizen/commands/commit.py | 2 +- commitizen/commands/example.py | 2 +- commitizen/commands/info.py | 2 +- commitizen/commands/init.py | 2 +- commitizen/commands/list_cz.py | 2 +- commitizen/commands/schema.py | 2 +- commitizen/commands/version.py | 2 +- commitizen/config/json_config.py | 2 +- commitizen/config/toml_config.py | 2 +- commitizen/config/yaml_config.py | 2 +- commitizen/cz/customize/customize.py | 2 +- commitizen/exceptions.py | 4 ++-- commitizen/git.py | 4 ++-- commitizen/providers/base_provider.py | 2 +- commitizen/version_schemes.py | 2 +- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py index 782bfb24cb..b7b3cac01d 100644 --- a/commitizen/changelog_formats/__init__.py +++ b/commitizen/changelog_formats/__init__.py @@ -25,7 +25,7 @@ class ChangelogFormat(Protocol): config: BaseConfig - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: self.config = config @property diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index f69cf8f00f..cb5d385bf8 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -20,7 +20,7 @@ class BaseFormat(ChangelogFormat, metaclass=ABCMeta): extension: ClassVar[str] = "" alternative_extensions: ClassVar[set[str]] = set() - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: # Constructor needs to be redefined because `Protocol` prevent instantiation by default # See: https://bugs.python.org/issue44807 self.config = config diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 0607c7ef73..0aee74a817 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -40,7 +40,7 @@ class Bump: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict): + def __init__(self, config: BaseConfig, arguments: dict) -> None: if not git.is_git_project(): raise NotAGitProjectError() diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 5a1270dfd8..59d799b037 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -29,7 +29,7 @@ class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args: Mapping[str, Any]): + def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None: if not git.is_git_project(): raise NotAGitProjectError() diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 6cf91bb926..82a8039fa3 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -19,7 +19,7 @@ class Check: def __init__( self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd() - ): + ) -> None: """Initial check command. Args: diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 22ad642c43..68095d0e0c 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -31,7 +31,7 @@ class Commit: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict): + def __init__(self, config: BaseConfig, arguments: dict) -> None: if not git.is_git_project(): raise NotAGitProjectError() diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py index 818e217530..ba9f34adc4 100644 --- a/commitizen/commands/example.py +++ b/commitizen/commands/example.py @@ -5,7 +5,7 @@ class Example: """Show an example so people understands the rules.""" - def __init__(self, config: BaseConfig, *args: object): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py index 5b649ceb5d..5ea8227313 100644 --- a/commitizen/commands/info.py +++ b/commitizen/commands/info.py @@ -5,7 +5,7 @@ class Info: """Show in depth explanation of your rules.""" - def __init__(self, config: BaseConfig, *args: object): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index ca6426b0bd..1207cd02ee 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -79,7 +79,7 @@ def is_pre_commit_installed(self) -> bool: class Init: - def __init__(self, config: BaseConfig, *args: object): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py index ff4e12ad44..412266f6c3 100644 --- a/commitizen/commands/list_cz.py +++ b/commitizen/commands/list_cz.py @@ -6,7 +6,7 @@ class ListCz: """List currently installed rules.""" - def __init__(self, config: BaseConfig, *args: object): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config def __call__(self) -> None: diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py index 5c77898d8a..a7aeb53569 100644 --- a/commitizen/commands/schema.py +++ b/commitizen/commands/schema.py @@ -5,7 +5,7 @@ class Schema: """Show structure of the rule.""" - def __init__(self, config: BaseConfig, *args: object): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 55d0a950a3..ecf1f03129 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -12,7 +12,7 @@ class Version: """Get the version of the installed commitizen or the current project.""" - def __init__(self, config: BaseConfig, *args: Mapping[str, Any]): + def __init__(self, config: BaseConfig, *args: Mapping[str, Any]) -> None: self.config: BaseConfig = config self.parameter = args[0] self.operating_system = platform.system() diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 83e0a928a0..be1f1c36b0 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -20,7 +20,7 @@ class JsonConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path | str) -> None: super().__init__() self.is_empty_config = False self.path = path diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 9bab994fd5..3571c9c882 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -21,7 +21,7 @@ class TomlConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path | str) -> None: super().__init__() self.is_empty_config = False self.path = path diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index 8eac9bb785..f2a79e6937 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -21,7 +21,7 @@ class YAMLConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path | str) -> None: super().__init__() self.is_empty_config = False self.path = path diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index 53ada4b2c0..8f844501ec 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -26,7 +26,7 @@ class CustomizeCommitsCz(BaseCommitizen): bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO change_type_order = defaults.CHANGE_TYPE_ORDER - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: super().__init__(config) if "customize" not in self.config.settings: diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 87efabe2ab..8c0956be53 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -41,7 +41,7 @@ class ExitCode(enum.IntEnum): class CommitizenException(Exception): - def __init__(self, *args: str, **kwargs: Any): + def __init__(self, *args: str, **kwargs: Any) -> None: self.output_method = kwargs.get("output_method") or out.error self.exit_code: ExitCode = self.__class__.exit_code if args: @@ -58,7 +58,7 @@ def __str__(self) -> str: class ExpectedExit(CommitizenException): exit_code = ExitCode.EXPECTED_EXIT - def __init__(self, *args: str, **kwargs: Any): + def __init__(self, *args: str, **kwargs: Any) -> None: output_method = kwargs.get("output_method") or out.write kwargs["output_method"] = output_method super().__init__(*args, **kwargs) diff --git a/commitizen/git.py b/commitizen/git.py index 54d57f69bb..b8528e3f77 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -59,7 +59,7 @@ def __init__( author: str = "", author_email: str = "", parents: list[str] | None = None, - ): + ) -> None: self.rev = rev.strip() self.title = title.strip() self.body = body.strip() @@ -132,7 +132,7 @@ def __repr__(self) -> str: class GitTag(GitObject): - def __init__(self, name: str, rev: str, date: str): + def __init__(self, name: str, rev: str, date: str) -> None: self.rev = rev.strip() self.name = name.strip() self._date = date.strip() diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 7a7e1205b4..27c3123416 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -20,7 +20,7 @@ class VersionProvider(ABC): config: BaseConfig - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: self.config = config @abstractmethod diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 96a68b05fd..a59d3c0aa0 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -51,7 +51,7 @@ class VersionProtocol(Protocol): parser: ClassVar[re.Pattern] """Regex capturing this version scheme into a `version` group""" - def __init__(self, version: str): + def __init__(self, version: str) -> None: """ Initialize a version object from its string representation. From 44123c41b8afb58b67d889fd25b90d935f14399a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 22:35:44 +0800 Subject: [PATCH 095/275] build(ruff,mypy): more strict rules --- commitizen/git.py | 2 +- pyproject.toml | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index b8528e3f77..fb59750eaf 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -312,7 +312,7 @@ def get_core_editor() -> str | None: return None -def smart_open(*args, **kwargs): # type: ignore[no-untyped-def,unused-ignore] +def smart_open(*args, **kwargs): # type: ignore[no-untyped-def,unused-ignore] # noqa: ANN201 """Open a file with the EOL style determined from Git.""" return open(*args, newline=EOLType.for_open(), **kwargs) diff --git a/pyproject.toml b/pyproject.toml index 757c72bc0a..9961a2f409 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,6 +187,9 @@ line-length = 88 [tool.ruff.lint] select = [ + # flake8-annotations + "ANN001", + "ANN2", # pycodestyle "E", # Pyflakes @@ -202,6 +205,9 @@ select = [ ] ignore = ["E501", "D1", "D415"] +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["ANN"] + [tool.ruff.lint.isort] known-first-party = ["commitizen", "tests"] @@ -209,7 +215,8 @@ known-first-party = ["commitizen", "tests"] convention = "google" [tool.mypy] -files = "commitizen" +files = ["commitizen", "tests"] +disallow_untyped_defs = true disallow_untyped_decorators = true disallow_subclassing_any = true warn_return_any = true @@ -217,6 +224,10 @@ warn_redundant_casts = true warn_unused_ignores = true warn_unused_configs = true +[[tool.mypy.overrides]] +module = "tests/*" +disallow_untyped_defs = false + [[tool.mypy.overrides]] module = "py.*" # Legacy pytest dependencies ignore_missing_imports = true @@ -233,14 +244,14 @@ poetry_command = "" [tool.poe.tasks] format.help = "Format the code" format.sequence = [ - { cmd = "ruff check --fix commitizen tests" }, - { cmd = "ruff format commitizen tests" }, + { cmd = "ruff check --fix" }, + { cmd = "ruff format" }, ] lint.help = "Lint the code" lint.sequence = [ - { cmd = "ruff check commitizen/ tests/ --fix" }, - { cmd = "mypy commitizen/ tests/" }, + { cmd = "ruff check" }, + { cmd = "mypy" }, ] check-commit.help = "Check the commit message" From 20dda31eb1da62eab4839dcfc910d3130f7dfe7b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 31 May 2025 18:20:41 +0800 Subject: [PATCH 096/275] fix(BaseConfig): mypy error --- commitizen/config/base_config.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 10a26e3f0c..59c0d16a06 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -26,16 +26,11 @@ def settings(self) -> Settings: return self._settings @property - def path(self) -> Path | None: - return self._path + def path(self) -> Path: + return self._path # type: ignore @path.setter def path(self, path: str | Path) -> None: - """ - mypy does not like this until 1.16 - See https://github.com/python/mypy/pull/18510 - TODO: remove "type: ignore" from the call sites when 1.16 is available - """ self._path = Path(path) def set_key(self, key: str, value: Any) -> Self: From 001fe8d2711f50fa093cb0f9e230856da660c316 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 31 May 2025 22:04:10 +0800 Subject: [PATCH 097/275] build(mypy): remove disallow_untyped_defs because it's already done by ruff rules --- pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9961a2f409..ee0ecc9ced 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -216,7 +216,6 @@ convention = "google" [tool.mypy] files = ["commitizen", "tests"] -disallow_untyped_defs = true disallow_untyped_decorators = true disallow_subclassing_any = true warn_return_any = true @@ -224,10 +223,6 @@ warn_redundant_casts = true warn_unused_ignores = true warn_unused_configs = true -[[tool.mypy.overrides]] -module = "tests/*" -disallow_untyped_defs = false - [[tool.mypy.overrides]] module = "py.*" # Legacy pytest dependencies ignore_missing_imports = true From f66e577442305255b7f2fdc9ce4e26c742921538 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 31 May 2025 00:45:15 +0800 Subject: [PATCH 098/275] refactor(bump): TypedDict for bump argument --- commitizen/cli.py | 2 +- commitizen/commands/bump.py | 129 ++++++++++++++++------------ commitizen/defaults.py | 38 ++++---- tests/commands/test_bump_command.py | 2 +- 4 files changed, 97 insertions(+), 74 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index fa05fb2f73..e1bd57b523 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -680,7 +680,7 @@ def main() -> None: ) sys.excepthook = no_raise_debug_excepthook - args.func(conf, arguments)() + args.func(conf, arguments)() # type: ignore if __name__ == "__main__": diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 0aee74a817..beed384b78 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -37,37 +37,65 @@ logger = getLogger("commitizen") +class BumpArguments(Settings, total=False): + allow_no_commit: bool | None + annotated_tag_message: str | None + annotated_tag: bool + build_metadata: str | None + changelog_to_stdout: bool + changelog: bool + check_consistency: bool + devrelease: int | None + dry_run: bool + file_name: str + files_only: bool | None + get_next: bool + git_output_to_stderr: bool + gpg_sign: bool + increment_mode: str + increment: Increment | None + local_version: bool + manual_version: str | None + no_verify: bool + prerelease: Prerelease | None + retry: bool + yes: bool + + class Bump: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict) -> None: + def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None: if not git.is_git_project(): raise NotAGitProjectError() self.config: BaseConfig = config self.encoding = config.settings["encoding"] - self.arguments: dict = arguments - self.bump_settings: dict = { - **config.settings, - **{ - key: arguments[key] - for key in [ - "tag_format", - "prerelease", - "increment", - "increment_mode", - "bump_message", - "gpg_sign", - "annotated_tag", - "annotated_tag_message", - "major_version_zero", - "prerelease_offset", - "template", - "file_name", - ] - if arguments.get(key) is not None + self.arguments = arguments + self.bump_settings = cast( + BumpArguments, + { + **config.settings, + **{ + k: v + for k, v in { + "annotated_tag_message": arguments.get("annotated_tag_message"), + "annotated_tag": arguments.get("annotated_tag"), + "bump_message": arguments.get("bump_message"), + "file_name": arguments.get("file_name"), + "gpg_sign": arguments.get("gpg_sign"), + "increment_mode": arguments.get("increment_mode"), + "increment": arguments.get("increment"), + "major_version_zero": arguments.get("major_version_zero"), + "prerelease_offset": arguments.get("prerelease_offset"), + "prerelease": arguments.get("prerelease"), + "tag_format": arguments.get("tag_format"), + "template": arguments.get("template"), + }.items() + if v is not None + }, }, - } + ) self.cz = factory.committer_factory(self.config) self.changelog_flag = arguments["changelog"] self.changelog_config = self.config.settings.get("update_changelog_on_bump") @@ -120,11 +148,10 @@ def _is_initial_tag( def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None: # Update the bump map to ensure major version doesn't increment. - is_major_version_zero: bool = self.bump_settings["major_version_zero"] # self.cz.bump_map = defaults.bump_map_major_version_zero bump_map = ( self.cz.bump_map_major_version_zero - if is_major_version_zero + if self.bump_settings["major_version_zero"] else self.cz.bump_map ) bump_pattern = self.cz.bump_pattern @@ -144,23 +171,14 @@ def __call__(self) -> None: except TypeError: raise NoVersionSpecifiedError() - bump_commit_message: str | None = self.bump_settings["bump_message"] - version_files: list[str] = self.bump_settings["version_files"] - major_version_zero: bool = self.bump_settings["major_version_zero"] - prerelease_offset: int = self.bump_settings["prerelease_offset"] - - dry_run: bool = self.arguments["dry_run"] - is_yes: bool = self.arguments["yes"] - increment: Increment | None = self.arguments["increment"] - prerelease: Prerelease | None = self.arguments["prerelease"] - devrelease: int | None = self.arguments["devrelease"] - is_files_only: bool | None = self.arguments["files_only"] - is_local_version: bool = self.arguments["local_version"] + increment = self.arguments["increment"] + prerelease = self.arguments["prerelease"] + devrelease = self.arguments["devrelease"] + is_local_version = self.arguments["local_version"] manual_version = self.arguments["manual_version"] build_metadata = self.arguments["build_metadata"] - increment_mode: str = self.arguments["increment_mode"] - get_next: bool = self.arguments["get_next"] - allow_no_commit: bool | None = self.arguments["allow_no_commit"] + get_next = self.arguments["get_next"] + allow_no_commit = self.arguments["allow_no_commit"] if manual_version: if increment: @@ -182,7 +200,7 @@ def __call__(self) -> None: "--build-metadata cannot be combined with MANUAL_VERSION" ) - if major_version_zero: + if self.bump_settings["major_version_zero"]: raise NotAllowed( "--major-version-zero cannot be combined with MANUAL_VERSION" ) @@ -190,7 +208,7 @@ def __call__(self) -> None: if get_next: raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION") - if major_version_zero: + if self.bump_settings["major_version_zero"]: if not current_version.release[0] == 0: raise NotAllowed( f"--major-version-zero is meaningless for current version {current_version}" @@ -215,7 +233,7 @@ def __call__(self) -> None: else: # If user specified changelog_to_stdout, they probably want the # changelog to be generated as well, this is the most intuitive solution - self.changelog_flag = ( + self.changelog_flag = bool( self.changelog_flag or bool(self.changelog_to_stdout) or self.changelog_config @@ -227,7 +245,7 @@ def __call__(self) -> None: current_tag, "name", rules.normalize_tag(current_version) ) - is_initial = self._is_initial_tag(current_tag, is_yes) + is_initial = self._is_initial_tag(current_tag, self.arguments["yes"]) if manual_version: try: @@ -273,16 +291,16 @@ def __call__(self) -> None: new_version = current_version.bump( increment, prerelease=prerelease, - prerelease_offset=prerelease_offset, + prerelease_offset=self.bump_settings["prerelease_offset"], devrelease=devrelease, is_local_version=is_local_version, build_metadata=build_metadata, - exact_increment=increment_mode == "exact", + exact_increment=self.arguments["increment_mode"] == "exact", ) new_tag_version = rules.normalize_tag(new_version) message = bump.create_commit_message( - current_version, new_version, bump_commit_message + current_version, new_version, self.bump_settings["bump_message"] ) if get_next: @@ -314,6 +332,7 @@ def __call__(self) -> None: ) files: list[str] = [] + dry_run = self.arguments["dry_run"] if self.changelog_flag: args = { "unreleased_version": new_tag_version, @@ -342,7 +361,7 @@ def __call__(self) -> None: bump.update_version_in_files( str(current_version), str(new_version), - version_files, + self.bump_settings["version_files"], check_consistency=self.check_consistency, encoding=self.encoding, ) @@ -366,7 +385,7 @@ def __call__(self) -> None: else None, ) - if is_files_only: + if self.arguments["files_only"]: raise ExpectedExit() # FIXME: check if any changes have been staged @@ -395,11 +414,15 @@ def __call__(self) -> None: c = git.tag( new_tag_version, - signed=self.bump_settings.get("gpg_sign", False) - or bool(self.config.settings.get("gpg_sign", False)), - annotated=self.bump_settings.get("annotated_tag", False) - or bool(self.config.settings.get("annotated_tag", False)) - or bool(self.bump_settings.get("annotated_tag_message", False)), + signed=bool( + self.bump_settings.get("gpg_sign") + or self.config.settings.get("gpg_sign") + ), + annotated=bool( + self.bump_settings.get("annotated_tag") + or self.config.settings.get("annotated_tag") + or self.bump_settings.get("annotated_tag_message") + ), msg=self.bump_settings.get("annotated_tag_message", None), # TODO: also get from self.config.settings? ) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index adbb9e6d1a..4ad7ae4d8f 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -29,36 +29,36 @@ class CzSettings(TypedDict, total=False): class Settings(TypedDict, total=False): - name: str - version: str | None - version_files: list[str] - version_provider: str | None - version_scheme: str | None - version_type: str | None - tag_format: str - legacy_tag_formats: Sequence[str] - ignored_tag_formats: Sequence[str] - bump_message: str | None - retry_after_failure: bool allow_abort: bool allowed_prefixes: list[str] + always_signoff: bool + bump_message: str | None changelog_file: str changelog_format: str | None changelog_incremental: bool - changelog_start_rev: str | None changelog_merge_prerelease: bool - update_changelog_on_bump: bool - use_shortcuts: bool - style: list[tuple[str, str]] + changelog_start_rev: str | None customize: CzSettings + encoding: str + extras: dict[str, Any] + ignored_tag_formats: Sequence[str] + legacy_tag_formats: Sequence[str] major_version_zero: bool - pre_bump_hooks: list[str] | None + name: str post_bump_hooks: list[str] | None + pre_bump_hooks: list[str] | None prerelease_offset: int - encoding: str - always_signoff: bool + retry_after_failure: bool + style: list[tuple[str, str]] + tag_format: str template: str | None - extras: dict[str, Any] + update_changelog_on_bump: bool + use_shortcuts: bool + version_files: list[str] + version_provider: str | None + version_scheme: str | None + version_type: str | None + version: str | None CONFIG_FILES: list[str] = [ diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 41da985704..64b810e4de 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1721,7 +1721,7 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): "extras": None, } - bump_cmd = bump.Bump(config, arguments) + bump_cmd = bump.Bump(config, arguments) # type: ignore # Test case 1: No current tag, not yes mode mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True)) From 8e88d8cf1766cc78c6975c7663480cc5752b64d5 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 00:57:10 +0800 Subject: [PATCH 099/275] refactor(changelog): type untyped arguments --- commitizen/commands/bump.py | 4 ++-- commitizen/commands/changelog.py | 38 +++++++++++++++++++++++--------- commitizen/defaults.py | 1 + 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index beed384b78..65827d406e 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -342,14 +342,14 @@ def __call__(self) -> None: "dry_run": dry_run, } if self.changelog_to_stdout: - changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) + changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) # type: ignore try: changelog_cmd() except DryRunExit: pass args["file_name"] = self.file_name - changelog_cmd = Changelog(self.config, args) + changelog_cmd = Changelog(self.config, args) # type: ignore changelog_cmd() files.append(changelog_cmd.file_name) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 59d799b037..6d150b03f9 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -2,11 +2,11 @@ import os import os.path -from collections.abc import Generator, Iterable, Mapping +from collections.abc import Generator, Iterable from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path -from typing import Any, cast +from typing import Any, TypedDict, cast from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format @@ -26,10 +26,28 @@ from commitizen.version_schemes import get_version_scheme +class ChangelogArgs(TypedDict, total=False): + change_type_map: dict[str, str] + change_type_order: list[str] + current_version: str + dry_run: bool + file_name: str + incremental: bool + merge_prerelease: bool + rev_range: str + start_rev: str + tag_format: str + unreleased_version: str | None + version_scheme: str + template: str + extras: dict[str, Any] + export_template: str + + class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None: + def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() @@ -59,17 +77,17 @@ def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None: self.changelog_format = get_changelog_format(self.config, self.file_name) - self.incremental = args["incremental"] or self.config.settings.get( - "changelog_incremental" + self.incremental = bool( + args.get("incremental") or self.config.settings.get("changelog_incremental") ) - self.dry_run = args["dry_run"] + self.dry_run = bool(args.get("dry_run")) self.scheme = get_version_scheme( self.config.settings, args.get("version_scheme") ) current_version = ( - args.get("current_version", config.settings.get("version")) or "" + args.get("current_version") or self.config.settings.get("version") or "" ) self.current_version = self.scheme(current_version) if current_version else None @@ -84,9 +102,7 @@ def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None: or defaults.CHANGE_TYPE_ORDER, ) self.rev_range = args.get("rev_range") - self.tag_format: str = ( - args.get("tag_format") or self.config.settings["tag_format"] - ) + self.tag_format = args.get("tag_format") or self.config.settings["tag_format"] self.tag_rules = TagRules( scheme=self.scheme, tag_format=self.tag_format, @@ -164,7 +180,7 @@ def __call__(self) -> None: start_rev = self.start_rev unreleased_version = self.unreleased_version changelog_meta = changelog.Metadata() - change_type_map: dict[str, str] | None = self.change_type_map # type: ignore + change_type_map: dict[str, str] | None = self.change_type_map changelog_message_builder_hook: MessageBuilderHook | None = ( self.cz.changelog_message_builder_hook ) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 4ad7ae4d8f..29c9f2a494 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -37,6 +37,7 @@ class Settings(TypedDict, total=False): changelog_format: str | None changelog_incremental: bool changelog_merge_prerelease: bool + change_type_map: dict[str, str] changelog_start_rev: str | None customize: CzSettings encoding: str From 49bb8547a3366a22ddd10973fad5623a5662c0a9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 00:59:21 +0800 Subject: [PATCH 100/275] refactor(check): remove unused argument --- commitizen/commands/check.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 82a8039fa3..a293b6f855 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import re import sys from typing import Any @@ -17,9 +16,7 @@ class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__( - self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd() - ) -> None: + def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None: """Initial check command. Args: From 296b1b6281ce843d99d9f9c76c51675683c086c4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:01:08 +0800 Subject: [PATCH 101/275] style(bump): rename class for consistency --- commitizen/commands/bump.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 65827d406e..4bad83f5d6 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -37,7 +37,7 @@ logger = getLogger("commitizen") -class BumpArguments(Settings, total=False): +class BumpArgs(Settings, total=False): allow_no_commit: bool | None annotated_tag_message: str | None annotated_tag: bool @@ -65,7 +65,7 @@ class BumpArguments(Settings, total=False): class Bump: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None: + def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() @@ -73,7 +73,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None: self.encoding = config.settings["encoding"] self.arguments = arguments self.bump_settings = cast( - BumpArguments, + BumpArgs, { **config.settings, **{ From 1ded688bf13fd13ab465b4236290ac0ed8aaea8b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:08:03 +0800 Subject: [PATCH 102/275] refactor(check): type CheckArgs arguments --- commitizen/commands/check.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index a293b6f855..ba0beeeb4e 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -2,7 +2,7 @@ import re import sys -from typing import Any +from typing import TypedDict from commitizen import factory, git, out from commitizen.config import BaseConfig @@ -13,10 +13,20 @@ ) +class CheckArgs(TypedDict, total=False): + commit_msg_file: str + commit_msg: str + rev_range: str + allow_abort: bool + message_length_limit: int + allowed_prefixes: list[str] + message: str + + class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None: + def __init__(self, config: BaseConfig, arguments: CheckArgs) -> None: """Initial check command. Args: @@ -24,16 +34,15 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None: arguments: All the flags provided by the user cwd: Current work directory """ - self.commit_msg_file: str | None = arguments.get("commit_msg_file") - self.commit_msg: str | None = arguments.get("message") - self.rev_range: str | None = arguments.get("rev_range") - self.allow_abort: bool = bool( + self.commit_msg_file = arguments.get("commit_msg_file") + self.commit_msg = arguments.get("message") + self.rev_range = arguments.get("rev_range") + self.allow_abort = bool( arguments.get("allow_abort", config.settings["allow_abort"]) ) - self.max_msg_length: int = arguments.get("message_length_limit", 0) + self.max_msg_length = arguments.get("message_length_limit", 0) # we need to distinguish between None and [], which is a valid value - allowed_prefixes = arguments.get("allowed_prefixes") self.allowed_prefixes: list[str] = ( allowed_prefixes From e32778afb257706c7bea839ec784d5fc5b9020b3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:16:52 +0800 Subject: [PATCH 103/275] refactor(commit): type commit args --- commitizen/commands/commit.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 68095d0e0c..43feb272ba 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -6,7 +6,7 @@ import subprocess import tempfile from pathlib import Path -from typing import Union, cast +from typing import TypedDict import questionary @@ -28,10 +28,22 @@ from commitizen.git import smart_open +class CommitArgs(TypedDict, total=False): + all: bool + dry_run: bool + edit: bool + extra_cli_args: str + message_length_limit: int + no_retry: bool + signoff: bool + write_message_to_file: Path | None + retry: bool + + class Commit: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict) -> None: + def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() @@ -69,7 +81,7 @@ def _prompt_commit_questions(self) -> str: message = cz.message(answers) message_len = len(message.partition("\n")[0].strip()) - message_length_limit: int = self.arguments.get("message_length_limit", 0) + message_length_limit = self.arguments.get("message_length_limit", 0) if 0 < message_length_limit < message_len: raise CommitMessageLengthExceededError( f"Length of commit message exceeds limit ({message_len}/{message_length_limit})" @@ -108,12 +120,10 @@ def _get_message(self) -> str: return self._prompt_commit_questions() def __call__(self) -> None: - extra_args = cast(str, self.arguments.get("extra_cli_args", "")) - dry_run = cast(bool, self.arguments.get("dry_run")) - write_message_to_file = cast( - Union[Path, None], self.arguments.get("write_message_to_file") - ) - signoff = cast(bool, self.arguments.get("signoff")) + extra_args = self.arguments.get("extra_cli_args", "") + dry_run = bool(self.arguments.get("dry_run")) + write_message_to_file = self.arguments.get("write_message_to_file") + signoff = bool(self.arguments.get("signoff")) if self.arguments.get("all"): git.add("-u") From c60a3b877336c119cbece2e923a1cacfc4dcb974 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:30:59 +0800 Subject: [PATCH 104/275] refactor(commands): remove unused args, type version command args --- commitizen/commands/version.py | 13 +++++++++---- tests/commands/test_version_command.py | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index ecf1f03129..6b0aa331ad 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -1,7 +1,6 @@ import platform import sys -from collections.abc import Mapping -from typing import Any +from typing import TypedDict from commitizen import out from commitizen.__version__ import __version__ @@ -9,12 +8,18 @@ from commitizen.providers import get_provider +class VersionArgs(TypedDict, total=False): + report: bool + project: bool + verbose: bool + + class Version: """Get the version of the installed commitizen or the current project.""" - def __init__(self, config: BaseConfig, *args: Mapping[str, Any]) -> None: + def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None: self.config: BaseConfig = config - self.parameter = args[0] + self.parameter = arguments self.operating_system = platform.system() self.python_version = sys.version diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 927cf55f25..3dcbed168b 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -97,7 +97,6 @@ def test_version_use_version_provider( { "report": False, "project": project, - "commitizen": False, "verbose": not project, }, )() From 289198e938d28348d21dc4b52d0d93ab3e20ba40 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:38:22 +0800 Subject: [PATCH 105/275] style(cli): shorten arg type --- commitizen/cli.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index e1bd57b523..b5c03527d3 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -3,7 +3,6 @@ import argparse import logging import sys -from collections.abc import Sequence from copy import deepcopy from functools import partial from pathlib import Path @@ -48,7 +47,7 @@ def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - kwarg: str | Sequence[object] | None, + kwarg: object, option_string: str | None = None, ) -> None: if not isinstance(kwarg, str): From e9758e1dad46f514901851d68d624c3d73e50523 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:41:39 +0800 Subject: [PATCH 106/275] docs(bump): comment on a stupid looking pattern --- commitizen/commands/bump.py | 1 + 1 file changed, 1 insertion(+) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 4bad83f5d6..fa49d37d5e 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -79,6 +79,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: **{ k: v for k, v in { + # All these are for making mypy happy "annotated_tag_message": arguments.get("annotated_tag_message"), "annotated_tag": arguments.get("annotated_tag"), "bump_message": arguments.get("bump_message"), From b04ebdf70b2c9e04f69a9b36f893db12532c231d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:50:37 +0800 Subject: [PATCH 107/275] fix(Check): make parameters backward compatiable --- commitizen/commands/check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index ba0beeeb4e..8a7d0dd019 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -26,7 +26,7 @@ class CheckArgs(TypedDict, total=False): class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__(self, config: BaseConfig, arguments: CheckArgs) -> None: + def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> None: """Initial check command. Args: From b9a181cc70ab60c736a91cbf042a12cc9f12b30e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:52:12 +0800 Subject: [PATCH 108/275] style(changelog): rename parameter for consistency --- commitizen/commands/changelog.py | 33 ++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 6d150b03f9..f0dd1c516c 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -47,13 +47,13 @@ class ChangelogArgs(TypedDict, total=False): class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None: + def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() self.config = config - changelog_file_name = args.get("file_name") or self.config.settings.get( + changelog_file_name = arguments.get("file_name") or self.config.settings.get( "changelog_file" ) if not isinstance(changelog_file_name, str): @@ -71,27 +71,30 @@ def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None: self.encoding = self.config.settings["encoding"] self.cz = factory.committer_factory(self.config) - self.start_rev = args.get("start_rev") or self.config.settings.get( + self.start_rev = arguments.get("start_rev") or self.config.settings.get( "changelog_start_rev" ) self.changelog_format = get_changelog_format(self.config, self.file_name) self.incremental = bool( - args.get("incremental") or self.config.settings.get("changelog_incremental") + arguments.get("incremental") + or self.config.settings.get("changelog_incremental") ) - self.dry_run = bool(args.get("dry_run")) + self.dry_run = bool(arguments.get("dry_run")) self.scheme = get_version_scheme( - self.config.settings, args.get("version_scheme") + self.config.settings, arguments.get("version_scheme") ) current_version = ( - args.get("current_version") or self.config.settings.get("version") or "" + arguments.get("current_version") + or self.config.settings.get("version") + or "" ) self.current_version = self.scheme(current_version) if current_version else None - self.unreleased_version = args["unreleased_version"] + self.unreleased_version = arguments["unreleased_version"] self.change_type_map = ( self.config.settings.get("change_type_map") or self.cz.change_type_map ) @@ -101,24 +104,26 @@ def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None: or self.cz.change_type_order or defaults.CHANGE_TYPE_ORDER, ) - self.rev_range = args.get("rev_range") - self.tag_format = args.get("tag_format") or self.config.settings["tag_format"] + self.rev_range = arguments.get("rev_range") + self.tag_format = ( + arguments.get("tag_format") or self.config.settings["tag_format"] + ) self.tag_rules = TagRules( scheme=self.scheme, tag_format=self.tag_format, legacy_tag_formats=self.config.settings["legacy_tag_formats"], ignored_tag_formats=self.config.settings["ignored_tag_formats"], - merge_prereleases=args.get("merge_prerelease") + merge_prereleases=arguments.get("merge_prerelease") or self.config.settings["changelog_merge_prerelease"], ) self.template = ( - args.get("template") + arguments.get("template") or self.config.settings.get("template") or self.changelog_format.template ) - self.extras = args.get("extras") or {} - self.export_template_to = args.get("export_template") + self.extras = arguments.get("extras") or {} + self.export_template_to = arguments.get("export_template") def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str: """Try to find the 'start_rev'. From 15a090069d0324f52a4ecf9b29719310ca942848 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 01:55:38 +0800 Subject: [PATCH 109/275] refactor(bump): improve readability and still bypass mypy check --- commitizen/commands/bump.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index fa49d37d5e..5edf59e9e8 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -78,22 +78,21 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: **config.settings, **{ k: v - for k, v in { - # All these are for making mypy happy - "annotated_tag_message": arguments.get("annotated_tag_message"), - "annotated_tag": arguments.get("annotated_tag"), - "bump_message": arguments.get("bump_message"), - "file_name": arguments.get("file_name"), - "gpg_sign": arguments.get("gpg_sign"), - "increment_mode": arguments.get("increment_mode"), - "increment": arguments.get("increment"), - "major_version_zero": arguments.get("major_version_zero"), - "prerelease_offset": arguments.get("prerelease_offset"), - "prerelease": arguments.get("prerelease"), - "tag_format": arguments.get("tag_format"), - "template": arguments.get("template"), - }.items() - if v is not None + for k in ( + "annotated_tag_message", + "annotated_tag", + "bump_message", + "file_name", + "gpg_sign", + "increment_mode", + "increment", + "major_version_zero", + "prerelease_offset", + "prerelease", + "tag_format", + "template", + ) + if (v := arguments.get(k)) is not None }, }, ) From 48f06e313724438794a3027262aa921deb1715f3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 1 Jun 2025 02:05:23 +0800 Subject: [PATCH 110/275] refactor: remove unnecessary bool() and remove Any type from TypedDict get --- commitizen/commands/bump.py | 8 ++------ commitizen/defaults.py | 4 +++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 5edf59e9e8..9f8bc08244 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -40,7 +40,6 @@ class BumpArgs(Settings, total=False): allow_no_commit: bool | None annotated_tag_message: str | None - annotated_tag: bool build_metadata: str | None changelog_to_stdout: bool changelog: bool @@ -51,7 +50,6 @@ class BumpArgs(Settings, total=False): files_only: bool | None get_next: bool git_output_to_stderr: bool - gpg_sign: bool increment_mode: str increment: Increment | None local_version: bool @@ -222,7 +220,7 @@ def __call__(self) -> None: if get_next: # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout - if self.changelog_flag or bool(self.changelog_to_stdout): + if self.changelog_flag or self.changelog_to_stdout: raise NotAllowed( "--changelog or --changelog-to-stdout is not allowed with --get-next" ) @@ -234,9 +232,7 @@ def __call__(self) -> None: # If user specified changelog_to_stdout, they probably want the # changelog to be generated as well, this is the most intuitive solution self.changelog_flag = bool( - self.changelog_flag - or bool(self.changelog_to_stdout) - or self.changelog_config + self.changelog_flag or self.changelog_to_stdout or self.changelog_config ) rules = TagRules.from_settings(cast(Settings, self.bump_settings)) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 29c9f2a494..7a51389b7a 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -32,16 +32,18 @@ class Settings(TypedDict, total=False): allow_abort: bool allowed_prefixes: list[str] always_signoff: bool + annotated_tag: bool bump_message: str | None + change_type_map: dict[str, str] changelog_file: str changelog_format: str | None changelog_incremental: bool changelog_merge_prerelease: bool - change_type_map: dict[str, str] changelog_start_rev: str | None customize: CzSettings encoding: str extras: dict[str, Any] + gpg_sign: bool ignored_tag_formats: Sequence[str] legacy_tag_formats: Sequence[str] major_version_zero: bool From 8e9d40fbc8842651674257b7bb3c42969ae53418 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 4 Jun 2025 23:24:59 +0800 Subject: [PATCH 111/275] style(changelog): add TODO to fixable type ignores --- commitizen/commands/changelog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index f0dd1c516c..8ca36bdb9a 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -176,6 +176,7 @@ def _write_changelog( def _export_template(self) -> None: tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) + # TODO: fix the following type ignores src = Path(tpl.filename) # type: ignore Path(self.export_template_to).write_text(src.read_text()) # type: ignore From c49a55c27bc09ba5f18ff2fe056d387233e52018 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 4 Jun 2025 23:26:37 +0800 Subject: [PATCH 112/275] style(cli): more specific type ignore --- commitizen/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index b5c03527d3..c13f713a27 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -679,7 +679,7 @@ def main() -> None: ) sys.excepthook = no_raise_debug_excepthook - args.func(conf, arguments)() # type: ignore + args.func(conf, arguments)() # type: ignore[arg-type] if __name__ == "__main__": From 150b35dc8e2aad836077887a0086a5c5f5f4fbe8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 4 Jun 2025 23:35:19 +0800 Subject: [PATCH 113/275] style(cli): rename kwarg to values --- commitizen/cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index c13f713a27..12db01711c 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -47,17 +47,17 @@ def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - kwarg: object, + values: object, option_string: str | None = None, ) -> None: - if not isinstance(kwarg, str): + if not isinstance(values, str): return - if "=" not in kwarg: + if "=" not in values: raise InvalidCommandArgumentError( f"Option {option_string} expect a key=value format" ) kwargs = getattr(namespace, self.dest, None) or {} - key, value = kwarg.split("=", 1) + key, value = values.split("=", 1) if not key: raise InvalidCommandArgumentError( f"Option {option_string} expect a key=value format" From 773575598eac2ab520e698935ded1896f14da137 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 6 Jun 2025 11:24:46 +0800 Subject: [PATCH 114/275] refactor(bump): use any to replace 'or' chain --- commitizen/commands/bump.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 9f8bc08244..a4024eee35 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -231,8 +231,8 @@ def __call__(self) -> None: else: # If user specified changelog_to_stdout, they probably want the # changelog to be generated as well, this is the most intuitive solution - self.changelog_flag = bool( - self.changelog_flag or self.changelog_to_stdout or self.changelog_config + self.changelog_flag = any( + (self.changelog_flag, self.changelog_to_stdout, self.changelog_config) ) rules = TagRules.from_settings(cast(Settings, self.bump_settings)) @@ -410,14 +410,18 @@ def __call__(self) -> None: c = git.tag( new_tag_version, - signed=bool( - self.bump_settings.get("gpg_sign") - or self.config.settings.get("gpg_sign") + signed=any( + ( + self.bump_settings.get("gpg_sign"), + self.config.settings.get("gpg_sign"), + ) ), - annotated=bool( - self.bump_settings.get("annotated_tag") - or self.config.settings.get("annotated_tag") - or self.bump_settings.get("annotated_tag_message") + annotated=any( + ( + self.bump_settings.get("annotated_tag"), + self.config.settings.get("annotated_tag"), + self.bump_settings.get("annotated_tag_message"), + ) ), msg=self.bump_settings.get("annotated_tag_message", None), # TODO: also get from self.config.settings? From 2966cf938b17a37537949e4ee2bbee99919f8999 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 6 Jun 2025 00:46:21 +0800 Subject: [PATCH 115/275] perf(bump): avoid unnecessary list construction and rename variable to avoid confusion --- commitizen/bump.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 2fa814f7c8..6d6b6dc069 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -3,6 +3,7 @@ import os import re from collections import OrderedDict +from collections.abc import Iterable from glob import iglob from logging import getLogger from string import Template @@ -61,7 +62,7 @@ def find_increment( def update_version_in_files( current_version: str, new_version: str, - files: list[str], + files: Iterable[str], *, check_consistency: bool = False, encoding: str = ENCODING, @@ -99,11 +100,11 @@ def update_version_in_files( return updated -def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str]]: +def _files_and_regexes(patterns: Iterable[str], version: str) -> list[tuple[str, str]]: """ Resolve all distinct files with their regexp from a list of glob patterns with optional regexp """ - out: list[tuple[str, str]] = [] + out: set[tuple[str, str]] = set() for pattern in patterns: drive, tail = os.path.splitdrive(pattern) path, _, regex = tail.partition(":") @@ -111,9 +112,10 @@ def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str if not regex: regex = re.escape(version) - for path in iglob(filepath): - out.append((path, regex)) - return sorted(list(set(out))) + for file in iglob(filepath): + out.add((file, regex)) + + return sorted(out) def _bump_with_regex( From 303d2ebc356f8753a1d7ebbffd68d51d924971a9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 7 Jun 2025 23:29:10 +0800 Subject: [PATCH 116/275] refactor(bump): eliminate similar patterns in code --- commitizen/commands/bump.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index a4024eee35..7fe01393a2 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -397,16 +397,10 @@ def __call__(self) -> None: err = c.err.strip() or c.out raise BumpCommitFailedError(f'2nd git.commit error: "{err}"') - if c.out: - if self.git_output_to_stderr: - out.diagnostic(c.out) - else: - out.write(c.out) - if c.err: - if self.git_output_to_stderr: - out.diagnostic(c.err) - else: - out.write(c.err) + for msg in (c.out, c.err): + if msg: + out_func = out.diagnostic if self.git_output_to_stderr else out.write + out_func(msg) c = git.tag( new_tag_version, From af50d8572b611667690110bd540b10afafd4684a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 6 Jun 2025 02:03:32 +0800 Subject: [PATCH 117/275] refactor(git): simplify tag logic --- commitizen/git.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index fb59750eaf..0f5f820eb2 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -160,16 +160,14 @@ def from_line(cls, line: str, inner_delimiter: str) -> GitTag: def tag( tag: str, annotated: bool = False, signed: bool = False, msg: str | None = None ) -> cmd.Command: - _opt = "" - if annotated: - _opt = f"-a {tag} -m" - if signed: - _opt = f"-s {tag} -m" + if not annotated and not signed: + return cmd.run(f"git tag {tag}") # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging, # we're not able to create lightweight tag with message. # by adding message, we make it a annotated tags - return cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') + option = "-s" if signed else "-a" # The else case is for annotated tags + return cmd.run(f'git tag {option} {tag} -m "{msg or tag}"') def add(*args: str) -> cmd.Command: From 654f4a03b34c094b5b718f3b2a66f0905689c15c Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 6 Jun 2025 01:50:14 +0800 Subject: [PATCH 118/275] refactor(git): retype get_commits parameter to make it more friendly to call sites --- commitizen/commands/bump.py | 5 +---- commitizen/commands/check.py | 2 +- commitizen/git.py | 7 +++++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 7fe01393a2..ec4cb0b0b6 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -253,10 +253,7 @@ def __call__(self) -> None: ) from exc else: if increment is None: - if current_tag: - commits = git.get_commits(current_tag.name) - else: - commits = git.get_commits() + commits = git.get_commits(current_tag.name if current_tag else None) # No commits, there is no need to create an empty tag. # Unless we previously had a prerelease. diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 8a7d0dd019..ba003cf3cc 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -110,7 +110,7 @@ def _get_commits(self) -> list[git.GitCommit]: return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))] # Get commit messages from git log (--rev-range) - return git.get_commits(end=self.rev_range or "HEAD") + return git.get_commits(end=self.rev_range) @staticmethod def _filter_comments(msg: str) -> str: diff --git a/commitizen/git.py b/commitizen/git.py index 0f5f820eb2..8025041abb 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -201,15 +201,18 @@ def _create_commit_cmd_string(args: str, committer_date: str | None, name: str) def get_commits( start: str | None = None, - end: str = "HEAD", + end: str | None = None, *, args: str = "", ) -> list[GitCommit]: """Get the commits between start and end.""" + if end is None: + end = "HEAD" git_log_entries = _get_log_as_str_list(start, end, args) return [ GitCommit.from_rev_and_commit(rev_and_commit) - for rev_and_commit in filter(None, git_log_entries) + for rev_and_commit in git_log_entries + if rev_and_commit ] From 6abbc530b02e3d6d5c3def020b18fd771ed111c3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 6 Jun 2025 01:33:04 +0800 Subject: [PATCH 119/275] refactor(bump): simplify nested if --- commitizen/commands/bump.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index ec4cb0b0b6..ba252fcf21 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -206,17 +206,13 @@ def __call__(self) -> None: if get_next: raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION") - if self.bump_settings["major_version_zero"]: - if not current_version.release[0] == 0: - raise NotAllowed( - f"--major-version-zero is meaningless for current version {current_version}" - ) + if self.bump_settings["major_version_zero"] and current_version.release[0]: + raise NotAllowed( + f"--major-version-zero is meaningless for current version {current_version}" + ) - if build_metadata: - if is_local_version: - raise NotAllowed( - "--local-version cannot be combined with --build-metadata" - ) + if build_metadata and is_local_version: + raise NotAllowed("--local-version cannot be combined with --build-metadata") if get_next: # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout From f4fe43c363bf7d37e02e8fb633e197e3eab85f1e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 00:11:57 +0800 Subject: [PATCH 120/275] fix(commit): emit deprecated warning of cz commit -s --- commitizen/commands/commit.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 43feb272ba..bb4c2fdc94 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -125,6 +125,11 @@ def __call__(self) -> None: write_message_to_file = self.arguments.get("write_message_to_file") signoff = bool(self.arguments.get("signoff")) + if signoff: + out.warn( + "Deprecated warning: `cz commit -s` is deprecated and will be removed in v5, please use `cz commit -- -s` instead." + ) + if self.arguments.get("all"): git.add("-u") @@ -147,11 +152,6 @@ def __call__(self) -> None: if dry_run: raise DryRunExit() - if signoff: - out.warn( - "signoff mechanic is deprecated, please use `cz commit -- -s` instead." - ) - if self.config.settings["always_signoff"] or signoff: extra_args = f"{extra_args} -s".strip() From a9c4e0a87eb3004cb02181d536fcb9d117aff2d6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 00:26:32 +0800 Subject: [PATCH 121/275] fix(cli): update description for deprecate warning Closes #1135 --- commitizen/cli.py | 4 ++-- ...st_bump_command_shows_description_when_use_help_option.txt | 2 +- ..._commit_command_shows_description_when_use_help_option.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 12db01711c..6f7556df49 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -147,7 +147,7 @@ def __call__( { "name": ["-s", "--signoff"], "action": "store_true", - "help": "sign off the commit", + "help": "Deprecated, use 'cz commit -- -s' instead", }, { "name": ["-a", "--all"], @@ -347,7 +347,7 @@ def __call__( }, { "name": ["--version-type"], - "help": "Deprecated, use --version-scheme", + "help": "Deprecated, use --version-scheme instead", "default": None, "choices": version_schemes.KNOWN_SCHEMES, }, diff --git a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt index 5d4438875d..4cf8e6c91b 100644 --- a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt @@ -74,7 +74,7 @@ options: --version-scheme {pep440,semver,semver2} choose version scheme --version-type {pep440,semver,semver2} - Deprecated, use --version-scheme + Deprecated, use --version-scheme instead --build-metadata BUILD_METADATA Add additional build-metadata to the version-number --get-next Determine the next version and write to stdout diff --git a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt index dd1f53f3da..ba531042aa 100644 --- a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt @@ -12,7 +12,7 @@ options: --write-message-to-file FILE_PATH write message to file before committing (can be combined with --dry-run) - -s, --signoff sign off the commit + -s, --signoff Deprecated, use 'cz commit -- -s' instead -a, --all Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected. From fa5f3c8062bfde36fa3bdb79d9e06c5a3dfe7dd6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 31 May 2025 23:30:27 +0800 Subject: [PATCH 122/275] refactor(questions): type questions with TypedDict --- commitizen/commands/commit.py | 2 +- commitizen/cz/base.py | 4 +-- .../conventional_commits.py | 6 ++-- commitizen/cz/customize/customize.py | 7 ++-- commitizen/cz/jira/jira.py | 4 +-- commitizen/defaults.py | 6 ++-- commitizen/question.py | 32 +++++++++++++++++++ tests/conftest.py | 3 +- 8 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 commitizen/question.py diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index bb4c2fdc94..19bb72fb00 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -66,7 +66,7 @@ def _prompt_commit_questions(self) -> str: # Prompt user for the commit message cz = self.cz questions = cz.questions() - for question in filter(lambda q: q["type"] == "list", questions): + for question in (q for q in questions if q["type"] == "list"): question["use_shortcuts"] = self.config.settings["use_shortcuts"] try: answers = questionary.prompt(questions, style=cz.style) diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 9e803c3d01..a30540cae6 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -9,7 +9,7 @@ from commitizen import git from commitizen.config.base_config import BaseConfig -from commitizen.defaults import Questions +from commitizen.question import CzQuestion class MessageBuilderHook(Protocol): @@ -68,7 +68,7 @@ def __init__(self, config: BaseConfig) -> None: self.config.settings.update({"style": BaseCommitizen.default_style_config}) @abstractmethod - def questions(self) -> Questions: + def questions(self) -> Iterable[CzQuestion]: """Questions regarding the commit message.""" @abstractmethod diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 912770a726..ca4809a90e 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -3,7 +3,7 @@ from commitizen import defaults from commitizen.cz.base import BaseCommitizen from commitizen.cz.utils import multiple_line_breaker, required_validator -from commitizen.defaults import Questions +from commitizen.question import CzQuestion __all__ = ["ConventionalCommitsCz"] @@ -29,7 +29,7 @@ class ConventionalCommitsCz(BaseCommitizen): } changelog_pattern = defaults.BUMP_PATTERN - def questions(self) -> Questions: + def questions(self) -> list[CzQuestion]: return [ { "type": "list", @@ -122,8 +122,8 @@ def questions(self) -> Questions: }, { "type": "confirm", - "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer", "name": "is_breaking_change", + "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer", "default": False, }, { diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index 8f844501ec..6581ad444e 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING +from commitizen.question import CzQuestion + if TYPE_CHECKING: from jinja2 import Template else: @@ -14,7 +16,6 @@ from commitizen import defaults from commitizen.config import BaseConfig from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions from commitizen.exceptions import MissingCzCustomizeConfigError __all__ = ["CustomizeCommitsCz"] @@ -45,8 +46,8 @@ def __init__(self, config: BaseConfig) -> None: if value := self.custom_settings.get(attr_name): setattr(self, attr_name, value) - def questions(self) -> Questions: - return self.custom_settings.get("questions", [{}]) + def questions(self) -> list[CzQuestion]: + return self.custom_settings.get("questions", [{}]) # type: ignore def message(self, answers: dict) -> str: message_template = Template(self.custom_settings.get("message_template", "")) diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 05e23e1690..eafded615e 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,13 +1,13 @@ import os from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions +from commitizen.question import CzQuestion __all__ = ["JiraSmartCz"] class JiraSmartCz(BaseCommitizen): - def questions(self) -> Questions: + def questions(self) -> list[CzQuestion]: return [ { "type": "input", diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 7a51389b7a..a49d6d9428 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -6,8 +6,10 @@ from collections.abc import Iterable, MutableMapping, Sequence from typing import Any, TypedDict +from commitizen.question import CzQuestion + # Type -Questions = Iterable[MutableMapping[str, Any]] +Questions = Iterable[MutableMapping[str, Any]] # TODO: deprecate this? class CzSettings(TypedDict, total=False): @@ -16,7 +18,7 @@ class CzSettings(TypedDict, total=False): bump_map_major_version_zero: OrderedDict[str, str] change_type_order: list[str] - questions: Questions + questions: Iterable[CzQuestion] example: str | None schema_pattern: str | None schema: str | None diff --git a/commitizen/question.py b/commitizen/question.py new file mode 100644 index 0000000000..393e386092 --- /dev/null +++ b/commitizen/question.py @@ -0,0 +1,32 @@ +from typing import Callable, Literal, TypedDict, Union + + +class Choice(TypedDict, total=False): + value: str + name: str + key: str + + +class ListQuestion(TypedDict, total=False): + type: Literal["list"] + name: str + message: str + choices: list[Choice] + use_shortcuts: bool + + +class InputQuestion(TypedDict, total=False): + type: Literal["input"] + name: str + message: str + filter: Callable[[str], str] + + +class ConfirmQuestion(TypedDict): + type: Literal["confirm"] + name: str + message: str + default: bool + + +CzQuestion = Union[ListQuestion, InputQuestion, ConfirmQuestion] diff --git a/tests/conftest.py b/tests/conftest.py index 1b49dcbfaa..a4815b3d5a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,7 @@ from commitizen.config import BaseConfig from commitizen.cz import registry from commitizen.cz.base import BaseCommitizen +from commitizen.question import CzQuestion from tests.utils import create_file_and_commit SIGNER = "GitHub Action" @@ -222,7 +223,7 @@ def use_cz_semver(mocker): class MockPlugin(BaseCommitizen): - def questions(self) -> defaults.Questions: + def questions(self) -> list[CzQuestion]: return [] def message(self, answers: dict) -> str: From 547836617d8fbae1197a7e7306f554c4446dea35 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 12:31:14 +0800 Subject: [PATCH 123/275] refactor(check): compile once and rename variable --- commitizen/commands/check.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index ba003cf3cc..69147bcfbe 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -79,20 +79,19 @@ def __call__(self) -> None: if not commits: raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'") - pattern = self.cz.schema_pattern() - displayed_msgs_content = "\n".join( - [ - f'commit "{commit.rev}": "{commit.message}"' - for commit in commits - if not self.validate_commit_message(commit.message, pattern) - ] + pattern = re.compile(self.cz.schema_pattern()) + invalid_msgs_content = "\n".join( + f'commit "{commit.rev}": "{commit.message}"' + for commit in commits + if not self._validate_commit_message(commit.message, pattern) ) - if displayed_msgs_content: + if invalid_msgs_content: + # TODO: capitalize the first letter of the error message for consistency in v5 raise InvalidCommitMessageError( "commit validation: failed!\n" "please enter a commit message in the commitizen format.\n" - f"{displayed_msgs_content}\n" - f"pattern: {pattern}" + f"{invalid_msgs_content}\n" + f"pattern: {pattern.pattern}" ) out.success("Commit validation: successful!") @@ -143,14 +142,18 @@ def _filter_comments(msg: str) -> str: lines.append(line) return "\n".join(lines) - def validate_commit_message(self, commit_msg: str, pattern: str) -> bool: + def _validate_commit_message( + self, commit_msg: str, pattern: re.Pattern[str] + ) -> bool: if not commit_msg: return self.allow_abort if any(map(commit_msg.startswith, self.allowed_prefixes)): return True + if self.max_msg_length: msg_len = len(commit_msg.partition("\n")[0].strip()) if msg_len > self.max_msg_length: return False - return bool(re.match(pattern, commit_msg)) + + return bool(pattern.match(commit_msg)) From 7fc35151829335d09a9428e306ad18a161810a91 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 21:10:05 +0800 Subject: [PATCH 124/275] refactor: do not guess if changelog format is provided --- commitizen/changelog_formats/__init__.py | 17 +++++++++++------ commitizen/factory.py | 4 +--- tests/test_changelog_formats.py | 8 ++++---- tests/{test_defaults.py => test_deprecated.py} | 6 +++++- 4 files changed, 21 insertions(+), 14 deletions(-) rename tests/{test_defaults.py => test_deprecated.py} (84%) diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py index b7b3cac01d..9a5eea7ab2 100644 --- a/commitizen/changelog_formats/__init__.py +++ b/commitizen/changelog_formats/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from typing import ClassVar, Protocol +from typing import Callable, ClassVar, Protocol if sys.version_info >= (3, 10): from importlib import metadata @@ -64,10 +64,9 @@ def get_changelog_format( :raises FormatUnknown: if a non-empty name is provided but cannot be found in the known formats """ name: str | None = config.settings.get("changelog_format") - format: type[ChangelogFormat] | None = guess_changelog_format(filename) - - if name and name in KNOWN_CHANGELOG_FORMATS: - format = KNOWN_CHANGELOG_FORMATS[name] + format = ( + name and KNOWN_CHANGELOG_FORMATS.get(name) or _guess_changelog_format(filename) + ) if not format: raise ChangelogFormatUnknown(f"Unknown changelog format '{name}'") @@ -75,7 +74,7 @@ def get_changelog_format( return format(config) -def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None: +def _guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None: """ Try guessing the file format from the filename. @@ -91,3 +90,9 @@ def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None if filename.endswith(f".{alt_extension}"): return format return None + + +def __getattr__(name: str) -> Callable[[str], type[ChangelogFormat] | None]: + if name == "guess_changelog_format": + return _guess_changelog_format + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/commitizen/factory.py b/commitizen/factory.py index b5d665b65e..d9e99fb771 100644 --- a/commitizen/factory.py +++ b/commitizen/factory.py @@ -8,12 +8,10 @@ def committer_factory(config: BaseConfig) -> BaseCommitizen: """Return the correct commitizen existing in the registry.""" name: str = config.settings["name"] try: - _cz = registry[name](config) + return registry[name](config) except KeyError: msg_error = ( "The committer has not been found in the system.\n\n" f"Try running 'pip install {name}'\n" ) raise NoCommitizenFoundException(msg_error) - else: - return _cz diff --git a/tests/test_changelog_formats.py b/tests/test_changelog_formats.py index dec23720dc..e0d99e0325 100644 --- a/tests/test_changelog_formats.py +++ b/tests/test_changelog_formats.py @@ -6,8 +6,8 @@ from commitizen.changelog_formats import ( KNOWN_CHANGELOG_FORMATS, ChangelogFormat, + _guess_changelog_format, get_changelog_format, - guess_changelog_format, ) from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ChangelogFormatUnknown @@ -15,14 +15,14 @@ @pytest.mark.parametrize("format", KNOWN_CHANGELOG_FORMATS.values()) def test_guess_format(format: type[ChangelogFormat]): - assert guess_changelog_format(f"CHANGELOG.{format.extension}") is format + assert _guess_changelog_format(f"CHANGELOG.{format.extension}") is format for ext in format.alternative_extensions: - assert guess_changelog_format(f"CHANGELOG.{ext}") is format + assert _guess_changelog_format(f"CHANGELOG.{ext}") is format @pytest.mark.parametrize("filename", ("CHANGELOG", "NEWS", "file.unknown", None)) def test_guess_format_unknown(filename: str): - assert guess_changelog_format(filename) is None + assert _guess_changelog_format(filename) is None @pytest.mark.parametrize( diff --git a/tests/test_defaults.py b/tests/test_deprecated.py similarity index 84% rename from tests/test_defaults.py rename to tests/test_deprecated.py index 73cd35b80c..41bea81a73 100644 --- a/tests/test_defaults.py +++ b/tests/test_deprecated.py @@ -1,6 +1,6 @@ import pytest -from commitizen import defaults +from commitizen import changelog_formats, defaults def test_getattr_deprecated_vars(): @@ -15,6 +15,10 @@ def test_getattr_deprecated_vars(): assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER assert defaults.encoding == defaults.ENCODING assert defaults.name == defaults.DEFAULT_SETTINGS["name"] + assert ( + changelog_formats._guess_changelog_format + == changelog_formats.guess_changelog_format + ) # Verify warning messages assert len(record) == 7 From 2d23942fe77e547bb0ecb65e883a9921e094eca9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 12:37:11 +0800 Subject: [PATCH 125/275] refactor(conventional_commits): make schema_pattern more readable --- .../conventional_commits.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index ca4809a90e..8e6caac1f5 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -174,11 +174,27 @@ def schema(self) -> str: ) def schema_pattern(self) -> str: + change_types = ( + "build", + "bump", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test", + ) return ( r"(?s)" # To explicitly make . match new line - r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)" # type - r"(\(\S+\))?!?:" # scope - r"( [^\n\r]+)" # subject + r"(" + "|".join(change_types) + r")" # type + r"(\(\S+\))?" # scope + r"!?" + r": " + r"([^\n\r]+)" # subject r"((\n\n.*)|(\s*))?$" ) From 451e0fc50577d9081266b8bd0c36b82d903bc21d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 21:46:10 +0800 Subject: [PATCH 126/275] refactor(conventional_commits): use TypedDict for answers --- .../cz/conventional_commits/conventional_commits.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 8e6caac1f5..6893423478 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,4 +1,5 @@ import os +from typing import TypedDict from commitizen import defaults from commitizen.cz.base import BaseCommitizen @@ -16,6 +17,15 @@ def _parse_subject(text: str) -> str: return required_validator(text.strip(".").strip(), msg="Subject is required.") +class ConventionalCommitsAnswers(TypedDict): + prefix: str + scope: str + subject: str + body: str + footer: str + is_breaking_change: bool + + class ConventionalCommitsCz(BaseCommitizen): bump_pattern = defaults.BUMP_PATTERN bump_map = defaults.BUMP_MAP @@ -136,7 +146,7 @@ def questions(self) -> list[CzQuestion]: }, ] - def message(self, answers: dict) -> str: + def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[override] prefix = answers["prefix"] scope = answers["scope"] subject = answers["subject"] From 18b9729640b7c311831475af7c91d0360de527d7 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 21:46:31 +0800 Subject: [PATCH 127/275] refactor(jira): refactor message --- commitizen/cz/jira/jira.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index eafded615e..8bf8c210b3 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -43,18 +43,11 @@ def questions(self) -> list[CzQuestion]: }, ] - def message(self, answers: dict) -> str: + def message(self, answers: dict[str, str]) -> str: return " ".join( - filter( - bool, - [ - answers["message"], - answers["issues"], - answers["workflow"], - answers["time"], - answers["comment"], - ], - ) + x + for k in ("message", "issues", "workflow", "time", "comment") + if (x := answers.get(k)) ) def example(self) -> str: From c1eea63240c1b24f39699f7001717bcc7cd80e93 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 30 May 2025 22:04:34 +0800 Subject: [PATCH 128/275] style: replace dict with Mapping --- commitizen/cz/base.py | 4 ++-- commitizen/cz/customize/customize.py | 5 +++-- commitizen/cz/jira/jira.py | 3 ++- tests/conftest.py | 6 +++--- tests/test_cz_base.py | 4 +++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index a30540cae6..cdc1476694 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -1,7 +1,7 @@ from __future__ import annotations from abc import ABCMeta, abstractmethod -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from typing import Any, Callable, Protocol from jinja2 import BaseLoader, PackageLoader @@ -72,7 +72,7 @@ def questions(self) -> Iterable[CzQuestion]: """Questions regarding the commit message.""" @abstractmethod - def message(self, answers: dict) -> str: + def message(self, answers: Mapping[str, Any]) -> str: """Format your git message.""" @property diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index 6581ad444e..d6e9f9a5c3 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any from commitizen.question import CzQuestion @@ -49,7 +50,7 @@ def __init__(self, config: BaseConfig) -> None: def questions(self) -> list[CzQuestion]: return self.custom_settings.get("questions", [{}]) # type: ignore - def message(self, answers: dict) -> str: + def message(self, answers: Mapping[str, Any]) -> str: message_template = Template(self.custom_settings.get("message_template", "")) if getattr(Template, "substitute", None): return message_template.substitute(**answers) # type: ignore diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 8bf8c210b3..4e6024ff74 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,4 +1,5 @@ import os +from collections.abc import Mapping from commitizen.cz.base import BaseCommitizen from commitizen.question import CzQuestion @@ -43,7 +44,7 @@ def questions(self) -> list[CzQuestion]: }, ] - def message(self, answers: dict[str, str]) -> str: + def message(self, answers: Mapping[str, str]) -> str: return " ".join( x for k in ("message", "issues", "workflow", "time", "comment") diff --git a/tests/conftest.py b/tests/conftest.py index a4815b3d5a..324ef9bebb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ import os import re import tempfile -from collections.abc import Iterator +from collections.abc import Iterator, Mapping from pathlib import Path import pytest @@ -210,7 +210,7 @@ def questions(self) -> list: }, ] - def message(self, answers: dict) -> str: + def message(self, answers: Mapping) -> str: prefix = answers["prefix"] subject = answers.get("subject", "default message").trim() return f"{prefix}: {subject}" @@ -226,7 +226,7 @@ class MockPlugin(BaseCommitizen): def questions(self) -> list[CzQuestion]: return [] - def message(self, answers: dict) -> str: + def message(self, answers: Mapping) -> str: return "" diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py index be93b4ca0f..0ee5a23fb8 100644 --- a/tests/test_cz_base.py +++ b/tests/test_cz_base.py @@ -1,3 +1,5 @@ +from collections.abc import Mapping + import pytest from commitizen.cz.base import BaseCommitizen @@ -7,7 +9,7 @@ class DummyCz(BaseCommitizen): def questions(self): return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}] - def message(self, answers: dict): + def message(self, answers: Mapping): return answers["commit"] From 5df4b1651d58037d604fda1af7b3eca724dfc2c9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 13:33:19 +0800 Subject: [PATCH 129/275] ci(pyproject.toml): strict check for invalid commit messages --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ee0ecc9ced..58425a81ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -249,8 +249,8 @@ lint.sequence = [ { cmd = "mypy" }, ] -check-commit.help = "Check the commit message" -check-commit.cmd = "cz -nr 3 check --rev-range origin/master.." +check-commit.help = "Check the commit messages" +check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.." test.help = "Run the test suite" test.cmd = "pytest -n 3 --dist=loadfile" @@ -262,7 +262,7 @@ cover.help = "Run the test suite with coverage" cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen" all.help = "Run all tasks" -all.sequence = ["format", "lint", "cover", "check-commit"] +all.sequence = ["check-commit", "format", "lint", "cover"] "doc:screenshots".help = "Render documentation screenshots" "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots" @@ -274,7 +274,7 @@ doc.help = "Live documentation server" doc.cmd = "mkdocs serve" ci.help = "Run all tasks in CI" -ci.sequence = [{ cmd = "pre-commit run --all-files" }, "cover"] +ci.sequence = ["check-commit", { cmd = "pre-commit run --all-files" }, "cover"] ci.env = { SKIP = "no-commit-to-branch" } setup-pre-commit.help = "Install pre-commit hooks" From 188bc3faf96ea2f7eac34b5eee65bf9fcc2648c3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 2 Jun 2025 14:30:22 +0800 Subject: [PATCH 130/275] docs(pyproject.toml): move check-commit before cover --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 58425a81ac..1de17464a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -262,7 +262,7 @@ cover.help = "Run the test suite with coverage" cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen" all.help = "Run all tasks" -all.sequence = ["check-commit", "format", "lint", "cover"] +all.sequence = ["format", "lint", "check-commit", "cover"] "doc:screenshots".help = "Render documentation screenshots" "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots" From 8675c289df0e5c37b2867bf16c7c2c41f7eef438 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 8 Jun 2025 23:10:04 +0800 Subject: [PATCH 131/275] build: add PGH003 and PGH004, add types-colorama --- commitizen/__init__.py | 2 +- commitizen/commands/bump.py | 4 ++-- commitizen/commands/changelog.py | 4 ++-- commitizen/config/base_config.py | 2 +- commitizen/config/toml_config.py | 6 +++--- commitizen/cz/customize/__init__.py | 2 +- commitizen/cz/customize/customize.py | 4 ++-- commitizen/providers/base_provider.py | 4 ++-- commitizen/providers/cargo_provider.py | 12 ++++++------ commitizen/providers/commitizen_provider.py | 2 +- commitizen/providers/poetry_provider.py | 4 ++-- commitizen/version_schemes.py | 4 ++-- poetry.lock | 14 +++++++++++++- pyproject.toml | 14 ++++++-------- tests/commands/test_bump_command.py | 2 +- 15 files changed, 45 insertions(+), 35 deletions(-) diff --git a/commitizen/__init__.py b/commitizen/__init__.py index f16def4441..6db9e6e7db 100644 --- a/commitizen/__init__.py +++ b/commitizen/__init__.py @@ -1,7 +1,7 @@ import logging import logging.config -from colorama import init # type: ignore +from colorama import init from commitizen.cz.base import BaseCommitizen diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index ba252fcf21..2a84483c94 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -331,14 +331,14 @@ def __call__(self) -> None: "dry_run": dry_run, } if self.changelog_to_stdout: - changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) # type: ignore + changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) # type: ignore[typeddict-item] try: changelog_cmd() except DryRunExit: pass args["file_name"] = self.file_name - changelog_cmd = Changelog(self.config, args) # type: ignore + changelog_cmd = Changelog(self.config, args) # type: ignore[arg-type] changelog_cmd() files.append(changelog_cmd.file_name) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 8ca36bdb9a..dc50eb3ad2 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -177,8 +177,8 @@ def _write_changelog( def _export_template(self) -> None: tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) # TODO: fix the following type ignores - src = Path(tpl.filename) # type: ignore - Path(self.export_template_to).write_text(src.read_text()) # type: ignore + src = Path(tpl.filename) # type: ignore[arg-type] + Path(self.export_template_to).write_text(src.read_text()) # type: ignore[arg-type] def __call__(self) -> None: commit_parser = self.cz.commit_parser diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 59c0d16a06..4b8f5f05fc 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -27,7 +27,7 @@ def settings(self) -> Settings: @property def path(self) -> Path: - return self._path # type: ignore + return self._path # type: ignore[return-value] @path.setter def path(self, path: str | Path) -> None: diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 3571c9c882..2164d3f992 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -37,7 +37,7 @@ def init_empty_config_content(self) -> None: with open(self.path, "wb") as output_toml_file: if parser.get("tool") is None: parser["tool"] = table() - parser["tool"]["commitizen"] = table() # type: ignore + parser["tool"]["commitizen"] = table() # type: ignore[index] output_toml_file.write(parser.as_string().encode(self.encoding)) def set_key(self, key: str, value: Any) -> Self: @@ -49,7 +49,7 @@ def set_key(self, key: str, value: Any) -> Self: with open(self.path, "rb") as f: parser = parse(f.read()) - parser["tool"]["commitizen"][key] = value # type: ignore + parser["tool"]["commitizen"][key] = value # type: ignore[index] with open(self.path, "wb") as f: f.write(parser.as_string().encode(self.encoding)) return self @@ -68,6 +68,6 @@ def _parse_setting(self, data: bytes | str) -> None: raise InvalidConfigurationError(f"Failed to parse {self.path}: {e}") try: - self.settings.update(doc["tool"]["commitizen"]) # type: ignore + self.settings.update(doc["tool"]["commitizen"]) # type: ignore[index,typeddict-item] # TODO: fix this except exceptions.NonExistentKey: self.is_empty_config = True diff --git a/commitizen/cz/customize/__init__.py b/commitizen/cz/customize/__init__.py index c5af001a79..0aedb9337a 100644 --- a/commitizen/cz/customize/__init__.py +++ b/commitizen/cz/customize/__init__.py @@ -1 +1 @@ -from .customize import CustomizeCommitsCz # noqa +from .customize import CustomizeCommitsCz # noqa: F401 diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index d6e9f9a5c3..dde2685496 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -48,12 +48,12 @@ def __init__(self, config: BaseConfig) -> None: setattr(self, attr_name, value) def questions(self) -> list[CzQuestion]: - return self.custom_settings.get("questions", [{}]) # type: ignore + return self.custom_settings.get("questions", [{}]) # type: ignore[return-value] def message(self, answers: Mapping[str, Any]) -> str: message_template = Template(self.custom_settings.get("message_template", "")) if getattr(Template, "substitute", None): - return message_template.substitute(**answers) # type: ignore + return message_template.substitute(**answers) # type: ignore[attr-defined,no-any-return] # pragma: no cover # TODO: check if we can fix this return message_template.render(**answers) def example(self) -> str: diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 27c3123416..c91bfdae20 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -86,7 +86,7 @@ def set_version(self, version: str) -> None: self.file.write_text(tomlkit.dumps(document)) def get(self, document: tomlkit.TOMLDocument) -> str: - return document["project"]["version"] # type: ignore + return document["project"]["version"] # type: ignore[index,return-value] def set(self, document: tomlkit.TOMLDocument, version: str) -> None: - document["project"]["version"] = version # type: ignore + document["project"]["version"] = version # type: ignore[index] diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 87e45cd71c..d453a4aa5b 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -23,18 +23,18 @@ def lock_file(self) -> Path: def get(self, document: tomlkit.TOMLDocument) -> str: try: - return document["package"]["version"] # type: ignore + return document["package"]["version"] # type: ignore[index,return-value] except tomlkit.exceptions.NonExistentKey: ... - return document["workspace"]["package"]["version"] # type: ignore + return document["workspace"]["package"]["version"] # type: ignore[index,return-value] def set(self, document: tomlkit.TOMLDocument, version: str) -> None: try: - document["workspace"]["package"]["version"] = version # type: ignore + document["workspace"]["package"]["version"] = version # type: ignore[index] return except tomlkit.exceptions.NonExistentKey: ... - document["package"]["version"] = version # type: ignore + document["package"]["version"] = version # type: ignore[index] def set_version(self, version: str) -> None: super().set_version(version) @@ -44,9 +44,9 @@ def set_version(self, version: str) -> None: def set_lock_version(self, version: str) -> None: cargo_toml_content = tomlkit.parse(self.file.read_text()) try: - package_name = cargo_toml_content["package"]["name"] # type: ignore + package_name = cargo_toml_content["package"]["name"] # type: ignore[index] except tomlkit.exceptions.NonExistentKey: - package_name = cargo_toml_content["workspace"]["package"]["name"] # type: ignore + package_name = cargo_toml_content["workspace"]["package"]["name"] # type: ignore[index] cargo_lock_content = tomlkit.parse(self.lock_file.read_text()) packages: tomlkit.items.AoT = cargo_lock_content["package"] # type: ignore[assignment] diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py index 7ce177a604..d9207b19ff 100644 --- a/commitizen/providers/commitizen_provider.py +++ b/commitizen/providers/commitizen_provider.py @@ -9,7 +9,7 @@ class CommitizenProvider(VersionProvider): """ def get_version(self) -> str: - return self.config.settings["version"] # type: ignore + return self.config.settings["version"] # type: ignore[return-value] # TODO: check if we can fix this by tweaking the `Settings` type def set_version(self, version: str) -> None: self.config.set_key("version", version) diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py index 1dd33f053e..f63b13b793 100644 --- a/commitizen/providers/poetry_provider.py +++ b/commitizen/providers/poetry_provider.py @@ -13,7 +13,7 @@ class PoetryProvider(TomlProvider): filename = "pyproject.toml" def get(self, pyproject: tomlkit.TOMLDocument) -> str: - return pyproject["tool"]["poetry"]["version"] # type: ignore + return pyproject["tool"]["poetry"]["version"] # type: ignore[index,return-value] def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None: - pyproject["tool"]["poetry"]["version"] = version # type: ignore + pyproject["tool"]["poetry"]["version"] = version # type: ignore[index] diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index a59d3c0aa0..e9f99c5514 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -266,7 +266,7 @@ def bump( if self.local and is_local_version: local_version = self.scheme(self.local).bump(increment) - return self.scheme(f"{self.public}+{local_version}") # type: ignore + return self.scheme(f"{self.public}+{local_version}") # type: ignore[return-value] base = self._get_increment_base(increment, exact_increment) dev_version = self.generate_devrelease(devrelease) @@ -283,7 +283,7 @@ def bump( # TODO: post version return self.scheme( f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}" - ) # type: ignore + ) # type: ignore[return-value] def _get_increment_base( self, increment: Increment | None, exact_increment: bool diff --git a/poetry.lock b/poetry.lock index f9f74513a8..c5c893931a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1746,6 +1746,18 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "types-colorama" +version = "0.4.15.20240311" +description = "Typing stubs for colorama" +optional = false +python-versions = ">=3.8" +groups = ["linters"] +files = [ + {file = "types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a"}, + {file = "types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e"}, +] + [[package]] name = "types-deprecated" version = "1.2.15.20250304" @@ -2014,4 +2026,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "10e298e9b4d2f2d81a82b43649a68d557ed3e9824b3b210b5b6a607935f9fe9d" +content-hash = "f35cf57718e28836cf1e70b33edab9ce623b01a7f2d31d712585554721f6d403" diff --git a/pyproject.toml b/pyproject.toml index 1de17464a3..0d135954a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,6 +126,7 @@ types-deprecated = "^1.2.9.2" types-python-dateutil = "^2.8.19.13" types-PyYAML = ">=5.4.3,<7.0.0" types-termcolor = "^0.1.1" +types-colorama = "^0.4.15.20240311" [tool.poetry.group.documentation.dependencies] mkdocs = "^1.4.2" @@ -198,6 +199,9 @@ select = [ "UP", # isort "I", + # pygrep-hooks + "PGH003", + "PGH004", # unsorted-dunder-all "RUF022", # unused-noqa @@ -238,16 +242,10 @@ poetry_command = "" [tool.poe.tasks] format.help = "Format the code" -format.sequence = [ - { cmd = "ruff check --fix" }, - { cmd = "ruff format" }, -] +format.sequence = [{ cmd = "ruff check --fix" }, { cmd = "ruff format" }] lint.help = "Lint the code" -lint.sequence = [ - { cmd = "ruff check" }, - { cmd = "mypy" }, -] +lint.sequence = [{ cmd = "ruff check" }, { cmd = "mypy" }] check-commit.help = "Check the commit messages" check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.." diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 64b810e4de..59297b1726 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1721,7 +1721,7 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): "extras": None, } - bump_cmd = bump.Bump(config, arguments) # type: ignore + bump_cmd = bump.Bump(config, arguments) # type: ignore[arg-type] # Test case 1: No current tag, not yes mode mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True)) From c710c9f541ae452547fdce5c360929f007ec4867 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Jun 2025 14:18:28 +0000 Subject: [PATCH 132/275] =?UTF-8?q?bump:=20version=204.8.2=20=E2=86=92=204?= =?UTF-8?q?.8.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 60 +++++++++++++++++++++++++++++++++++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 +-- 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e108eb9b9..9e09fb63e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.8.2 # automatically updated by Commitizen + rev: v4.8.3 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b737a851a..76b6e36e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,63 @@ +## v4.8.3 (2025-06-09) + +### Fix + +- **cli**: update description for deprecate warning +- **commit**: emit deprecated warning of cz commit -s +- **Check**: make parameters backward compatiable +- **BaseConfig**: mypy error +- **deprecated**: mark deprecate in v5 +- **defaults**: add non-capitalized default constants back and deprecated warning + +### Refactor + +- **jira**: refactor message +- **conventional_commits**: use TypedDict for answers +- **conventional_commits**: make schema_pattern more readable +- do not guess if changelog format is provided +- **check**: compile once and rename variable +- **questions**: type questions with TypedDict +- **bump**: simplify nested if +- **git**: retype get_commits parameter to make it more friendly to call sites +- **git**: simplify tag logic +- **bump**: eliminate similar patterns in code +- **bump**: use any to replace 'or' chain +- remove unnecessary bool() and remove Any type from TypedDict get +- **bump**: improve readability and still bypass mypy check +- **commands**: remove unused args, type version command args +- **commit**: type commit args +- **check**: type CheckArgs arguments +- **check**: remove unused argument +- **changelog**: type untyped arguments +- **bump**: TypedDict for bump argument +- make methods protected, better type +- **conventional_commits**: remove unnecessary checks +- fix mypy output and better type +- **BaseCommitizen**: remove unused process_commit +- remove `TypeError` handling since `Python >=3.9` is required +- add comment clarifying `no_raise` parsing to `list[int]` +- **cli.py**: add type hints +- **mypy**: remove `unused-ignore` +- **changelog**: better typing, yield +- **cli**: early return and improve test coverage +- **git**: extract _create_commit_cmd_string +- misc cleanup +- **bump**: clean up +- **bump**: add type for out, replace function with re escape +- **BaseConfig**: use setter +- **changelog**: minor cleanup +- **git**: refactor get_tag_names +- **EOLType**: add eol enum back and reorganize methods +- **git**: code cleanup and better test coverage +- **commit**: simplify call +- **version_scheme**: cleanup +- improve readability and fix typos + +### Perf + +- **bump**: avoid unnecessary list construction and rename variable to avoid confusion +- **tags**: use set + ## v4.8.2 (2025-05-22) ### Refactor diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 8e288a597c..af82c57223 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.8.2" +__version__ = "4.8.3" diff --git a/pyproject.toml b/pyproject.toml index 0d135954a5..6990741957 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.8.2" +version = "4.8.3" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.8.2" +version = "4.8.3" tag_format = "v$version" version_files = [ "pyproject.toml:version", From a5e3cc6e11148f4978a4771cb9ed46137ee61b0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Jun 2025 14:18:53 +0000 Subject: [PATCH 133/275] docs(cli/screenshots): update CLI screenshots [skip ci] --- docs/images/cli_help/cz_bump___help.svg | 348 +++++++++++----------- docs/images/cli_help/cz_commit___help.svg | 111 +++---- 2 files changed, 230 insertions(+), 229 deletions(-) diff --git a/docs/images/cli_help/cz_bump___help.svg b/docs/images/cli_help/cz_bump___help.svg index 7f27636ddf..7d007df312 100644 --- a/docs/images/cli_help/cz_bump___help.svg +++ b/docs/images/cli_help/cz_bump___help.svg @@ -19,273 +19,273 @@ font-weight: 700; } - .terminal-243650528-matrix { + .terminal-1655558888-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-243650528-title { + .terminal-1655558888-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-243650528-r1 { fill: #c5c8c6 } -.terminal-243650528-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-243650528-r3 { fill: #68a0b3;font-weight: bold } -.terminal-243650528-r4 { fill: #98a84b } + .terminal-1655558888-r1 { fill: #c5c8c6 } +.terminal-1655558888-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-1655558888-r3 { fill: #68a0b3;font-weight: bold } +.terminal-1655558888-r4 { fill: #98a84b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -297,92 +297,92 @@ - + - - $ cz bump --help -usage: cz bump [-h][--dry-run][--files-only][--local-version][--changelog] -[--no-verify][--yes][--tag-format TAG_FORMAT] -[--bump-message BUMP_MESSAGE][--prerelease {alpha,beta,rc}] -[--devrelease DEVRELEASE][--increment {MAJOR,MINOR,PATCH}] -[--increment-mode {linear,exact}][--check-consistency] -[--annotated-tag] -[--annotated-tag-message ANNOTATED_TAG_MESSAGE][--gpg-sign] -[--changelog-to-stdout][--git-output-to-stderr][--retry] -[--major-version-zero][--template TEMPLATE][--extra EXTRA] -[--file-name FILE_NAME][--prerelease-offset PRERELEASE_OFFSET] -[--version-scheme {pep440,semver,semver2}] -[--version-type {pep440,semver,semver2}] -[--build-metadata BUILD_METADATA][--get-next] -[--allow-no-commit] -[MANUAL_VERSION] - -bump semantic version based on the git log - -positional arguments: -  MANUAL_VERSION        bump to the given version (e.g: 1.5.3) - -options: -  -h, --help            show this help message and exit -  --dry-run             show output to stdout, no commit, no modified files -  --files-only          bump version in the files from the config -  --local-version       bump only the local version portion -  --changelog, -ch      generate the changelog for the newest version -  --no-verify           this option bypasses the pre-commit and commit-msg -                        hooks -  --yes                 accept automatically questions done -  --tag-format TAG_FORMAT -                        the format used to tag the commit and read it, use it -                        in existing projects, wrap around simple quotes -  --bump-message BUMP_MESSAGE -                        template used to create the release commit, useful -                        when working with CI -  --prerelease, -pr {alpha,beta,rc} -                        choose type of prerelease -  --devrelease, -d DEVRELEASE -                        specify non-negative integer for dev. release -  --increment {MAJOR,MINOR,PATCH} -                        manually specify the desired increment -  --increment-mode {linear,exact} -                        set the method by which the new version is chosen. -'linear'(default) guesses the next version based on -                        typical linear version progression, such that bumping -                        of a pre-release with lower precedence than the -                        current pre-release phase maintains the current phase -                        of higher precedence. 'exact' applies the changes that -                        have been specified (or determined from the commit -                        log) without interpretation, such that the increment -                        and pre-release are always honored -  --check-consistency, -cc -                        check consistency among versions defined in commitizen -                        configuration and version_files -  --annotated-tag, -at  create annotated tag instead of lightweight one -  --annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE -                        create annotated tag message -  --gpg-sign, -s        sign tag instead of lightweight one -  --changelog-to-stdout -                        Output changelog to the stdout -  --git-output-to-stderr -                        Redirect git output to stderr -  --retry               retry commit if it fails the 1st time -  --major-version-zero  keep major version at zero, even for breaking changes -  --template, -t TEMPLATE -                        changelog template file name (relative to the current -                        working directory) -  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') -  --file-name FILE_NAME -                        file name of changelog (default: 'CHANGELOG.md') -  --prerelease-offset PRERELEASE_OFFSET -                        start pre-releases with this offset -  --version-scheme {pep440,semver,semver2} -                        choose version scheme -  --version-type {pep440,semver,semver2} -                        Deprecated, use --version-scheme -  --build-metadata BUILD_METADATA -                        Add additional build-metadata to the version-number -  --get-next            Determine the next version and write to stdout -  --allow-no-commit     bump version without eligible commits - + + $ cz bump --help +usage: cz bump [-h][--dry-run][--files-only][--local-version][--changelog] +[--no-verify][--yes][--tag-format TAG_FORMAT] +[--bump-message BUMP_MESSAGE][--prerelease {alpha,beta,rc}] +[--devrelease DEVRELEASE][--increment {MAJOR,MINOR,PATCH}] +[--increment-mode {linear,exact}][--check-consistency] +[--annotated-tag] +[--annotated-tag-message ANNOTATED_TAG_MESSAGE][--gpg-sign] +[--changelog-to-stdout][--git-output-to-stderr][--retry] +[--major-version-zero][--template TEMPLATE][--extra EXTRA] +[--file-name FILE_NAME][--prerelease-offset PRERELEASE_OFFSET] +[--version-scheme {pep440,semver,semver2}] +[--version-type {pep440,semver,semver2}] +[--build-metadata BUILD_METADATA][--get-next] +[--allow-no-commit] +[MANUAL_VERSION] + +bump semantic version based on the git log + +positional arguments: +  MANUAL_VERSION        bump to the given version (e.g: 1.5.3) + +options: +  -h, --help            show this help message and exit +  --dry-run             show output to stdout, no commit, no modified files +  --files-only          bump version in the files from the config +  --local-version       bump only the local version portion +  --changelog, -ch      generate the changelog for the newest version +  --no-verify           this option bypasses the pre-commit and commit-msg +                        hooks +  --yes                 accept automatically questions done +  --tag-format TAG_FORMAT +                        the format used to tag the commit and read it, use it +                        in existing projects, wrap around simple quotes +  --bump-message BUMP_MESSAGE +                        template used to create the release commit, useful +                        when working with CI +  --prerelease, -pr {alpha,beta,rc} +                        choose type of prerelease +  --devrelease, -d DEVRELEASE +                        specify non-negative integer for dev. release +  --increment {MAJOR,MINOR,PATCH} +                        manually specify the desired increment +  --increment-mode {linear,exact} +                        set the method by which the new version is chosen. +'linear'(default) guesses the next version based on +                        typical linear version progression, such that bumping +                        of a pre-release with lower precedence than the +                        current pre-release phase maintains the current phase +                        of higher precedence. 'exact' applies the changes that +                        have been specified (or determined from the commit +                        log) without interpretation, such that the increment +                        and pre-release are always honored +  --check-consistency, -cc +                        check consistency among versions defined in commitizen +                        configuration and version_files +  --annotated-tag, -at  create annotated tag instead of lightweight one +  --annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE +                        create annotated tag message +  --gpg-sign, -s        sign tag instead of lightweight one +  --changelog-to-stdout +                        Output changelog to the stdout +  --git-output-to-stderr +                        Redirect git output to stderr +  --retry               retry commit if it fails the 1st time +  --major-version-zero  keep major version at zero, even for breaking changes +  --template, -t TEMPLATE +                        changelog template file name (relative to the current +                        working directory) +  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') +  --file-name FILE_NAME +                        file name of changelog (default: 'CHANGELOG.md') +  --prerelease-offset PRERELEASE_OFFSET +                        start pre-releases with this offset +  --version-scheme {pep440,semver,semver2} +                        choose version scheme +  --version-type {pep440,semver,semver2} +                        Deprecated, use --version-scheme instead +  --build-metadata BUILD_METADATA +                        Add additional build-metadata to the version-number +  --get-next            Determine the next version and write to stdout +  --allow-no-commit     bump version without eligible commits + diff --git a/docs/images/cli_help/cz_commit___help.svg b/docs/images/cli_help/cz_commit___help.svg index 5aea02232f..59cdd12b9b 100644 --- a/docs/images/cli_help/cz_commit___help.svg +++ b/docs/images/cli_help/cz_commit___help.svg @@ -19,95 +19,96 @@ font-weight: 700; } - .terminal-463778956-matrix { + .terminal-905765158-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-463778956-title { + .terminal-905765158-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-463778956-r1 { fill: #c5c8c6 } -.terminal-463778956-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-463778956-r3 { fill: #68a0b3;font-weight: bold } + .terminal-905765158-r1 { fill: #c5c8c6 } +.terminal-905765158-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-905765158-r3 { fill: #98a84b } +.terminal-905765158-r4 { fill: #68a0b3;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -119,33 +120,33 @@ - + - - $ cz commit --help -usage: cz commit [-h][--retry][--no-retry][--dry-run] -[--write-message-to-file FILE_PATH][-s][-a][-e] -[-l MESSAGE_LENGTH_LIMIT][--] - -create new commit - -options: -  -h, --help            show this help message and exit -  --retry               retry last commit -  --no-retry            skip retry if retry_after_failure is set to true -  --dry-run             show output to stdout, no commit, no modified files -  --write-message-to-file FILE_PATH -                        write message to file before committing (can be -                        combined with --dry-run) -  -s, --signoff         sign off the commit -  -a, --all             Tell the command to automatically stage files that -                        have been modified and deleted, but new files you have -                        not told Git about are not affected. -  -e, --edit            edit the commit message before committing -  -l, --message-length-limit MESSAGE_LENGTH_LIMIT -                        length limit of the commit message; 0 for no limit -  --                    Positional arguments separator (recommended) - + + $ cz commit --help +usage: cz commit [-h][--retry][--no-retry][--dry-run] +[--write-message-to-file FILE_PATH][-s][-a][-e] +[-l MESSAGE_LENGTH_LIMIT][--] + +create new commit + +options: +  -h, --help            show this help message and exit +  --retry               retry last commit +  --no-retry            skip retry if retry_after_failure is set to true +  --dry-run             show output to stdout, no commit, no modified files +  --write-message-to-file FILE_PATH +                        write message to file before committing (can be +                        combined with --dry-run) +  -s, --signoff         Deprecated, use 'cz commit -- -s' instead +  -a, --all             Tell the command to automatically stage files that +                        have been modified and deleted, but new files you have +                        not told Git about are not affected. +  -e, --edit            edit the commit message before committing +  -l, --message-length-limit MESSAGE_LENGTH_LIMIT +                        length limit of the commit message; 0 for no limit +  --                    Positional arguments separator (recommended) + From 6b4f8b0c888e0b59a95f12826c6466227e221da8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 9 Jun 2025 22:49:58 +0800 Subject: [PATCH 134/275] docs(CHANGELOG): run `pre-commit run --all-files` to fix CI --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76b6e36e94..9d7a31cc40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - **cli**: update description for deprecate warning - **commit**: emit deprecated warning of cz commit -s -- **Check**: make parameters backward compatiable +- **Check**: make parameters backward compatible - **BaseConfig**: mypy error - **deprecated**: mark deprecate in v5 - **defaults**: add non-capitalized default constants back and deprecated warning From 98a81f89bd3f512067835cfa77fc55701673fbb6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 19:13:10 +0800 Subject: [PATCH 135/275] build: remove not needed importlib-metadata dependency for python >= 3.10 --- poetry.lock | 93 +++----------------------------------------------- pyproject.toml | 3 +- 2 files changed, 6 insertions(+), 90 deletions(-) diff --git a/poetry.lock b/poetry.lock index c5c893931a..e7f0e96e51 100644 --- a/poetry.lock +++ b/poetry.lock @@ -222,7 +222,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["documentation"] -markers = "python_version == \"3.9\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -231,22 +230,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["documentation"] -markers = "python_version != \"3.9\"" -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "colorama" version = "0.4.6" @@ -550,31 +533,6 @@ perf = ["ipython"] test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] -[[package]] -name = "importlib-metadata" -version = "8.7.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version != \"3.9\"" -files = [ - {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, - {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - [[package]] name = "iniconfig" version = "2.1.0" @@ -594,7 +552,6 @@ description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version == \"3.9\"" files = [ {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, @@ -626,46 +583,6 @@ qtconsole = ["qtconsole"] test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] -[[package]] -name = "ipython" -version = "8.37.0" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version != \"3.9\"" -files = [ - {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, - {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt_toolkit = ">=3.0.41,<3.1.0" -pygments = ">=2.4.0" -stack_data = "*" -traitlets = ">=5.13.0" -typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} - -[package.extras] -all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] -kernel = ["ipykernel"] -matplotlib = ["matplotlib"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] - [[package]] name = "jedi" version = "0.19.2" @@ -1104,7 +1021,7 @@ description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" groups = ["dev"] -markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\"" +markers = "sys_platform != \"win32\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1207,7 +1124,7 @@ description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" groups = ["dev"] -markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\"" +markers = "sys_platform != \"win32\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -1817,7 +1734,7 @@ files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] -markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.12\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" @@ -2009,11 +1926,11 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] +markers = "python_version == \"3.9\"" files = [ {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"}, {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"}, ] -markers = {documentation = "python_version == \"3.9\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] @@ -2026,4 +1943,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "f35cf57718e28836cf1e70b33edab9ce623b01a7f2d31d712585554721f6d403" +content-hash = "c98af83d4ce726bbfba04eea3de90e642fb7bdb08bedf0bf1b00b92a1b140e76" diff --git a/pyproject.toml b/pyproject.toml index 6990741957..ab3ba39ee1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,8 +23,7 @@ dependencies = [ "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'", "charset-normalizer (>=2.1.0,<4)", # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility - "importlib-metadata >=8.0.0,!=8.7.0,<9.0.0 ; python_version == '3.9'", # importlib-metadata@8.7.0 + python3.9 breaks our unit test - "importlib-metadata >=8.0.0,<9.0.0 ; python_version != '3.9'", + "importlib-metadata >=8.0.0,<8.7.0 ; python_version < '3.10'", ] keywords = ["commitizen", "conventional", "commits", "git"] # See also: https://pypi.org/classifiers/ From db94c4dbc12f153cb340654ba7f2458c3c652d3d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 19:31:34 +0800 Subject: [PATCH 136/275] docs(defaults): deprecate type Questions --- commitizen/defaults.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index a49d6d9428..94d4d97b22 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -8,9 +8,6 @@ from commitizen.question import CzQuestion -# Type -Questions = Iterable[MutableMapping[str, Any]] # TODO: deprecate this? - class CzSettings(TypedDict, total=False): bump_pattern: str @@ -161,6 +158,10 @@ def get_tag_regexes( } +# Type +Questions = Iterable[MutableMapping[str, Any]] # TODO: remove this in v5 + + def __getattr__(name: str) -> Any: # PEP-562: deprecate module-level variable @@ -176,6 +177,7 @@ def __getattr__(name: str) -> Any: "change_type_order": (CHANGE_TYPE_ORDER, "CHANGE_TYPE_ORDER"), "encoding": (ENCODING, "ENCODING"), "name": (DEFAULT_SETTINGS["name"], "DEFAULT_SETTINGS['name']"), + "Questions": (Questions, "Iterable[CzQuestion]"), } if name in deprecated_vars: value, replacement = deprecated_vars[name] From f80292e15be28014c820eabc5ec652f46fe5a3a4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 20:44:06 +0800 Subject: [PATCH 137/275] docs(customization.md): fix grammar mistake, add title to code blocks --- docs/customization.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index e97558a308..df77171077 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -10,7 +10,7 @@ The basic steps are: Example: -```toml +```toml title="pyproject.toml" [tool.commitizen] name = "cz_customize" @@ -50,7 +50,7 @@ message = "Do you want to add body message in commit?" The equivalent example for a json config file: -```json +```json title=".cz.json" { "commitizen": { "name": "cz_customize", @@ -106,7 +106,7 @@ The equivalent example for a json config file: And the correspondent example for a yaml file: -```yaml +```yaml title=".cz.yaml" commitizen: name: cz_customize customize: @@ -149,16 +149,16 @@ commitizen: | Parameter | Type | Default | Description | | ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | +| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | | `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | -| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | -| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | -| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | -| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | -| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | +| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | +| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | +| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | +| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | +| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | | `bump_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | | `bump_pattern` | `str` | `None` | (OPTIONAL) Regex to extract information from commit (subject and body) | -| `change_type_order`| `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | +| `change_type_order` | `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | | `commit_parser` | `str` | `None` | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec] | | `changelog_pattern` | `str` | `None` | (OPTIONAL) Regex to understand which commits to include in the changelog | | `change_type_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry | @@ -215,7 +215,7 @@ Create a Python module, for example `cz_jira.py`. Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. -```python +```python title="cz_jira.py" from commitizen.cz.base import BaseCommitizen from commitizen.defaults import Questions @@ -259,7 +259,7 @@ class JiraCz(BaseCommitizen): The next file required is `setup.py` modified from flask version. -```python +```python title="setup.py" from setuptools import setup setup( @@ -295,7 +295,7 @@ You need to define 2 parameters inside your custom `BaseCommitizen`. Let's see an example. -```python +```python title="cz_strange.py" from commitizen.cz.base import BaseCommitizen @@ -315,7 +315,7 @@ cz -n cz_strange bump ### Custom changelog generator The changelog generator should just work in a very basic manner without touching anything. -You can customize it of course, and this are the variables you need to add to your custom `BaseCommitizen`. +You can customize it of course, and the following variables are the ones you need to add to your custom `BaseCommitizen`. | Parameter | Type | Required | Description | | -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -326,7 +326,7 @@ You can customize it of course, and this are the variables you need to add to yo | `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | | `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release -```python +```python title="cz_strange.py" from commitizen.cz.base import BaseCommitizen import chat import compliance @@ -376,8 +376,6 @@ class StrangeCommitizen(BaseCommitizen): return full_changelog ``` -[changelog-des]: ./commands/changelog.md#description - ### Raise Customize Exception If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`. @@ -528,3 +526,4 @@ by: [template-config]: config.md#template [extras-config]: config.md#extras +[changelog-des]: ./commands/changelog.md#description From 7ef1f7e8d6519270df63387b50c2c0cd2591c832 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 11 Jun 2025 14:21:02 +0800 Subject: [PATCH 138/275] docs(bump.md): add titles to code blocks and fix minor grammar issues --- docs/commands/bump.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 8219cc3461..1320e30a7a 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -31,7 +31,7 @@ cz bump --version-scheme semver ``` 2. Configuration file: -```toml +```toml title="pyproject.toml" [tool.commitizen] version_scheme = "semver" ``` @@ -113,7 +113,7 @@ Note that as per [semantic versioning spec](https://semver.org/#spec-item-9) For example, the following versions (using the [PEP 440](https://peps.python.org/pep-0440/) scheme) are ordered by their precedence and showcase how a release might flow through a development cycle: -- `1.0.0` is the current published version +- `1.0.0` is the currently published version - `1.0.1a0` after committing a `fix:` for pre-release - `1.1.0a1` after committing an additional `feat:` for pre-release - `1.1.0b0` after bumping a beta release @@ -153,7 +153,7 @@ cz bump --check-consistency For example, if we have `pyproject.toml` -```toml +```toml title="pyproject.toml" [tool.commitizen] version = "1.21.0" version_files = [ @@ -162,15 +162,16 @@ version_files = [ ] ``` -`src/__version__.py`, +`src/__version__.py` -```python + +```python title="src/__version__.py" __version__ = "1.21.0" ``` -and `setup.py`. +and `setup.py` -```python +```python title="setup.py" from setuptools import setup setup(..., version="1.0.5", ...) @@ -193,7 +194,7 @@ cz bump --local-version For example, if we have `pyproject.toml` -```toml +```toml title="pyproject.toml" [tool.commitizen] version = "5.3.5+0.1.0" ``` @@ -454,7 +455,7 @@ In your `pyproject.toml` or `.cz.toml` tag_format = "v$major.$minor.$patch$prerelease" ``` -The variables must be preceded by a `$` sign and optionally can be wrapped in `{}` . Default is `$version`. +The variables must be preceded by a `$` sign and optionally can be wrapped in `{}`. The default is `$version`. Supported variables: @@ -483,7 +484,7 @@ Some examples `pyproject.toml`, `.cz.toml` or `cz.toml` -```toml +```toml title="pyproject.toml" [tool.commitizen] version_files = [ "src/__version__.py", @@ -496,8 +497,7 @@ This means that it will find a file `setup.py` and will only make a change in a line containing the `version` substring. !!! note - Files can be specified using relative (to the execution) paths, absolute paths - or glob patterns. + Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. --- @@ -516,7 +516,7 @@ Some examples `pyproject.toml`, `.cz.toml` or `cz.toml` -```toml +```toml title="pyproject.toml" [tool.commitizen] bump_message = "release $current_version → $new_version [skip-ci]" ``` @@ -529,7 +529,7 @@ When set to `true` the changelog is always updated incrementally when running `c Defaults to: `false` -```toml +```toml title="pyproject.toml" [tool.commitizen] update_changelog_on_bump = true ``` @@ -540,7 +540,7 @@ update_changelog_on_bump = true When set to `true`, Commitizen will create annotated tags. -```toml +```toml title="pyproject.toml" [tool.commitizen] annotated_tag = true ``` @@ -551,7 +551,7 @@ annotated_tag = true When set to `true`, Commitizen will create gpg signed tags. -```toml +```toml title="pyproject.toml" [tool.commitizen] gpg_sign = true ``` @@ -565,7 +565,7 @@ Useful during the initial development stage of your project. Defaults to: `false` -```toml +```toml title="pyproject.toml" [tool.commitizen] major_version_zero = true ``` @@ -591,7 +591,7 @@ execution of the script, some environment variables are available: | `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATH` release | | `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | -```toml +```toml title="pyproject.toml" [tool.commitizen] pre_bump_hooks = [ "scripts/generate_documentation.sh" @@ -618,7 +618,7 @@ release. During execution of the script, some environment variables are availabl | `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATH` release | | `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | -```toml +```toml title="pyproject.toml" [tool.commitizen] post_bump_hooks = [ "scripts/slack_notification.sh" @@ -631,7 +631,7 @@ Offset with which to start counting prereleases. Defaults to: `0` -```toml +```toml title="pyproject.toml" [tool.commitizen] prerelease_offset = 1 ``` @@ -651,7 +651,7 @@ Options: `pep440`, `semver`, `semver2` Defaults to: `pep440` -```toml +```toml title="pyproject.toml" [tool.commitizen] version_scheme = "semver" ``` From 2c230107f39778a74d61c2c39a808f0ad76a6cb4 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 13 Jun 2025 23:22:08 +0800 Subject: [PATCH 139/275] ci(github-actions): set organization to true for JamesIves/github-sponsors-readme-action@v1 --- .github/workflows/docspublish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index a871d3c379..e6bb9c7226 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -68,6 +68,7 @@ jobs: with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN_FOR_ORG }} file: "docs/README.md" + organization: true - name: Push doc to Github Page uses: peaceiris/actions-gh-pages@v4 with: From d0c89afc74baa5ca9ea79b8a2846b7682482edbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:17:47 +0000 Subject: [PATCH 140/275] ci(deps): bump dawidd6/action-homebrew-bump-formula from 4 to 5 Bumps [dawidd6/action-homebrew-bump-formula](https://github.com/dawidd6/action-homebrew-bump-formula) from 4 to 5. - [Release notes](https://github.com/dawidd6/action-homebrew-bump-formula/releases) - [Commits](https://github.com/dawidd6/action-homebrew-bump-formula/compare/v4...v5) --- updated-dependencies: - dependency-name: dawidd6/action-homebrew-bump-formula dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/homebrewpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index 443a13b1ba..f5d8004e73 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -24,7 +24,7 @@ jobs: run: | echo "project_version=$(cz version --project)" >> $GITHUB_ENV - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@v4 + uses: dawidd6/action-homebrew-bump-formula@v5 with: token: ${{secrets.PERSONAL_ACCESS_TOKEN}} formula: commitizen From b2e9c1be5ead017a9c40dc8918dc65e8a86812c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= Date: Fri, 11 Jul 2025 20:54:37 -0600 Subject: [PATCH 141/275] docs(config.md): Document glob pattern support in `version_files` It was added in https://github.com/commitizen-tools/commitizen/pull/1070 but never documented AFAICT. --- docs/commands/bump.md | 5 +++-- docs/config.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 1320e30a7a..548f6e3616 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -472,7 +472,7 @@ Supported variables: ### `version_files` \* -It is used to identify the files which should be updated with the new version. +It is used to identify the files or glob patterns which should be updated with the new version. It is also possible to provide a pattern for each file, separated by colons (`:`). Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping, @@ -488,7 +488,8 @@ Some examples [tool.commitizen] version_files = [ "src/__version__.py", - "setup.py:version" + "packages/*/pyproject.toml:version", + "setup.py:version", ] ``` diff --git a/docs/config.md b/docs/config.md index 5ca2c5d788..649881daec 100644 --- a/docs/config.md +++ b/docs/config.md @@ -24,7 +24,7 @@ Type: `list` Default: `[ ]` -Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] +Files (or glob patterns) where the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] ### `version_provider` From c5a9f67cc26b330dcaca1ba697c4d37f98fd4265 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Sun, 13 Jul 2025 22:16:57 -0400 Subject: [PATCH 142/275] docs(third-party-commitizen.md): Add cz-path info --- docs/third-party-commitizen.md | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md index dc9b539c85..0c35a31557 100644 --- a/docs/third-party-commitizen.md +++ b/docs/third-party-commitizen.md @@ -145,3 +145,56 @@ commitizen: version_provider: deno-provider version_scheme: semver ``` + +### [cz-path](https://pypi.org/project/cz-path/) + +Provides prefix choices for commit messages based on staged files (Git only). +For example, if the staged files are `component/z/a.ts` and `component/z/b.ts`, +the path prefix option will be `component/z` and commit message might look like: +`component/z/: description of changes`. If only one file is staged, the extension +is removed in the prefix. + +#### Installation + +```sh +pip install cz-path +``` + +#### Usage + +Add `cz-path` to your configuration file. + +Example for `.cz.json`: + +```json +{ + "commitizen": { + "name": "cz_path", + "remove_path_prefixes": ["src", "module_name"] + } +} +``` + +The default value for `remove_path_prefixes` is `["src"]`. Adding `/` to the +prefixes is not required. + +#### Example session + +```plain + $ git add .vscode/ + $ cz -n cz_path c +? Prefix: (Use arrow keys) + » .vscode + .vscode/ + project + (empty) +? Prefix: .vscode +? Commit title: adjust settings + +.vscode: adjust settings + +[main 0000000] .vscode: adjust settings + 2 files changed, 1 insertion(+), 11 deletions(-) + +Commit successful! +``` From baa31a6c539795fd819fcd13548ad75c1d1249b6 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 15 Jul 2025 18:45:12 +0200 Subject: [PATCH 143/275] ci(github-actions): fix sponsorship page generation --- .github/workflows/docspublish.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index e6bb9c7226..6418a20fa7 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -4,6 +4,7 @@ on: push: branches: - master + workflow_dispatch: jobs: update-cli-screenshots: @@ -58,22 +59,19 @@ jobs: python -m pip install -U pip poetry poethepoet poetry --version poetry install --no-root --only documentation - - name: Build docs - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - poetry doc:build - name: Generate Sponsors 💖 uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN_FOR_ORG }} file: "docs/README.md" organization: true - - name: Push doc to Github Page - uses: peaceiris/actions-gh-pages@v4 + - name: Build docs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + poetry doc:build + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4 with: - personal_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - publish_branch: gh-pages - publish_dir: ./site - user_name: "github-actions[bot]" - user_email: "github-actions[bot]@users.noreply.github.com" + folder: ./site # The folder the action should deploy. + branch: gh-pages From eb2199967e6a69779a8093395d0ac38915f8c36d Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:55:35 +0800 Subject: [PATCH 144/275] docs(README): install into dev dependency when using uv --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 31f2a77d15..c944855f38 100644 --- a/docs/README.md +++ b/docs/README.md @@ -111,7 +111,7 @@ poetry add commitizen --dev **Using uv:** ```bash -uv add commitizen +uv add --dev commitizen ``` **Using pdm:** From b8f9bf6bef1aab6b9580fe4425811badbac36a6c Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:57:15 +0800 Subject: [PATCH 145/275] docs(README): ensure line break --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index c944855f38..fa3fab2239 100644 --- a/docs/README.md +++ b/docs/README.md @@ -63,6 +63,7 @@ Before installing Commitizen, ensure you have: #### Global Installation (Recommended) The recommended way to install Commitizen is using [`pipx`](https://pipx.pypa.io/) or [`uv`](https://docs.astral.sh/uv/), which ensures a clean, isolated installation: + **Using pipx:** ```bash # Install Commitizen From 19306741b3634189a7c3c6f58aef6fd2c6302fa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:11:26 +0000 Subject: [PATCH 146/275] ci(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/bumpversion.yml | 2 +- .github/workflows/docspublish.yml | 4 ++-- .github/workflows/homebrewpublish.yml | 2 +- .github/workflows/label_pr.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- .github/workflows/pythonpublish.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bumpversion.yml b/.github/workflows/bumpversion.yml index ed0c8cffce..ba48d677e0 100644 --- a/.github/workflows/bumpversion.yml +++ b/.github/workflows/bumpversion.yml @@ -12,7 +12,7 @@ jobs: name: "Bump version and create changelog with commitizen" steps: - name: Check out - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index 6418a20fa7..7a2109266f 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -10,7 +10,7 @@ jobs: update-cli-screenshots: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest needs: update-cli-screenshots steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index f5d8004e73..c9a1b35081 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -12,7 +12,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index 176d7cc794..a74a507bba 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -9,7 +9,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: sparse-checkout: | .github/labeler.yml diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b50b02a681..00794babbf 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -10,7 +10,7 @@ jobs: platform: [ubuntu-22.04, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index dc522bc0fd..3976d309dd 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -9,7 +9,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 From 9e6830c97b498d1ffc7a52b66908d82764668c44 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 20:20:26 +0800 Subject: [PATCH 147/275] docs(config.md): fix format issue, add period and add title to example config code blocks --- docs/config.md | 186 ++++++++++++++++++++++--------------------------- 1 file changed, 83 insertions(+), 103 deletions(-) diff --git a/docs/config.md b/docs/config.md index 649881daec..05f45a1a80 100644 --- a/docs/config.md +++ b/docs/config.md @@ -4,58 +4,56 @@ ### `name` -Type: `str` +- Type: `str` +- Default: `"cz_conventional_commits"` -Default: `"cz_conventional_commits"` - -Name of the committing rules to use +Name of the committing rules to use. ### `version` -Type: `str` - -Default: `None` +- Type: `str` +- Default: `None` -Current version. Example: "0.1.2". Required if you use `version_provider = "commitizen"`. +Current version. Example: `"0.1.2"`. Required if you use `version_provider = "commitizen"`. ### `version_files` -Type: `list` - -Default: `[ ]` +- Type: `list` +- Default: `[]` Files (or glob patterns) where the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] ### `version_provider` -Type: `str` - -Default: `commitizen` +- Type: `str` +- Default: `commitizen` -Version provider used to read and write version [Read more](#version-providers) +Version provider used to read and write version. [Read more](#version-providers) ### `version_scheme` -Type: `str` +- Type: `str` +- Default: `pep440` -Default: `pep440` +Select a version scheme from the following options: + +- `pep440` +- `semver` +- `semver2` -Select a version scheme from the following options [`pep440`, `semver`, `semver2`]. Useful for non-python projects. [Read more][version-scheme] ### `tag_format` -Type: `str` - -Default: `$version` +- Type: `str` +- Default: `$version` Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] ### `legacy_tag_formats` -Type: `list` - -Default: `[ ]` +- Type: `list` +- Default: `[]` Legacy git tag formats, useful for old projects that changed tag format. Tags matching those formats will be recognized as version tags and be included in the changelog. @@ -63,9 +61,8 @@ Each entry uses the syntax as [`tag_format`](#tag_format). [Read more][tag_forma ### `ignored_tag_formats` -Type: `list` - -Default: `[ ]` +- Type: `list` +- Default: `[]` Tags matching those formats will be totally ignored and won't raise a warning. Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*` @@ -73,175 +70,158 @@ that will match everything (non-greedy). [Read more][tag_format] ### `update_changelog_on_bump` -Type: `bool` +- Type: `bool` +- Default: `False` -Default: `false` - -Create changelog when running `cz bump` +Create changelog when running `cz bump`. ### `gpg_sign` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` Use gpg signed tags instead of lightweight tags. ### `annotated_tag` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] ### `bump_message` -Type: `str` - -Default: `None` +- Type: `str` +- Default: `None` -Create custom commit message, useful to skip CI. [Read more][bump_message] +Create custom commit message. Useful to skip CI. [Read more][bump_message] ### `retry_after_failure` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` Automatically retry failed commit when running `cz commit`. [Read more][retry_after_failure] ### `allow_abort` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` -Disallow empty commit messages, useful in CI. [Read more][allow_abort] +Disallow empty commit messages. Useful in CI. [Read more][allow_abort] ### `allowed_prefixes` -Type: `list` -Default: `[ "Merge", "Revert", "Pull request", "fixup!", "squash!"]` -Allow some prefixes and do not try to match the regex when checking the message [Read more][allowed_prefixes] +- Type: `list` +- Default: `["Merge", "Revert", "Pull request", "fixup!", "squash!"]` -### `changelog_file` +List of prefixes that commitizen ignores when verifying messages. [Read more][allowed_prefixes] -Type: `str` +### `changelog_file` -Default: `CHANGELOG.md` +- Type: `str` +- Default: `CHANGELOG.md` Filename of exported changelog ### `changelog_format` -Type: `str` +- Type: `str` +- Default: `None` -Default: None - -Format used to parse and generate the changelog, If not specified, guessed from [`changelog_file`](#changelog_file). +Format used to parse and generate the changelog. If not specified, guessed from [`changelog_file`](#changelog_file). ### `changelog_incremental` -Type: `bool` +- Type: `bool` +- Default: `False` -Default: `false` +Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. -Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` +!!! note + When doing `cz bump --changelog` this is automatically set to `True` ### `changelog_start_rev` -Type: `str` - -Default: `None` +- Type: `str` +- Default: `None` Start from a given git rev to generate the changelog ### `changelog_merge_prerelease` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog. ### `style` -Type: `list` - -see above +- Type: `list` +- Default: `[]` Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] ### `customize` -Type: `dict` - -Default: `None` +- Type: `dict` +- Default: `None` **This is only supported when config through `toml`.** Custom rules for committing and bumping. [Read more][customization] ### `use_shortcuts` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` If enabled, Commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] ### `major_version_zero` -Type: `bool` - -Default: `false` +- Type: `bool` +- Default: `False` -When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero] +If enabled, breaking changes on a `0.x` will remain as a `0.x` version. Otherwise, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero] ### `prerelease_offset` -Type: `int` +- Type: `int` +- Default: `0` -Default: `0` - -In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [Read more][prerelease-offset] +In some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. [Read more][prerelease-offset] ### `pre_bump_hooks` -Type: `list[str]` - -Default: `[]` +- Type: `list[str]` +- Default: `[]` Calls the hook scripts **before** bumping version. [Read more][pre_bump_hooks] ### `post_bump_hooks` -Type: `list[str]` - -Default: `[]` +- Type: `list[str]` +- Default: `[]` Calls the hook scripts **after** bumping the version. [Read more][post_bump_hooks] ### `encoding` -Type: `str` - -Default: `utf-8` +- Type: `str` +- Default: `"utf-8"` Sets the character encoding to be used when parsing commit messages. [Read more][encoding] ### `template` -Type: `str` - -Default: `None` (provided by plugin) +- Type: `str` +- Default: `None` (provided by plugin) Provide custom changelog jinja template path relative to the current working directory. [Read more][template-customization] ### `extras` -Type: `dict[str, Any]` - -Default: `{}` +- Type: `dict[str, Any]` +- Default: `{}` Provide extra variables to the changelog template. [Read more][template-customization] @@ -255,7 +235,7 @@ You can also create a `.cz.toml` or `cz.toml` file at the root of your project f Example configuration: -```toml +```toml title=".cz.toml" [tool.commitizen] name = "cz_conventional_commits" version = "0.1.0" @@ -282,7 +262,7 @@ style = [ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects. -```json +```json title=".cz.json" { "commitizen": { "name": "cz_conventional_commits", @@ -308,7 +288,7 @@ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects. -```yaml +```yaml title=".cz.yaml" commitizen: name: cz_conventional_commits version: 0.1.0 @@ -371,7 +351,7 @@ version_provider = "pep621" You can add your own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. -Here is a quick example of a `my-provider` provider reading and writing version in a `VERSION` file. +Here is a quick example of a provider `my_provider.py`, reading and writing version in a `VERSION` file. ```python title="my_provider.py" from pathlib import Path From 75a97e56bddac011e0d0eca3018fc0f873edbe4c Mon Sep 17 00:00:00 2001 From: "D. Danchev" <12420863+danchev@users.noreply.github.com> Date: Wed, 27 Aug 2025 14:19:27 -0500 Subject: [PATCH 148/275] build(deps): exclude prompt_toolkit 3.0.52 due to questionary compatibility issue Issue: https://github.com/tmbo/questionary/issues/454 --- poetry.lock | 4 ++-- pyproject.toml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index e7f0e96e51..f8fb9aa803 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "argcomplete" @@ -1943,4 +1943,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "c98af83d4ce726bbfba04eea3de90e642fb7bdb08bedf0bf1b00b92a1b140e76" +content-hash = "0c750c9d3befb187e16df9b2524cedd8319da36c1311ac74d083b9e8ff54ff1d" diff --git a/pyproject.toml b/pyproject.toml index ab3ba39ee1..82fe8b6e33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ readme = "docs/README.md" requires-python = ">=3.9,<4.0" dependencies = [ "questionary (>=2.0,<3.0)", + "prompt_toolkit!=3.0.52", # Exclude transitive dependency due to known issue in questionary: https://github.com/tmbo/questionary/issues/454 "decli (>=0.6.0,<1.0)", "colorama (>=0.4.1,<1.0)", "termcolor (>=1.1.0,<4.0.0)", From d7fcd97726c85b582c2b42e810531bc23e6f695b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 01:05:30 +0000 Subject: [PATCH 149/275] ci(deps): bump actions/labeler from 5 to 6 Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/label_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index a74a507bba..fda20e4173 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -14,7 +14,7 @@ jobs: sparse-checkout: | .github/labeler.yml sparse-checkout-cone-mode: false - - uses: actions/labeler@v5 + - uses: actions/labeler@v6 with: configuration-path: .github/labeler.yml - name: Label based on PR title From adbe3cac1133a36272c302c88fe06ca9a6afc58d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 01:05:35 +0000 Subject: [PATCH 150/275] ci(deps): bump actions/setup-python from 5 to 6 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docspublish.yml | 4 ++-- .github/workflows/homebrewpublish.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- .github/workflows/pythonpublish.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index 7a2109266f..eb52dc46a8 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -15,7 +15,7 @@ jobs: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies @@ -51,7 +51,7 @@ jobs: run: | git pull origin master - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index c9a1b35081..8c315b6322 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -14,7 +14,7 @@ jobs: - name: Checkout uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 00794babbf..7a574c9e0a 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 3976d309dd..e70690a1a3 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -14,7 +14,7 @@ jobs: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies From 0fbc28718b96850257d9587ac8276383700c043f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 01:05:38 +0000 Subject: [PATCH 151/275] ci(deps): bump actions/github-script from 7 to 8 Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/label_issues.yml | 2 +- .github/workflows/label_pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml index e74a36c4bd..359c50d65a 100644 --- a/.github/workflows/label_issues.yml +++ b/.github/workflows/label_issues.yml @@ -12,7 +12,7 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 with: script: | const issue = await github.rest.issues.get({ diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index fda20e4173..c1a3df2c70 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -18,7 +18,7 @@ jobs: with: configuration-path: .github/labeler.yml - name: Label based on PR title - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const title = context.payload.pull_request.title.toLowerCase(); From 1f2d26a45a5d4bd9cb5ae44b523334b69a183a81 Mon Sep 17 00:00:00 2001 From: vemilano <230173219+vemilano@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:24:21 +0200 Subject: [PATCH 152/275] fix: cargo workspaces --- commitizen/providers/cargo_provider.py | 41 +++++++++++++++++++++----- tests/providers/test_cargo_provider.py | 16 ++++++++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index d453a4aa5b..53bf77caac 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -1,5 +1,6 @@ from __future__ import annotations +import glob from pathlib import Path import tomlkit @@ -22,8 +23,10 @@ def lock_file(self) -> Path: return Path() / self.lock_filename def get(self, document: tomlkit.TOMLDocument) -> str: + # If there is a root package, change its version (but not the workspace version) try: return document["package"]["version"] # type: ignore[index,return-value] + # Else, bump the workspace version except tomlkit.exceptions.NonExistentKey: ... return document["workspace"]["package"]["version"] # type: ignore[index,return-value] @@ -43,15 +46,39 @@ def set_version(self, version: str) -> None: def set_lock_version(self, version: str) -> None: cargo_toml_content = tomlkit.parse(self.file.read_text()) + cargo_lock_content = tomlkit.parse(self.lock_file.read_text()) + packages: tomlkit.items.AoT = cargo_lock_content["package"] # type: ignore[assignment] try: package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + for i, package in enumerate(packages): + if package["name"] == package_name: + cargo_lock_content["package"][i]["version"] = version # type: ignore[index] + break except tomlkit.exceptions.NonExistentKey: - package_name = cargo_toml_content["workspace"]["package"]["name"] # type: ignore[index] + workspace_members = ( + cargo_toml_content.get("workspace", {}) + .get("package", {}) + .get("members", []) + ) + members_inheriting = [] + + for member in workspace_members: + matched_paths = glob.glob(member, recursive=True) + for path in matched_paths: + cargo_file = Path(path) / "Cargo.toml" + cargo_toml_content = tomlkit.parse(cargo_file.read_text()) + try: + version_workspace = cargo_toml_content["package"]["version"][ # type: ignore[index] + "workspace" + ] + if version_workspace is True: + package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + members_inheriting.append(package_name) + except tomlkit.exceptions.NonExistentKey: + continue + + for i, package in enumerate(packages): + if package["name"] in members_inheriting: + cargo_lock_content["package"][i]["version"] = version # type: ignore[index] - cargo_lock_content = tomlkit.parse(self.lock_file.read_text()) - packages: tomlkit.items.AoT = cargo_lock_content["package"] # type: ignore[assignment] - for i, package in enumerate(packages): - if package["name"] == package_name: - cargo_lock_content["package"][i]["version"] = version # type: ignore[index] - break self.lock_file.write_text(tomlkit.dumps(cargo_lock_content)) diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index 4b20c6ea53..8a3b1b8da9 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path from textwrap import dedent @@ -23,13 +24,19 @@ CARGO_WORKSPACE_TOML = """\ [workspace.package] -name = "whatever" +members = ["whatever"] version = "0.1.0" """ +CARGO_WORKSPACE_MEMBER_TOML = """\ +[package] +name = "whatever" +version.workspace = true +""" + CARGO_WORKSPACE_TOML_EXPECTED = """\ [workspace.package] -name = "whatever" +members = ["whatever"] version = "42.1" """ @@ -115,6 +122,11 @@ def test_cargo_provider_with_lock( file = chdir / filename file.write_text(dedent(toml_content)) + member_folder = chdir / "whatever" + os.mkdir(member_folder) + member_file = member_folder / "Cargo.toml" + member_file.write_text(dedent(CARGO_WORKSPACE_MEMBER_TOML)) + lock_filename = CargoProvider.lock_filename lock_file = chdir / lock_filename lock_file.write_text(dedent(lock_content)) From 44bce6029e93f214e6014a1560be769d15632a92 Mon Sep 17 00:00:00 2001 From: vemilano <230173219+vemilano@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:30:45 +0200 Subject: [PATCH 153/275] fix: members in workspace, use exclude --- commitizen/providers/cargo_provider.py | 45 +++--- tests/providers/test_cargo_provider.py | 184 +++++++++++++++++++++++-- 2 files changed, 201 insertions(+), 28 deletions(-) diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 53bf77caac..afeb47f58b 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -1,5 +1,6 @@ from __future__ import annotations +import fnmatch import glob from pathlib import Path @@ -8,6 +9,13 @@ from commitizen.providers.base_provider import TomlProvider +def matches_exclude(path: str, exclude_patterns: list[str]) -> bool: + for pattern in exclude_patterns: + if fnmatch.fnmatch(path, pattern): + return True + return False + + class CargoProvider(TomlProvider): """ Cargo version management @@ -55,27 +63,30 @@ def set_lock_version(self, version: str) -> None: cargo_lock_content["package"][i]["version"] = version # type: ignore[index] break except tomlkit.exceptions.NonExistentKey: - workspace_members = ( - cargo_toml_content.get("workspace", {}) - .get("package", {}) - .get("members", []) + workspace_members = cargo_toml_content.get("workspace", {}).get( + "members", [] + ) + excluded_workspace_members = cargo_toml_content.get("workspace", {}).get( + "exclude", [] ) members_inheriting = [] for member in workspace_members: - matched_paths = glob.glob(member, recursive=True) - for path in matched_paths: - cargo_file = Path(path) / "Cargo.toml" - cargo_toml_content = tomlkit.parse(cargo_file.read_text()) - try: - version_workspace = cargo_toml_content["package"]["version"][ # type: ignore[index] - "workspace" - ] - if version_workspace is True: - package_name = cargo_toml_content["package"]["name"] # type: ignore[index] - members_inheriting.append(package_name) - except tomlkit.exceptions.NonExistentKey: - continue + for path in glob.glob(member, recursive=True): + if not matches_exclude(path, excluded_workspace_members): + cargo_file = Path(path) / "Cargo.toml" + cargo_toml_content = tomlkit.parse(cargo_file.read_text()) + try: + version_workspace = cargo_toml_content["package"][ + "version" + ][ # type: ignore[index] + "workspace" + ] + if version_workspace is True: + package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + members_inheriting.append(package_name) + except tomlkit.exceptions.NonExistentKey: + continue for i, package in enumerate(packages): if package["name"] in members_inheriting: diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index 8a3b1b8da9..5e7b2d8cb7 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -23,20 +23,64 @@ """ CARGO_WORKSPACE_TOML = """\ +[workspace] +members = ["member1", "folder/member2", "crates/*"] +exclude = ["crates/member4", "folder/member5"] + [workspace.package] -members = ["whatever"] version = "0.1.0" """ -CARGO_WORKSPACE_MEMBER_TOML = """\ +CARGO_WORKSPACE_MEMBERS = [ + { + "path": "member1", + "content": """\ [package] -name = "whatever" +name = "member1" version.workspace = true -""" +""", + }, + { + "path": "folder/member2", + "content": """\ +[package] +name = "member2" +version.workspace = "1.1.1" +""", + }, + { + "path": "crates/member3", + "content": """\ +[package] +name = "member3" +version.workspace = true +""", + }, + { + "path": "crates/member4", + "content": """\ +[package] +name = "member4" +version.workspace = "2.2.2" +""", + }, + { + "path": "folder/member5", + "content": """\ +[package] +name = "member5" +version.workspace = "3.3.3" +""", + }, +] + CARGO_WORKSPACE_TOML_EXPECTED = """\ +[workspace] +members = ["member1", "folder/member2", "crates/*"] +exclude = ["crates/member4", "folder/member5"] + [workspace.package] -members = ["whatever"] version = "42.1" """ @@ -66,6 +110,120 @@ ] """ +CARGO_WORKSPACE_LOCK = """\ +[[package]] +name = "member1" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member3" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member4" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member5" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] +""" + +CARGO_WORKSPACE_LOCK_EXPECTED = """\ +[[package]] +name = "member1" +version = "42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member3" +version = "42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member4" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member5" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] +""" + @pytest.mark.parametrize( "content, expected", @@ -104,9 +262,9 @@ def test_cargo_provider( ), ( CARGO_WORKSPACE_TOML, - CARGO_LOCK, + CARGO_WORKSPACE_LOCK, CARGO_WORKSPACE_TOML_EXPECTED, - CARGO_LOCK_EXPECTED, + CARGO_WORKSPACE_LOCK_EXPECTED, ), ), ) @@ -122,10 +280,14 @@ def test_cargo_provider_with_lock( file = chdir / filename file.write_text(dedent(toml_content)) - member_folder = chdir / "whatever" - os.mkdir(member_folder) - member_file = member_folder / "Cargo.toml" - member_file.write_text(dedent(CARGO_WORKSPACE_MEMBER_TOML)) + # Create workspace members + os.mkdir(chdir / "crates") + os.mkdir(chdir / "folder") + for i in range(0, 5): + member_folder = Path(CARGO_WORKSPACE_MEMBERS[i]["path"]) + os.mkdir(member_folder) + member_file = member_folder / "Cargo.toml" + member_file.write_text(dedent(CARGO_WORKSPACE_MEMBERS[i]["content"])) lock_filename = CargoProvider.lock_filename lock_file = chdir / lock_filename From 1cde9ee705928c75873a33ad738d61a83447482f Mon Sep 17 00:00:00 2001 From: vemilano <230173219+vemilano@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:15:37 +0200 Subject: [PATCH 154/275] refactor: reduce code indentation --- commitizen/providers/cargo_provider.py | 27 +++++++++++++------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index afeb47f58b..3e8c95e22a 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -73,20 +73,19 @@ def set_lock_version(self, version: str) -> None: for member in workspace_members: for path in glob.glob(member, recursive=True): - if not matches_exclude(path, excluded_workspace_members): - cargo_file = Path(path) / "Cargo.toml" - cargo_toml_content = tomlkit.parse(cargo_file.read_text()) - try: - version_workspace = cargo_toml_content["package"][ - "version" - ][ # type: ignore[index] - "workspace" - ] - if version_workspace is True: - package_name = cargo_toml_content["package"]["name"] # type: ignore[index] - members_inheriting.append(package_name) - except tomlkit.exceptions.NonExistentKey: - continue + if matches_exclude(path, excluded_workspace_members): + continue + cargo_file = Path(path) / "Cargo.toml" + cargo_toml_content = tomlkit.parse(cargo_file.read_text()) + try: + version_workspace = cargo_toml_content["package"]["version"][ # type: ignore[index] + "workspace" + ] + if version_workspace is True: + package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + members_inheriting.append(package_name) + except tomlkit.exceptions.NonExistentKey: + continue for i, package in enumerate(packages): if package["name"] in members_inheriting: From 186dd18042ad30269e157e17db271a34b71e3bcd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Sep 2025 16:49:37 +0000 Subject: [PATCH 155/275] =?UTF-8?q?bump:=20version=204.8.3=20=E2=86=92=204?= =?UTF-8?q?.8.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 11 +++++++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e09fb63e8..3dfd50ee68 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.8.3 # automatically updated by Commitizen + rev: v4.8.4 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d7a31cc40..5723e11274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## v4.8.4 (2025-09-05) + +### Fix + +- members in workspace, use exclude +- cargo workspaces + +### Refactor + +- reduce code indentation + ## v4.8.3 (2025-06-09) ### Fix diff --git a/commitizen/__version__.py b/commitizen/__version__.py index af82c57223..adba53e1e0 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.8.3" +__version__ = "4.8.4" diff --git a/pyproject.toml b/pyproject.toml index 82fe8b6e33..ebb2244770 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.8.3" +version = "4.8.4" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.8.3" +version = "4.8.4" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 5a21f833e8cf2a56d4218972471afbe98cca1bee Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 21:54:37 +0800 Subject: [PATCH 156/275] refactor(ParseArgs): simplify __call__ function body --- commitizen/cli.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 6f7556df49..a73377fe45 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -52,16 +52,13 @@ def __call__( ) -> None: if not isinstance(values, str): return - if "=" not in values: + + key, sep, value = values.partition("=") + if not key or not sep: raise InvalidCommandArgumentError( f"Option {option_string} expect a key=value format" ) kwargs = getattr(namespace, self.dest, None) or {} - key, value = values.split("=", 1) - if not key: - raise InvalidCommandArgumentError( - f"Option {option_string} expect a key=value format" - ) kwargs[key] = value.strip("'\"") setattr(namespace, self.dest, kwargs) From 647f0f50cd142dbd9ddcf462fedc7a575ba514c5 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 22:28:57 +0800 Subject: [PATCH 157/275] refactor(ExpectedExit): make the constructor more compact --- commitizen/exceptions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 8c0956be53..8be792258a 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -59,8 +59,7 @@ class ExpectedExit(CommitizenException): exit_code = ExitCode.EXPECTED_EXIT def __init__(self, *args: str, **kwargs: Any) -> None: - output_method = kwargs.get("output_method") or out.write - kwargs["output_method"] = output_method + kwargs["output_method"] = kwargs.get("output_method") or out.write super().__init__(*args, **kwargs) From d1bfd5535301efc15d2f3ddec236c245e3b9da90 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 21:42:30 +0800 Subject: [PATCH 158/275] refactor(ScmProvider): replace sorted with max --- commitizen/providers/scm_provider.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py index 3085b16efa..687775da30 100644 --- a/commitizen/providers/scm_provider.py +++ b/commitizen/providers/scm_provider.py @@ -18,10 +18,8 @@ def get_version(self) -> str: rules = TagRules.from_settings(self.config.settings) tags = get_tags(reachable_only=True) version_tags = rules.get_version_tags(tags) - versions = sorted(rules.extract_version(t) for t in version_tags) - if not versions: - return "0.0.0" - return str(versions[-1]) + version = max((rules.extract_version(t) for t in version_tags), default=None) + return str(version) if version is not None else "0.0.0" def set_version(self, version: str) -> None: # Not necessary From 0fa3db0f0c96c1c65633410dd403ab4377a4588d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 22:43:30 +0800 Subject: [PATCH 159/275] refactor(git): remove redundant if branch --- commitizen/git.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/commitizen/git.py b/commitizen/git.py index 8025041abb..4883b34e7d 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -328,6 +328,4 @@ def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]: c = cmd.run(command) if c.return_code != 0: raise GitCommandError(c.err) - if not c.out: - return [] return c.out.split(f"{delimiter}\n") From 280856adec28eb6d9d87731acdb853366bce6aad Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Jun 2025 21:00:01 +0800 Subject: [PATCH 160/275] fix(Bump): rewrite --get-next NotAllowed error message for consistency --- commitizen/commands/bump.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 2a84483c94..e07a359cf5 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -215,11 +215,13 @@ def __call__(self) -> None: raise NotAllowed("--local-version cannot be combined with --build-metadata") if get_next: - # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout - if self.changelog_flag or self.changelog_to_stdout: - raise NotAllowed( - "--changelog or --changelog-to-stdout is not allowed with --get-next" - ) + for value, option in ( + (self.changelog_flag, "--changelog"), + (self.changelog_to_stdout, "--changelog-to-stdout"), + ): + if value: + raise NotAllowed(f"{option} cannot be combined with --get-next") + # --get-next is a special case, taking precedence over config for 'update_changelog_on_bump' self.changelog_config = False # Setting dry_run to prevent any unwanted changes to the repo or files From a352e4b7dc8d562122ab2c636de9a56c0cb17a71 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Jun 2025 19:40:48 +0800 Subject: [PATCH 161/275] fix(Changelog): fix _export_template variable type Closes #1503 --- commitizen/commands/changelog.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index dc50eb3ad2..8668dd44c6 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -174,11 +174,15 @@ def _write_changelog( changelog_file.write(changelog_out) - def _export_template(self) -> None: - tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) - # TODO: fix the following type ignores - src = Path(tpl.filename) # type: ignore[arg-type] - Path(self.export_template_to).write_text(src.read_text()) # type: ignore[arg-type] + def _export_template(self, dist: str) -> None: + filename = changelog.get_changelog_template( + self.cz.template_loader, self.template + ).filename + if filename is None: + raise NotAllowed("Template filename is not set") + + text = Path(filename).read_text() + Path(dist).write_text(text) def __call__(self) -> None: commit_parser = self.cz.commit_parser @@ -195,7 +199,7 @@ def __call__(self) -> None: ) if self.export_template_to: - return self._export_template() + return self._export_template(self.export_template_to) if not changelog_pattern or not commit_parser: raise NoPatternMapError( From b35d3431978e357fcce68613869c17b99e5cffe9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Jun 2025 13:57:59 +0800 Subject: [PATCH 162/275] refactor(TagRules): extract tag_formats property and simplify list comprehension --- commitizen/tags.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/commitizen/tags.py b/commitizen/tags.py index b19bb89e09..e3f54370f1 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -5,6 +5,7 @@ from collections.abc import Iterable, Sequence from dataclasses import dataclass, field from functools import cached_property +from itertools import chain from string import Template from typing import TYPE_CHECKING, NamedTuple @@ -88,18 +89,22 @@ class TagRules: ignored_tag_formats: Sequence[str] = field(default_factory=list) merge_prereleases: bool = False + @property + def tag_formats(self) -> Iterable[str]: + return chain([self.tag_format], self.legacy_tag_formats) + @cached_property def version_regexes(self) -> list[re.Pattern]: """Regexes for all legit tag formats, current and legacy""" - tag_formats = [self.tag_format, *self.legacy_tag_formats] - regexes = (self._format_regex(p) for p in tag_formats) - return [re.compile(r) for r in regexes] + return [re.compile(self._format_regex(f)) for f in self.tag_formats] @cached_property def ignored_regexes(self) -> list[re.Pattern]: """Regexes for known but ignored tag formats""" - regexes = (self._format_regex(p, star=True) for p in self.ignored_tag_formats) - return [re.compile(r) for r in regexes] + return [ + re.compile(self._format_regex(f, star=True)) + for f in self.ignored_tag_formats + ] def _format_regex(self, tag_pattern: str, star: bool = False) -> str: """ @@ -240,10 +245,7 @@ def find_tag_for( ) -> GitTag | None: """Find the first matching tag for a given version.""" version = self.scheme(version) if isinstance(version, str) else version - possible_tags = set( - self.normalize_tag(version, f) - for f in (self.tag_format, *self.legacy_tag_formats) - ) + possible_tags = set(self.normalize_tag(version, f) for f in self.tag_formats) candidates = [t for t in tags if t.name in possible_tags] if len(candidates) > 1: warnings.warn( From 4ff20886b6be6c38c0642329bf09c9fb56764b97 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 11:46:44 +0800 Subject: [PATCH 163/275] refactor(Init): use ternary operator --- commitizen/commands/init.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 1207cd02ee..356c4ce27d 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -159,9 +159,9 @@ def __call__(self) -> None: out.success("Configuration complete 🚀") def _ask_config_path(self) -> str: - default_path = ".cz.toml" - if self.project_info.has_pyproject: - default_path = "pyproject.toml" + default_path = ( + "pyproject.toml" if self.project_info.has_pyproject else ".cz.toml" + ) name: str = questionary.select( "Please choose a supported config file: ", @@ -270,15 +270,13 @@ def _ask_version_provider(self) -> str: def _ask_version_scheme(self) -> str: """Ask for setting: version_scheme""" - default = "semver" - if self.project_info.is_python: - default = "pep440" + default_scheme = "pep440" if self.project_info.is_python else "semver" scheme: str = questionary.select( "Choose version scheme: ", - choices=list(KNOWN_SCHEMES), + choices=KNOWN_SCHEMES, style=self.cz.style, - default=default, + default=default_scheme, ).unsafe_ask() return scheme @@ -318,10 +316,9 @@ def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str: """Generate pre-commit command according to given hook types""" if not hook_types: raise ValueError("At least 1 hook type should be provided.") - cmd_str = "pre-commit install " + " ".join( + return "pre-commit install " + " ".join( f"--hook-type {ty}" for ty in hook_types ) - return cmd_str def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: pre_commit_config_filename = ".pre-commit-config.yaml" From d294d2f9a864eb38cc45682ac4d103a9e39c6545 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 00:11:54 +0800 Subject: [PATCH 164/275] refactor(bump): use a loop to shorten a series of similar NotAllowed exceptions --- commitizen/commands/bump.py | 40 ++++++++++++------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index e07a359cf5..49d90f14d2 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -177,36 +177,22 @@ def __call__(self) -> None: build_metadata = self.arguments["build_metadata"] get_next = self.arguments["get_next"] allow_no_commit = self.arguments["allow_no_commit"] + major_version_zero = self.arguments["major_version_zero"] if manual_version: - if increment: - raise NotAllowed("--increment cannot be combined with MANUAL_VERSION") - - if prerelease: - raise NotAllowed("--prerelease cannot be combined with MANUAL_VERSION") - - if devrelease is not None: - raise NotAllowed("--devrelease cannot be combined with MANUAL_VERSION") - - if is_local_version: - raise NotAllowed( - "--local-version cannot be combined with MANUAL_VERSION" - ) - - if build_metadata: - raise NotAllowed( - "--build-metadata cannot be combined with MANUAL_VERSION" - ) - - if self.bump_settings["major_version_zero"]: - raise NotAllowed( - "--major-version-zero cannot be combined with MANUAL_VERSION" - ) - - if get_next: - raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION") + for val, option in ( + (increment, "--increment"), + (prerelease, "--prerelease"), + (devrelease is not None, "--devrelease"), + (is_local_version, "--local-version"), + (build_metadata, "--build-metadata"), + (major_version_zero, "--major-version-zero"), + (get_next, "--get-next"), + ): + if val: + raise NotAllowed(f"{option} cannot be combined with MANUAL_VERSION") - if self.bump_settings["major_version_zero"] and current_version.release[0]: + if major_version_zero and current_version.release[0]: raise NotAllowed( f"--major-version-zero is meaningless for current version {current_version}" ) From 4a3afff1f503fa5f29b3b7842fea49b4d7740304 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Jun 2025 20:26:51 +0800 Subject: [PATCH 165/275] fix: raise NoVersionSpecifiedError if version is None, and adjust call sites of get_version --- commitizen/commands/bump.py | 7 +---- commitizen/commands/version.py | 32 +++++++++++---------- commitizen/providers/commitizen_provider.py | 5 +++- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 49d90f14d2..07338afb86 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -23,7 +23,6 @@ NoPatternMapError, NotAGitProjectError, NotAllowed, - NoVersionSpecifiedError, ) from commitizen.providers import get_provider from commitizen.tags import TagRules @@ -163,11 +162,7 @@ def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None: def __call__(self) -> None: """Steps executed to bump.""" provider = get_provider(self.config) - - try: - current_version = self.scheme(provider.get_version()) - except TypeError: - raise NoVersionSpecifiedError() + current_version = self.scheme(provider.get_version()) increment = self.arguments["increment"] prerelease = self.arguments["prerelease"] diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 6b0aa331ad..35e9aa6cd6 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -5,6 +5,7 @@ from commitizen import out from commitizen.__version__ import __version__ from commitizen.config import BaseConfig +from commitizen.exceptions import NoVersionSpecifiedError from commitizen.providers import get_provider @@ -28,19 +29,20 @@ def __call__(self) -> None: out.write(f"Commitizen Version: {__version__}") out.write(f"Python Version: {self.python_version}") out.write(f"Operating System: {self.operating_system}") - elif self.parameter.get("project"): - version = get_provider(self.config).get_version() - if version: - out.write(f"{version}") - else: - out.error("No project information in this project.") - elif self.parameter.get("verbose"): - out.write(f"Installed Commitizen Version: {__version__}") - version = get_provider(self.config).get_version() - if version: - out.write(f"Project Version: {version}") - else: + return + + if (verbose := self.parameter.get("verbose")) or self.parameter.get("project"): + if verbose: + out.write(f"Installed Commitizen Version: {__version__}") + + try: + version = get_provider(self.config).get_version() + except NoVersionSpecifiedError: out.error("No project information in this project.") - else: - # if no argument is given, show installed commitizen version - out.write(f"{__version__}") + return + + out.write(f"Project Version: {version}" if verbose else version) + return + + # if no argument is given, show installed commitizen version + out.write(f"{__version__}") diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py index d9207b19ff..f90d7d9b6e 100644 --- a/commitizen/providers/commitizen_provider.py +++ b/commitizen/providers/commitizen_provider.py @@ -1,5 +1,6 @@ from __future__ import annotations +from commitizen.exceptions import NoVersionSpecifiedError from commitizen.providers.base_provider import VersionProvider @@ -9,7 +10,9 @@ class CommitizenProvider(VersionProvider): """ def get_version(self) -> str: - return self.config.settings["version"] # type: ignore[return-value] # TODO: check if we can fix this by tweaking the `Settings` type + if ret := self.config.settings["version"]: + return ret + raise NoVersionSpecifiedError() def set_version(self, version: str) -> None: self.config.set_key("version", version) From 908f29c8f99b8955160c08447fe40bb4adab2dbd Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 25 May 2025 17:46:33 +0800 Subject: [PATCH 166/275] docs(taplo): add toml formatter --- .taplo.toml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .taplo.toml diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000000..36a3453ac9 --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,4 @@ +include = ["pyproject.toml", ".taplo.toml"] + +[formatting] +indent_string = " " From f15b7186d2fd4df55794d86da92d77c92c2020d2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 25 May 2025 20:25:49 +0800 Subject: [PATCH 167/275] docs(pre-commit): add taplo to pre-commit hook --- .pre-commit-config.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3dfd50ee68..46f6d04e34 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - id: debug-statements - id: no-commit-to-branch - id: check-merge-conflict - - id: check-toml + - id: check-toml # TOML linter (syntax checker) - id: check-yaml args: [ '--unsafe' ] # for mkdocs.yml - id: detect-private-key @@ -55,17 +55,22 @@ repos: stages: - post-commit + - repo: https://github.com/ComPWA/taplo-pre-commit + rev: v0.9.3 + hooks: + - id: taplo-format + - repo: local hooks: - id: format - name: Format + name: Format Python code via Poetry language: system pass_filenames: false entry: poetry format types: [ python ] - id: linter and test - name: Linters + name: Linters via Poetry language: system pass_filenames: false entry: poetry lint From 38397b26d24731c398859143335e228e04828782 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Jun 2025 20:09:07 +0800 Subject: [PATCH 168/275] refactor(Changelog): remove unnecessary intermediate variables for better readability --- commitizen/commands/changelog.py | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 8668dd44c6..0fffd8ed7c 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -11,7 +11,6 @@ from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format from commitizen.config import BaseConfig -from commitizen.cz.base import ChangelogReleaseHook, MessageBuilderHook from commitizen.cz.utils import strip_local_version from commitizen.exceptions import ( DryRunExit, @@ -188,15 +187,6 @@ def __call__(self) -> None: commit_parser = self.cz.commit_parser changelog_pattern = self.cz.changelog_pattern start_rev = self.start_rev - unreleased_version = self.unreleased_version - changelog_meta = changelog.Metadata() - change_type_map: dict[str, str] | None = self.change_type_map - changelog_message_builder_hook: MessageBuilderHook | None = ( - self.cz.changelog_message_builder_hook - ) - changelog_release_hook: ChangelogReleaseHook | None = ( - self.cz.changelog_release_hook - ) if self.export_template_to: return self._export_template(self.export_template_to) @@ -213,33 +203,37 @@ def __call__(self) -> None: assert self.file_name tags = self.tag_rules.get_version_tags(git.get_tags(), warn=True) - end_rev = "" + changelog_meta = changelog.Metadata() if self.incremental: changelog_meta = self.changelog_format.get_metadata(self.file_name) if changelog_meta.latest_version: start_rev = self._find_incremental_rev( strip_local_version(changelog_meta.latest_version_tag or ""), tags ) + + end_rev = "" if self.rev_range: start_rev, end_rev = changelog.get_oldest_and_newest_rev( tags, self.rev_range, self.tag_rules, ) + commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order") if not commits and ( self.current_version is None or not self.current_version.is_prerelease ): raise NoCommitsFoundError("No commits found") + tree = changelog.generate_tree_from_commits( commits, tags, commit_parser, changelog_pattern, - unreleased_version, - change_type_map=change_type_map, - changelog_message_builder_hook=changelog_message_builder_hook, - changelog_release_hook=changelog_release_hook, + self.unreleased_version, + change_type_map=self.change_type_map, + changelog_message_builder_hook=self.cz.changelog_message_builder_hook, + changelog_release_hook=self.cz.changelog_release_hook, rules=self.tag_rules, ) if self.change_type_order: @@ -247,11 +241,15 @@ def __call__(self) -> None: tree, self.change_type_order ) - extras = self.cz.template_extras.copy() - extras.update(self.config.settings["extras"]) - extras.update(self.extras) changelog_out = changelog.render_changelog( - tree, loader=self.cz.template_loader, template=self.template, **extras + tree, + self.cz.template_loader, + self.template, + **{ + **self.cz.template_extras, + **self.config.settings["extras"], + **self.extras, + }, ).lstrip("\n") # Dry_run is executed here to avoid checking and reading the files From 34a483658440b47c94d916528750feccf0dd2b63 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 17:52:46 +0800 Subject: [PATCH 169/275] refactor(changelog): shorten condition expression and early return --- commitizen/changelog.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index ba6fbbc6b3..8721734af7 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -251,6 +251,7 @@ def incremental_build( unreleased_start = metadata.unreleased_start unreleased_end = metadata.unreleased_end latest_version_position = metadata.latest_version_position + skip = False output_lines: list[str] = [] for index, line in enumerate(lines): @@ -260,9 +261,7 @@ def incremental_build( skip = False if ( latest_version_position is None - or isinstance(latest_version_position, int) - and isinstance(unreleased_end, int) - and latest_version_position > unreleased_end + or latest_version_position > unreleased_end ): continue @@ -271,13 +270,15 @@ def incremental_build( if index == latest_version_position: output_lines.extend([new_content, "\n"]) - output_lines.append(line) - if not isinstance(latest_version_position, int): - if output_lines and output_lines[-1].strip(): - # Ensure at least one blank line between existing and new content. - output_lines.append("\n") - output_lines.append(new_content) + + if latest_version_position is not None: + return output_lines + + if output_lines and output_lines[-1].strip(): + # Ensure at least one blank line between existing and new content. + output_lines.append("\n") + output_lines.append(new_content) return output_lines From 14f7db52223f6efee4a96c628ae1f6ecc22bd168 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 11 Jun 2025 22:40:59 +0800 Subject: [PATCH 170/275] refactor(Init): extract _get_config_data for readability --- commitizen/commands/init.py | 45 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 356c4ce27d..c298ffec8b 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -79,6 +79,8 @@ def is_pre_commit_installed(self) -> bool: class Init: + _PRE_COMMIT_CONFIG_PATH = ".pre-commit-config.yaml" + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.encoding = config.settings["encoding"] @@ -320,9 +322,8 @@ def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str: f"--hook-type {ty}" for ty in hook_types ) - def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: - pre_commit_config_filename = ".pre-commit-config.yaml" - cz_hook_config = { + def _get_config_data(self) -> dict[str, Any]: + CZ_HOOK_CONFIG = { "repo": "https://github.com/commitizen-tools/commitizen", "rev": f"v{__version__}", "hooks": [ @@ -331,31 +332,29 @@ def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: ], } - config_data = {} if not self.project_info.has_pre_commit_config: # .pre-commit-config.yaml does not exist - config_data["repos"] = [cz_hook_config] + return {"repos": [CZ_HOOK_CONFIG]} + + with open(self._PRE_COMMIT_CONFIG_PATH, encoding=self.encoding) as config_file: + config_data: dict[str, Any] = yaml.safe_load(config_file) or {} + + if not isinstance(repos := config_data.get("repos"), list): + # .pre-commit-config.yaml exists but there's no "repos" key + config_data["repos"] = [CZ_HOOK_CONFIG] + return config_data + + # Check if commitizen pre-commit hook is already in the config + if any("commitizen" in hook_config["repo"] for hook_config in repos): + out.write("commitizen already in pre-commit config") else: - with open( - pre_commit_config_filename, encoding=self.encoding - ) as config_file: - yaml_data = yaml.safe_load(config_file) - if yaml_data: - config_data = yaml_data - - if "repos" in config_data: - for pre_commit_hook in config_data["repos"]: - if "commitizen" in pre_commit_hook["repo"]: - out.write("commitizen already in pre-commit config") - break - else: - config_data["repos"].append(cz_hook_config) - else: - # .pre-commit-config.yaml exists but there's no "repos" key - config_data["repos"] = [cz_hook_config] + repos.append(CZ_HOOK_CONFIG) + return config_data + def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: + config_data = self._get_config_data() with smart_open( - pre_commit_config_filename, "w", encoding=self.encoding + self._PRE_COMMIT_CONFIG_PATH, "w", encoding=self.encoding ) as config_file: yaml.safe_dump(config_data, stream=config_file) From eca23bac01fca9a8b0b9221d004bc34370401f5c Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 20:32:51 +0800 Subject: [PATCH 171/275] refactor(process_commit_message): better type and early return --- commitizen/changelog.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 8721734af7..93b5339962 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -29,7 +29,7 @@ import re from collections import OrderedDict, defaultdict -from collections.abc import Generator, Iterable, Mapping, Sequence +from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence from dataclasses import dataclass from datetime import date from typing import TYPE_CHECKING, Any @@ -167,8 +167,8 @@ def process_commit_message( hook: MessageBuilderHook | None, parsed: re.Match[str], commit: GitCommit, - changes: dict[str | None, list], - change_type_map: dict[str, str] | None = None, + ref_changes: MutableMapping[str | None, list], + change_type_map: Mapping[str, str] | None = None, ) -> None: message: dict[str, Any] = { "sha1": commit.rev, @@ -178,13 +178,16 @@ def process_commit_message( **parsed.groupdict(), } - if processed := hook(message, commit) if hook else message: - messages = [processed] if isinstance(processed, dict) else processed - for msg in messages: - change_type = msg.pop("change_type", None) - if change_type_map: - change_type = change_type_map.get(change_type, change_type) - changes[change_type].append(msg) + processed_msg = hook(message, commit) if hook else message + if not processed_msg: + return + + messages = [processed_msg] if isinstance(processed_msg, dict) else processed_msg + for msg in messages: + change_type = msg.pop("change_type", None) + if change_type_map: + change_type = change_type_map.get(change_type, change_type) + ref_changes[change_type].append(msg) def generate_ordered_changelog_tree( From f92357a3b54f27ebdf5f2f1f0219b88f02154743 Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:13:11 +0800 Subject: [PATCH 172/275] refactor(init): remote extra words --- commitizen/commands/init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index c298ffec8b..8acc3ae5cb 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -238,7 +238,7 @@ def _ask_version_provider(self) -> str: "npm": "npm: Get and set version from package.json:project.version field", "pep621": "pep621: Get and set version from pyproject.toml:project.version field", "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field", - "uv": "uv: Get and Get and set version from pyproject.toml and uv.lock", + "uv": "uv: Get and set version from pyproject.toml and uv.lock", "scm": "scm: Fetch the version from git and does not need to set it back", } From 80f2812378f23d8a3dc57a9bff3815b65969ca15 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 12:25:24 +0800 Subject: [PATCH 173/275] refactor(Init): fix unbounded variable in _ask_tag_format --- commitizen/commands/init.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 8acc3ae5cb..eed7ff2d69 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -210,23 +210,20 @@ def _ask_tag(self) -> str: return latest_tag def _ask_tag_format(self, latest_tag: str) -> str: - is_correct_format = False if latest_tag.startswith("v"): - tag_format = r"v$version" - is_correct_format = questionary.confirm( - f'Is "{tag_format}" the correct tag format?', style=self.cz.style - ).unsafe_ask() + v_tag_format = r"v$version" + if questionary.confirm( + f'Is "{v_tag_format}" the correct tag format?', style=self.cz.style + ).unsafe_ask(): + return v_tag_format default_format = DEFAULT_SETTINGS["tag_format"] - if not is_correct_format: - tag_format = questionary.text( - f'Please enter the correct version format: (default: "{default_format}")', - style=self.cz.style, - ).unsafe_ask() + tag_format: str = questionary.text( + f'Please enter the correct version format: (default: "{default_format}")', + style=self.cz.style, + ).unsafe_ask() - if not tag_format: - tag_format = default_format - return tag_format + return tag_format or default_format def _ask_version_provider(self) -> str: """Ask for setting: version_provider""" From 07e78bcac775d67637bf87a6b4d345e62938c973 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 13:22:44 +0800 Subject: [PATCH 174/275] test(Init): improve coverage for _ask_tag_format --- tests/commands/test_init_command.py | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 3f12d0bd7f..2d39ae085e 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -255,6 +255,38 @@ def test_pre_commit_exec_failed( commands.Init(config)() +class TestAskTagFormat: + def test_confirm_v_tag_format(self, mocker: MockFixture, config): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + + result = init._ask_tag_format("v1.0.0") + assert result == r"v$version" + + def test_reject_v_tag_format(self, mocker: MockFixture, config): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) + + result = init._ask_tag_format("v1.0.0") + assert result == "custom-$version" + + def test_non_v_tag_format(self, mocker: MockFixture, config): + init = commands.Init(config) + mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) + + result = init._ask_tag_format("1.0.0") + assert result == "custom-$version" + + def test_empty_input_returns_default(self, mocker: MockFixture, config): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("")) + + result = init._ask_tag_format("v1.0.0") + assert result == "$version" # This is the default format from DEFAULT_SETTINGS + + @skip_below_py_3_10 def test_init_command_shows_description_when_use_help_option( mocker: MockFixture, capsys, file_regression From 263125330d3a02821220d9f49292d37b1a7585d8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 22:17:06 +0800 Subject: [PATCH 175/275] fix(ExitCode): add from_str in ExitCode and replace parse_no_raise with it Warn if a given decimal string is not in range --- commitizen/cli.py | 25 ++++++++++++------------- commitizen/exceptions.py | 12 ++++++++++-- tests/test_exceptions.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 tests/test_exceptions.py diff --git a/commitizen/cli.py b/commitizen/cli.py index a73377fe45..3862b8843e 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -580,20 +580,19 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]: Receives digits and strings and outputs the parsed integer which represents the exit code found in exceptions. """ - no_raise_items: list[str] = comma_separated_no_raise.split(",") - no_raise_codes: list[int] = [] - for item in no_raise_items: - if item.isdecimal(): - no_raise_codes.append(int(item)) - continue + + def exit_code_from_str_or_skip(s: str) -> ExitCode | None: try: - exit_code = ExitCode[item.strip()] - except KeyError: - out.warn(f"WARN: no_raise key `{item}` does not exist. Skipping.") - continue - else: - no_raise_codes.append(exit_code.value) - return no_raise_codes + return ExitCode.from_str(s) + except (KeyError, ValueError): + out.warn(f"WARN: no_raise value `{s}` is not a valid exit code. Skipping.") + return None + + return [ + code.value + for s in comma_separated_no_raise.split(",") + if (code := exit_code_from_str_or_skip(s)) is not None + ] if TYPE_CHECKING: diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 8be792258a..75b0ab2fb7 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -1,10 +1,12 @@ -import enum +from __future__ import annotations + +from enum import IntEnum from typing import Any from commitizen import out -class ExitCode(enum.IntEnum): +class ExitCode(IntEnum): EXPECTED_EXIT = 0 NO_COMMITIZEN_FOUND = 1 NOT_A_GIT_PROJECT = 2 @@ -39,6 +41,12 @@ class ExitCode(enum.IntEnum): CONFIG_FILE_IS_EMPTY = 31 COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED = 32 + @classmethod + def from_str(cls, value: str) -> ExitCode: + if value.isdecimal(): + return cls(int(value)) + return cls[value.strip()] + class CommitizenException(Exception): def __init__(self, *args: str, **kwargs: Any) -> None: diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000000..1a66c79d02 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,38 @@ +import pytest + +from commitizen.exceptions import ExitCode + + +def test_from_str_with_decimal(): + """Test from_str with decimal values.""" + assert ExitCode.from_str("0") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("1") == ExitCode.NO_COMMITIZEN_FOUND + assert ExitCode.from_str("32") == ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED + + +def test_from_str_with_enum_name(): + """Test from_str with enum names.""" + assert ExitCode.from_str("EXPECTED_EXIT") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("NO_COMMITIZEN_FOUND") == ExitCode.NO_COMMITIZEN_FOUND + assert ( + ExitCode.from_str("COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED") + == ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED + ) + + +def test_from_str_with_whitespace(): + """Test from_str with whitespace in enum names.""" + assert ExitCode.from_str(" EXPECTED_EXIT ") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("\tNO_COMMITIZEN_FOUND\t") == ExitCode.NO_COMMITIZEN_FOUND + + +def test_from_str_with_invalid_values(): + """Test from_str with invalid values.""" + with pytest.raises(KeyError): + ExitCode.from_str("invalid_name") + with pytest.raises(ValueError): + ExitCode.from_str("999") # Out of range decimal + with pytest.raises(KeyError): + ExitCode.from_str("") + with pytest.raises(KeyError): + ExitCode.from_str(" ") From 2cd0f88bd7faf8d137d708bee96a8f3ad8ec687b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 12:05:16 +0800 Subject: [PATCH 176/275] fix(Init): fix a typo in _ask_version_provider options and remove unnecessary filter, use named tuple for options --- commitizen/commands/init.py | 105 ++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index eed7ff2d69..33be58544a 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -2,7 +2,7 @@ import os import shutil -from typing import Any +from typing import Any, NamedTuple import questionary import yaml @@ -17,6 +17,54 @@ from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme +class _VersionProviderOption(NamedTuple): + provider_name: str + description: str + + @property + def title(self) -> str: + return f"{self.provider_name}: {self.description}" + + +_VERSION_PROVIDER_CHOICES = tuple( + questionary.Choice(title=option.title, value=option.provider_name) + for option in ( + _VersionProviderOption( + provider_name="commitizen", + description="Fetch and set version in commitizen config (default)", + ), + _VersionProviderOption( + provider_name="cargo", + description="Get and set version from Cargo.toml:project.version field", + ), + _VersionProviderOption( + provider_name="composer", + description="Get and set version from composer.json:project.version field", + ), + _VersionProviderOption( + provider_name="npm", + description="Get and set version from package.json:project.version field", + ), + _VersionProviderOption( + provider_name="pep621", + description="Get and set version from pyproject.toml:project.version field", + ), + _VersionProviderOption( + provider_name="poetry", + description="Get and set version from pyproject.toml:tool.poetry.version field", + ), + _VersionProviderOption( + provider_name="uv", + description="Get and set version from pyproject.toml and uv.lock", + ), + _VersionProviderOption( + provider_name="scm", + description="Fetch the version from git and does not need to set it back", + ), + ) +) + + class ProjectInfo: """Discover information about the current folder.""" @@ -228,45 +276,32 @@ def _ask_tag_format(self, latest_tag: str) -> str: def _ask_version_provider(self) -> str: """Ask for setting: version_provider""" - OPTS = { - "commitizen": "commitizen: Fetch and set version in commitizen config (default)", - "cargo": "cargo: Get and set version from Cargo.toml:project.version field", - "composer": "composer: Get and set version from composer.json:project.version field", - "npm": "npm: Get and set version from package.json:project.version field", - "pep621": "pep621: Get and set version from pyproject.toml:project.version field", - "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field", - "uv": "uv: Get and set version from pyproject.toml and uv.lock", - "scm": "scm: Fetch the version from git and does not need to set it back", - } - - default_val = "commitizen" - if self.project_info.is_python: - if self.project_info.is_python_poetry: - default_val = "poetry" - elif self.project_info.is_python_uv: - default_val = "uv" - else: - default_val = "pep621" - elif self.project_info.is_rust_cargo: - default_val = "cargo" - elif self.project_info.is_npm_package: - default_val = "npm" - elif self.project_info.is_php_composer: - default_val = "composer" - - choices = [ - questionary.Choice(title=title, value=value) - for value, title in OPTS.items() - ] - default = next(filter(lambda x: x.value == default_val, choices)) version_provider: str = questionary.select( "Choose the source of the version:", - choices=choices, + choices=_VERSION_PROVIDER_CHOICES, style=self.cz.style, - default=default, + default=self._default_version_provider, ).unsafe_ask() return version_provider + @property + def _default_version_provider(self) -> str: + if self.project_info.is_python: + if self.project_info.is_python_poetry: + return "poetry" + if self.project_info.is_python_uv: + return "uv" + return "pep621" + + if self.project_info.is_rust_cargo: + return "cargo" + if self.project_info.is_npm_package: + return "npm" + if self.project_info.is_php_composer: + return "composer" + + return "commitizen" + def _ask_version_scheme(self) -> str: """Ask for setting: version_scheme""" default_scheme = "pep440" if self.project_info.is_python else "semver" @@ -291,7 +326,7 @@ def _ask_major_version_zero(self, version: Version) -> bool: return major_version_zero def _ask_update_changelog_on_bump(self) -> bool: - "Ask for setting: update_changelog_on_bump" + """Ask for setting: update_changelog_on_bump""" update_changelog_on_bump: bool = questionary.confirm( "Create changelog automatically on bump", default=True, From 1b39ae41571b8c2b710725e22e89ffed33dc324d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 00:51:31 +0800 Subject: [PATCH 177/275] fix(init): make welcome message easier to read --- commitizen/commands/init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 33be58544a..0f19786d0b 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -142,8 +142,8 @@ def __call__(self) -> None: out.info("Welcome to commitizen!\n") out.line( - "Answer the questions to configure your project.\n" - "For further configuration visit:\n" + "Answer the following questions to configure your project.\n" + "For further configuration, visit:\n" "\n" "https://commitizen-tools.github.io/commitizen/config/" "\n" From 78b1fb12ecad52f8b4c3639d070229b443ac5de0 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 11:37:15 +0800 Subject: [PATCH 178/275] refactor(Init): remove unnecessary methods from ProjectInfo and refactor _ask_tag --- commitizen/commands/init.py | 49 +++++++++++++++---------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 0f19786d0b..4b5649b670 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -111,16 +111,6 @@ def is_npm_package(self) -> bool: def is_php_composer(self) -> bool: return os.path.isfile("composer.json") - @property - def latest_tag(self) -> str | None: - return get_latest_tag_name() - - def tags(self) -> list | None: - """Not a property, only use if necessary""" - if self.latest_tag is None: - return None - return get_tag_names() - @property def is_pre_commit_installed(self) -> bool: return bool(shutil.which("pre-commit")) @@ -231,31 +221,32 @@ def _ask_name(self) -> str: return name def _ask_tag(self) -> str: - latest_tag = self.project_info.latest_tag + latest_tag = get_latest_tag_name() if not latest_tag: out.error("No Existing Tag. Set tag to v0.0.1") return "0.0.1" - is_correct_tag = questionary.confirm( + if questionary.confirm( f"Is {latest_tag} the latest tag?", style=self.cz.style, default=False + ).unsafe_ask(): + return latest_tag + + existing_tags = get_tag_names() + if not existing_tags: + out.error("No Existing Tag. Set tag to v0.0.1") + return "0.0.1" + + answer: str = questionary.select( + "Please choose the latest tag: ", + # The latest tag is most likely with the largest number. + # Thus, listing the existing_tags in reverse order makes more sense. + choices=sorted(existing_tags, reverse=True), + style=self.cz.style, ).unsafe_ask() - if not is_correct_tag: - tags = self.project_info.tags() - if not tags: - out.error("No Existing Tag. Set tag to v0.0.1") - return "0.0.1" - - # the latest tag is most likely with the largest number. Thus list the tags in reverse order makes more sense - sorted_tags = sorted(tags, reverse=True) - latest_tag = questionary.select( - "Please choose the latest tag: ", - choices=sorted_tags, - style=self.cz.style, - ).unsafe_ask() - - if not latest_tag: - raise NoAnswersError("Tag is required!") - return latest_tag + + if not answer: + raise NoAnswersError("Tag is required!") + return answer def _ask_tag_format(self, latest_tag: str) -> str: if latest_tag.startswith("v"): From 4ca11909927def4aaa60d1cbab3abd93280a2855 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 10 Jun 2025 11:51:03 +0800 Subject: [PATCH 179/275] test(Init): cover _ask_tag test --- tests/commands/test_init_command.py | 122 ++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 2d39ae085e..7feaf18ef2 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -298,3 +298,125 @@ def test_init_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +def test_init_with_confirmed_tag_format(config, mocker: MockFixture, tmpdir): + mocker.patch( + "commitizen.commands.init.get_tag_names", return_value=["v0.0.2", "v0.0.1"] + ) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v0.0.2") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'tag_format = "v$version"' in toml_file.read() + + +def test_init_with_no_existing_tags(config, mocker: MockFixture, tmpdir): + mocker.patch("commitizen.commands.init.get_tag_names", return_value=[]) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "0.0.1"' in toml_file.read() + + +def test_init_with_no_existing_latest_tag(config, mocker: MockFixture, tmpdir): + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value=None) + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "0.0.1"' in toml_file.read() + + +def test_init_with_existing_tags(config, mocker: MockFixture, tmpdir): + expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] + mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), # Select version scheme first + FakeQuestion("v1.0.0"), # Then select the latest tag + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "1.0.0"' in toml_file.read() + + +def test_init_with_valid_tag_selection(config, mocker: MockFixture, tmpdir): + expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] + mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + + # Mock all questionary.select calls in the exact order they appear in Init.__call__ + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), # _ask_config_path + FakeQuestion("cz_conventional_commits"), # _ask_name + FakeQuestion("commitizen"), # _ask_version_provider + FakeQuestion("v0.9.0"), # _ask_tag (after confirm=False) + FakeQuestion("semver"), # _ask_version_scheme + ], + ) + + mocker.patch( + "questionary.confirm", return_value=FakeQuestion(False) + ) # Don't confirm latest tag + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + content = toml_file.read() + assert 'version = "0.9.0"' in content + assert 'version_scheme = "semver"' in content From 4cbf48518daf11e9d6cb55bef0ae783605c96f70 Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:49:58 +0800 Subject: [PATCH 180/275] fix(init): use pre-push as pre-commit stage Original "push" stage has been deprecated since pre-commit v3.2.0. See https://github.com/pre-commit/pre-commit/issues/2732 and https://github.com/pre-commit/pre-commit/pull/2808 for detailed information. --- .pre-commit-hooks.yaml | 2 +- commitizen/commands/init.py | 2 +- poetry.lock | 784 ++++++++++++++++++------------------ pyproject.toml | 2 +- 4 files changed, 401 insertions(+), 389 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2a3a088484..c142634581 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -24,4 +24,4 @@ pass_filenames: false language: python language_version: python3 - minimum_pre_commit_version: "1.4.3" + minimum_pre_commit_version: "3.2.0" diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 4b5649b670..a6efe0acc0 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -351,7 +351,7 @@ def _get_config_data(self) -> dict[str, Any]: "rev": f"v{__version__}", "hooks": [ {"id": "commitizen"}, - {"id": "commitizen-branch", "stages": ["push"]}, + {"id": "commitizen-branch", "stages": ["pre-push"]}, ], } diff --git a/poetry.lock b/poetry.lock index f8fb9aa803..829789632f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -48,18 +48,19 @@ dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)" [[package]] name = "backrefs" -version = "5.8" +version = "5.9" description = "A wrapper around re and regex that adds additional back references." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, - {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, - {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"}, - {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"}, - {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"}, - {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"}, + {file = "backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f"}, + {file = "backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf"}, + {file = "backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa"}, + {file = "backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b"}, + {file = "backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9"}, + {file = "backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60"}, + {file = "backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59"}, ] [package.extras] @@ -67,26 +68,26 @@ extras = ["regex"] [[package]] name = "cachetools" -version = "6.0.0" +version = "6.2.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "cachetools-6.0.0-py3-none-any.whl", hash = "sha256:82e73ba88f7b30228b5507dce1a1f878498fc669d972aef2dde4f3a3c24f103e"}, - {file = "cachetools-6.0.0.tar.gz", hash = "sha256:f225782b84438f828328fc2ad74346522f27e5b1440f4e9fd18b20ebfd1aa2cf"}, + {file = "cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6"}, + {file = "cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32"}, ] [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["documentation"] files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, + {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, ] [[package]] @@ -115,104 +116,91 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, ] [[package]] @@ -244,79 +232,100 @@ files = [ [[package]] name = "coverage" -version = "7.8.2" +version = "7.10.5" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a"}, - {file = "coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6"}, - {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3"}, - {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404"}, - {file = "coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7"}, - {file = "coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347"}, - {file = "coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9"}, - {file = "coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5"}, - {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb"}, - {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54"}, - {file = "coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a"}, - {file = "coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975"}, - {file = "coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53"}, - {file = "coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c"}, - {file = "coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99"}, - {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57"}, - {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f"}, - {file = "coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8"}, - {file = "coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223"}, - {file = "coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f"}, - {file = "coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca"}, - {file = "coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257"}, - {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050"}, - {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48"}, - {file = "coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7"}, - {file = "coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3"}, - {file = "coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7"}, - {file = "coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008"}, - {file = "coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be"}, - {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b"}, - {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199"}, - {file = "coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8"}, - {file = "coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d"}, - {file = "coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b"}, - {file = "coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a"}, - {file = "coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d"}, - {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3"}, - {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7"}, - {file = "coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a"}, - {file = "coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e"}, - {file = "coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837"}, - {file = "coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32"}, - {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"}, + {file = "coverage-7.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c6a5c3414bfc7451b879141ce772c546985163cf553f08e0f135f0699a911801"}, + {file = "coverage-7.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bc8e4d99ce82f1710cc3c125adc30fd1487d3cf6c2cd4994d78d68a47b16989a"}, + {file = "coverage-7.10.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:02252dc1216e512a9311f596b3169fad54abcb13827a8d76d5630c798a50a754"}, + {file = "coverage-7.10.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73269df37883e02d460bee0cc16be90509faea1e3bd105d77360b512d5bb9c33"}, + {file = "coverage-7.10.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f8a81b0614642f91c9effd53eec284f965577591f51f547a1cbeb32035b4c2f"}, + {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6a29f8e0adb7f8c2b95fa2d4566a1d6e6722e0a637634c6563cb1ab844427dd9"}, + {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fcf6ab569436b4a647d4e91accba12509ad9f2554bc93d3aee23cc596e7f99c3"}, + {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:90dc3d6fb222b194a5de60af8d190bedeeddcbc7add317e4a3cd333ee6b7c879"}, + {file = "coverage-7.10.5-cp310-cp310-win32.whl", hash = "sha256:414a568cd545f9dc75f0686a0049393de8098414b58ea071e03395505b73d7a8"}, + {file = "coverage-7.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:e551f9d03347196271935fd3c0c165f0e8c049220280c1120de0084d65e9c7ff"}, + {file = "coverage-7.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c177e6ffe2ebc7c410785307758ee21258aa8e8092b44d09a2da767834f075f2"}, + {file = "coverage-7.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:14d6071c51ad0f703d6440827eaa46386169b5fdced42631d5a5ac419616046f"}, + {file = "coverage-7.10.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:61f78c7c3bc272a410c5ae3fde7792b4ffb4acc03d35a7df73ca8978826bb7ab"}, + {file = "coverage-7.10.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f39071caa126f69d63f99b324fb08c7b1da2ec28cbb1fe7b5b1799926492f65c"}, + {file = "coverage-7.10.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343a023193f04d46edc46b2616cdbee68c94dd10208ecd3adc56fcc54ef2baa1"}, + {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:585ffe93ae5894d1ebdee69fc0b0d4b7c75d8007983692fb300ac98eed146f78"}, + {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0ef4e66f006ed181df29b59921bd8fc7ed7cd6a9289295cd8b2824b49b570df"}, + {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb7b0bbf7cc1d0453b843eca7b5fa017874735bef9bfdfa4121373d2cc885ed6"}, + {file = "coverage-7.10.5-cp311-cp311-win32.whl", hash = "sha256:1d043a8a06987cc0c98516e57c4d3fc2c1591364831e9deb59c9e1b4937e8caf"}, + {file = "coverage-7.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:fefafcca09c3ac56372ef64a40f5fe17c5592fab906e0fdffd09543f3012ba50"}, + {file = "coverage-7.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:7e78b767da8b5fc5b2faa69bb001edafcd6f3995b42a331c53ef9572c55ceb82"}, + {file = "coverage-7.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2d05c7e73c60a4cecc7d9b60dbfd603b4ebc0adafaef371445b47d0f805c8a9"}, + {file = "coverage-7.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32ddaa3b2c509778ed5373b177eb2bf5662405493baeff52278a0b4f9415188b"}, + {file = "coverage-7.10.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd382410039fe062097aa0292ab6335a3f1e7af7bba2ef8d27dcda484918f20c"}, + {file = "coverage-7.10.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7fa22800f3908df31cea6fb230f20ac49e343515d968cc3a42b30d5c3ebf9b5a"}, + {file = "coverage-7.10.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f366a57ac81f5e12797136552f5b7502fa053c861a009b91b80ed51f2ce651c6"}, + {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1dc8f1980a272ad4a6c84cba7981792344dad33bf5869361576b7aef42733a"}, + {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2285c04ee8676f7938b02b4936d9b9b672064daab3187c20f73a55f3d70e6b4a"}, + {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2492e4dd9daab63f5f56286f8a04c51323d237631eb98505d87e4c4ff19ec34"}, + {file = "coverage-7.10.5-cp312-cp312-win32.whl", hash = "sha256:38a9109c4ee8135d5df5505384fc2f20287a47ccbe0b3f04c53c9a1989c2bbaf"}, + {file = "coverage-7.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:6b87f1ad60b30bc3c43c66afa7db6b22a3109902e28c5094957626a0143a001f"}, + {file = "coverage-7.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:672a6c1da5aea6c629819a0e1461e89d244f78d7b60c424ecf4f1f2556c041d8"}, + {file = "coverage-7.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef3b83594d933020f54cf65ea1f4405d1f4e41a009c46df629dd964fcb6e907c"}, + {file = "coverage-7.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b96bfdf7c0ea9faebce088a3ecb2382819da4fbc05c7b80040dbc428df6af44"}, + {file = "coverage-7.10.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:63df1fdaffa42d914d5c4d293e838937638bf75c794cf20bee12978fc8c4e3bc"}, + {file = "coverage-7.10.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8002dc6a049aac0e81ecec97abfb08c01ef0c1fbf962d0c98da3950ace89b869"}, + {file = "coverage-7.10.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63d4bb2966d6f5f705a6b0c6784c8969c468dbc4bcf9d9ded8bff1c7e092451f"}, + {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1f672efc0731a6846b157389b6e6d5d5e9e59d1d1a23a5c66a99fd58339914d5"}, + {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3f39cef43d08049e8afc1fde4a5da8510fc6be843f8dea350ee46e2a26b2f54c"}, + {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2968647e3ed5a6c019a419264386b013979ff1fb67dd11f5c9886c43d6a31fc2"}, + {file = "coverage-7.10.5-cp313-cp313-win32.whl", hash = "sha256:0d511dda38595b2b6934c2b730a1fd57a3635c6aa2a04cb74714cdfdd53846f4"}, + {file = "coverage-7.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:9a86281794a393513cf117177fd39c796b3f8e3759bb2764259a2abba5cce54b"}, + {file = "coverage-7.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:cebd8e906eb98bb09c10d1feed16096700b1198d482267f8bf0474e63a7b8d84"}, + {file = "coverage-7.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0520dff502da5e09d0d20781df74d8189ab334a1e40d5bafe2efaa4158e2d9e7"}, + {file = "coverage-7.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d9cd64aca68f503ed3f1f18c7c9174cbb797baba02ca8ab5112f9d1c0328cd4b"}, + {file = "coverage-7.10.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0913dd1613a33b13c4f84aa6e3f4198c1a21ee28ccb4f674985c1f22109f0aae"}, + {file = "coverage-7.10.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b7181c0feeb06ed8a02da02792f42f829a7b29990fef52eff257fef0885d760"}, + {file = "coverage-7.10.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d42b7396b605f774d4372dd9c49bed71cbabce4ae1ccd074d155709dd8f235"}, + {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4fdc777e05c4940b297bf47bf7eedd56a39a61dc23ba798e4b830d585486ca5"}, + {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:42144e8e346de44a6f1dbd0a56575dd8ab8dfa7e9007da02ea5b1c30ab33a7db"}, + {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:66c644cbd7aed8fe266d5917e2c9f65458a51cfe5eeff9c05f15b335f697066e"}, + {file = "coverage-7.10.5-cp313-cp313t-win32.whl", hash = "sha256:2d1b73023854068c44b0c554578a4e1ef1b050ed07cf8b431549e624a29a66ee"}, + {file = "coverage-7.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:54a1532c8a642d8cc0bd5a9a51f5a9dcc440294fd06e9dda55e743c5ec1a8f14"}, + {file = "coverage-7.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:74d5b63fe3f5f5d372253a4ef92492c11a4305f3550631beaa432fc9df16fcff"}, + {file = "coverage-7.10.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:68c5e0bc5f44f68053369fa0d94459c84548a77660a5f2561c5e5f1e3bed7031"}, + {file = "coverage-7.10.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cf33134ffae93865e32e1e37df043bef15a5e857d8caebc0099d225c579b0fa3"}, + {file = "coverage-7.10.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad8fa9d5193bafcf668231294241302b5e683a0518bf1e33a9a0dfb142ec3031"}, + {file = "coverage-7.10.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:146fa1531973d38ab4b689bc764592fe6c2f913e7e80a39e7eeafd11f0ef6db2"}, + {file = "coverage-7.10.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6013a37b8a4854c478d3219ee8bc2392dea51602dd0803a12d6f6182a0061762"}, + {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:eb90fe20db9c3d930fa2ad7a308207ab5b86bf6a76f54ab6a40be4012d88fcae"}, + {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:384b34482272e960c438703cafe63316dfbea124ac62006a455c8410bf2a2262"}, + {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:467dc74bd0a1a7de2bedf8deaf6811f43602cb532bd34d81ffd6038d6d8abe99"}, + {file = "coverage-7.10.5-cp314-cp314-win32.whl", hash = "sha256:556d23d4e6393ca898b2e63a5bca91e9ac2d5fb13299ec286cd69a09a7187fde"}, + {file = "coverage-7.10.5-cp314-cp314-win_amd64.whl", hash = "sha256:f4446a9547681533c8fa3e3c6cf62121eeee616e6a92bd9201c6edd91beffe13"}, + {file = "coverage-7.10.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e78bd9cf65da4c303bf663de0d73bf69f81e878bf72a94e9af67137c69b9fe9"}, + {file = "coverage-7.10.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5661bf987d91ec756a47c7e5df4fbcb949f39e32f9334ccd3f43233bbb65e508"}, + {file = "coverage-7.10.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a46473129244db42a720439a26984f8c6f834762fc4573616c1f37f13994b357"}, + {file = "coverage-7.10.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1f64b8d3415d60f24b058b58d859e9512624bdfa57a2d1f8aff93c1ec45c429b"}, + {file = "coverage-7.10.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:44d43de99a9d90b20e0163f9770542357f58860a26e24dc1d924643bd6aa7cb4"}, + {file = "coverage-7.10.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a931a87e5ddb6b6404e65443b742cb1c14959622777f2a4efd81fba84f5d91ba"}, + {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9559b906a100029274448f4c8b8b0a127daa4dade5661dfd821b8c188058842"}, + {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b08801e25e3b4526ef9ced1aa29344131a8f5213c60c03c18fe4c6170ffa2874"}, + {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed9749bb8eda35f8b636fb7632f1c62f735a236a5d4edadd8bbcc5ea0542e732"}, + {file = "coverage-7.10.5-cp314-cp314t-win32.whl", hash = "sha256:609b60d123fc2cc63ccee6d17e4676699075db72d14ac3c107cc4976d516f2df"}, + {file = "coverage-7.10.5-cp314-cp314t-win_amd64.whl", hash = "sha256:0666cf3d2c1626b5a3463fd5b05f5e21f99e6aec40a3192eee4d07a15970b07f"}, + {file = "coverage-7.10.5-cp314-cp314t-win_arm64.whl", hash = "sha256:bc85eb2d35e760120540afddd3044a5bf69118a91a296a8b3940dfc4fdcfe1e2"}, + {file = "coverage-7.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:62835c1b00c4a4ace24c1a88561a5a59b612fbb83a525d1c70ff5720c97c0610"}, + {file = "coverage-7.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5255b3bbcc1d32a4069d6403820ac8e6dbcc1d68cb28a60a1ebf17e47028e898"}, + {file = "coverage-7.10.5-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3876385722e335d6e991c430302c24251ef9c2a9701b2b390f5473199b1b8ebf"}, + {file = "coverage-7.10.5-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8048ce4b149c93447a55d279078c8ae98b08a6951a3c4d2d7e87f4efc7bfe100"}, + {file = "coverage-7.10.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4028e7558e268dd8bcf4d9484aad393cafa654c24b4885f6f9474bf53183a82a"}, + {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03f47dc870eec0367fcdd603ca6a01517d2504e83dc18dbfafae37faec66129a"}, + {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2d488d7d42b6ded7ea0704884f89dcabd2619505457de8fc9a6011c62106f6e5"}, + {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3dcf2ead47fa8be14224ee817dfc1df98043af568fe120a22f81c0eb3c34ad2"}, + {file = "coverage-7.10.5-cp39-cp39-win32.whl", hash = "sha256:02650a11324b80057b8c9c29487020073d5e98a498f1857f37e3f9b6ea1b2426"}, + {file = "coverage-7.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:b45264dd450a10f9e03237b41a9a24e85cbb1e278e5a32adb1a303f58f0017f3"}, + {file = "coverage-7.10.5-py3-none-any.whl", hash = "sha256:0be24d35e4db1d23d0db5c0f6a74a962e2ec83c426b5cac09f4234aadef38e4a"}, + {file = "coverage-7.10.5.tar.gz", hash = "sha256:f2e57716a78bc3ae80b2207be0709a3b2b63b9f2dcf9740ee6ac03588a2015b6"}, ] [package.dependencies] @@ -369,14 +378,14 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version [[package]] name = "distlib" -version = "0.3.9" +version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" groups = ["dev", "linters"] files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, ] [[package]] @@ -430,31 +439,26 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.18.0" +version = "3.19.1" description = "A platform independent file lock." optional = false python-versions = ">=3.9" groups = ["dev", "linters"] files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, + {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, + {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, ] -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] - [[package]] name = "freezegun" -version = "1.5.2" +version = "1.5.5" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "freezegun-1.5.2-py3-none-any.whl", hash = "sha256:5aaf3ba229cda57afab5bd311f0108d86b6fb119ae89d2cd9c43ec8c1733c85b"}, - {file = "freezegun-1.5.2.tar.gz", hash = "sha256:a54ae1d2f9c02dbf42e02c18a3ab95ab4295818b549a34dac55592d72a905181"}, + {file = "freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2"}, + {file = "freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a"}, ] [package.dependencies] @@ -480,14 +484,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.12" +version = "2.6.13" description = "File identification library for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, - {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, + {file = "identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b"}, + {file = "identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32"}, ] [package.extras] @@ -623,14 +627,14 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" -version = "3.8" +version = "3.8.2" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"}, - {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"}, + {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, + {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, ] [package.dependencies] @@ -827,19 +831,20 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.6.14" +version = "9.6.18" description = "Documentation that simply works" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b"}, - {file = "mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754"}, + {file = "mkdocs_material-9.6.18-py3-none-any.whl", hash = "sha256:dbc1e146a0ecce951a4d84f97b816a54936cdc9e1edd1667fc6868878ac06701"}, + {file = "mkdocs_material-9.6.18.tar.gz", hash = "sha256:a2eb253bcc8b66f8c6eaf8379c10ed6e9644090c2e2e9d0971c7722dc7211c05"}, ] [package.dependencies] babel = ">=2.10,<3.0" backrefs = ">=5.7.post1,<6.0" +click = "<8.2.2" colorama = ">=0.4,<1.0" jinja2 = ">=3.1,<4.0" markdown = ">=3.2,<4.0" @@ -869,44 +874,50 @@ files = [ [[package]] name = "mypy" -version = "1.16.0" +version = "1.17.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"}, - {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"}, - {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"}, - {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"}, - {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"}, - {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"}, - {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"}, - {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"}, - {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"}, - {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"}, - {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"}, - {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"}, - {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"}, - {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"}, - {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"}, - {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"}, - {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"}, - {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"}, - {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"}, - {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"}, - {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"}, - {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"}, - {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"}, - {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"}, - {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"}, - {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"}, - {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"}, - {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"}, - {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"}, - {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"}, - {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"}, - {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"}, + {file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"}, + {file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"}, + {file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"}, + {file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"}, + {file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"}, + {file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"}, + {file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"}, + {file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"}, + {file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"}, + {file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"}, + {file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"}, + {file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"}, + {file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"}, + {file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"}, + {file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"}, + {file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"}, + {file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"}, + {file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"}, + {file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"}, + {file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"}, + {file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"}, + {file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"}, + {file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"}, + {file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"}, + {file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"}, + {file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"}, + {file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"}, + {file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"}, + {file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"}, + {file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"}, + {file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"}, + {file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"}, + {file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"}, + {file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"}, + {file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"}, + {file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"}, + {file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"}, + {file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"}, ] [package.dependencies] @@ -976,14 +987,14 @@ lint = ["black"] [[package]] name = "parso" -version = "0.8.4" +version = "0.8.5" description = "A Python Parser" optional = false python-versions = ">=3.6" groups = ["dev"] files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, + {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"}, + {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"}, ] [package.extras] @@ -1032,14 +1043,14 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.8" +version = "4.4.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" groups = ["dev", "documentation", "linters"] files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, + {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, + {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, ] [package.extras] @@ -1085,14 +1096,14 @@ poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] [[package]] name = "pre-commit" -version = "4.2.0" +version = "4.3.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, - {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, + {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"}, + {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"}, ] [package.dependencies] @@ -1147,14 +1158,14 @@ tests = ["pytest"] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["dev", "documentation", "script", "test"] files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -1162,14 +1173,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.15" +version = "10.16.1" description = "Extension pack for Python Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f"}, - {file = "pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7"}, + {file = "pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d"}, + {file = "pymdown_extensions-10.16.1.tar.gz", hash = "sha256:aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91"}, ] [package.dependencies] @@ -1201,14 +1212,14 @@ testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "p [[package]] name = "pytest" -version = "8.4.0" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"}, - {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] @@ -1225,33 +1236,34 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, - {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, + {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, + {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, ] [package.dependencies] coverage = {version = ">=7.5", extras = ["toml"]} -pytest = ">=4.6" +pluggy = ">=1.2" +pytest = ">=6.2.5" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-datadir" -version = "1.7.2" +version = "1.8.0" description = "pytest plugin for test data directories and files" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "pytest_datadir-1.7.2-py3-none-any.whl", hash = "sha256:8392ba0e9eaf37030e663dcd91cc5123dec99c44300f0c5eac44f35f13f0e086"}, - {file = "pytest_datadir-1.7.2.tar.gz", hash = "sha256:15f5228a35d0a3205e4968e75d3b9cca91762424e1eafc21eb637d380a48443e"}, + {file = "pytest_datadir-1.8.0-py3-none-any.whl", hash = "sha256:5c677bc097d907ac71ca418109adc3abe34cf0bddfe6cf78aecfbabd96a15cf0"}, + {file = "pytest_datadir-1.8.0.tar.gz", hash = "sha256:7a15faed76cebe87cc91941dd1920a9a38eba56a09c11e9ddf1434d28a0f78eb"}, ] [package.dependencies] @@ -1297,14 +1309,14 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-regressions" -version = "2.8.0" +version = "2.8.2" description = "Easy to use fixtures to write regression tests." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_regressions-2.8.0-py3-none-any.whl", hash = "sha256:2926f37efa5fd6793806b10352e274c5284a5469a845aeab6243e86f9214766f"}, - {file = "pytest_regressions-2.8.0.tar.gz", hash = "sha256:775044e17117f5427df2caad3ab1c66889abe770a0ce2bc3f24fdeac99af76fe"}, + {file = "pytest_regressions-2.8.2-py3-none-any.whl", hash = "sha256:a0804c1ce66d8e4d9a3c7c68f42a3d436182edca8e86565c232caeaf9e080fc2"}, + {file = "pytest_regressions-2.8.2.tar.gz", hash = "sha256:1d8f4767be58b9994bfa7d60271099469ad32b8ca9f9d9ceca1c1d6827156b19"}, ] [package.dependencies] @@ -1320,14 +1332,14 @@ num = ["numpy", "pandas"] [[package]] name = "pytest-xdist" -version = "3.7.0" +version = "3.8.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0"}, - {file = "pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [package.dependencies] @@ -1449,19 +1461,19 @@ prompt_toolkit = ">=2.0,<4.0" [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1621,31 +1633,28 @@ files = [ [[package]] name = "tox" -version = "4.26.0" +version = "4.28.4" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tox-4.26.0-py3-none-any.whl", hash = "sha256:75f17aaf09face9b97bd41645028d9f722301e912be8b4c65a3f938024560224"}, - {file = "tox-4.26.0.tar.gz", hash = "sha256:a83b3b67b0159fa58e44e646505079e35a43317a62d2ae94725e0586266faeca"}, + {file = "tox-4.28.4-py3-none-any.whl", hash = "sha256:8d4ad9ee916ebbb59272bb045e154a10fa12e3bbdcf94cc5185cbdaf9b241f99"}, + {file = "tox-4.28.4.tar.gz", hash = "sha256:b5b14c6307bd8994ff1eba5074275826620325ee1a4f61316959d562bfd70b9d"}, ] [package.dependencies] -cachetools = ">=5.5.1" +cachetools = ">=6.1" chardet = ">=5.2" colorama = ">=0.4.6" -filelock = ">=3.16.1" -packaging = ">=24.2" -platformdirs = ">=4.3.6" -pluggy = ">=1.5" -pyproject-api = ">=1.8" +filelock = ">=3.18" +packaging = ">=25" +platformdirs = ">=4.3.8" +pluggy = ">=1.6" +pyproject-api = ">=1.9.1" tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} -virtualenv = ">=20.31" - -[package.extras] -test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"] +typing-extensions = {version = ">=4.14.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.31.2" [[package]] name = "traitlets" @@ -1665,14 +1674,14 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "types-colorama" -version = "0.4.15.20240311" +version = "0.4.15.20250801" description = "Typing stubs for colorama" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a"}, - {file = "types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e"}, + {file = "types_colorama-0.4.15.20250801-py3-none-any.whl", hash = "sha256:b6e89bd3b250fdad13a8b6a465c933f4a5afe485ea2e2f104d739be50b13eea9"}, + {file = "types_colorama-0.4.15.20250801.tar.gz", hash = "sha256:02565d13d68963d12237d3f330f5ecd622a3179f7b5b14ee7f16146270c357f5"}, ] [[package]] @@ -1689,26 +1698,26 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20250516" +version = "2.9.0.20250822" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93"}, - {file = "types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5"}, + {file = "types_python_dateutil-2.9.0.20250822-py3-none-any.whl", hash = "sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc"}, + {file = "types_python_dateutil-2.9.0.20250822.tar.gz", hash = "sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20250516" +version = "6.0.12.20250822" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"}, - {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"}, + {file = "types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098"}, + {file = "types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413"}, ] [[package]] @@ -1725,27 +1734,27 @@ files = [ [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev", "linters", "script", "test"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -1756,20 +1765,21 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.31.2" +version = "20.34.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev", "linters"] files = [ - {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, - {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, + {file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"}, + {file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" +typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] @@ -1832,104 +1842,106 @@ files = [ [[package]] name = "wrapt" -version = "1.17.2" +version = "1.17.3" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, - {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, - {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"}, - {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"}, - {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, - {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, - {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, - {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, - {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, - {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, - {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, - {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, - {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"}, - {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"}, - {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"}, - {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"}, - {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"}, - {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, - {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, + {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, + {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, + {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, + {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, + {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, + {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, + {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, + {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, + {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, + {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, + {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, + {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, + {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, + {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, + {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, + {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, + {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, + {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, + {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, + {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, + {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, + {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, + {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, ] [[package]] name = "zipp" -version = "3.22.0" +version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] markers = "python_version == \"3.9\"" files = [ - {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"}, - {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"}, + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] @@ -1937,10 +1949,10 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \" cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib_resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "0c750c9d3befb187e16df9b2524cedd8319da36c1311ac74d083b9e8ff54ff1d" +content-hash = "79d126ebbb9e2d0976e3b2853980826e4b32b62ca9d982507bf21aff9bffacd0" diff --git a/pyproject.toml b/pyproject.toml index ebb2244770..6ff5219328 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,7 +120,7 @@ deprecated = "^1.2.13" [tool.poetry.group.linters.dependencies] ruff = "^0.11.5" -pre-commit = ">=2.18,<5.0" +pre-commit = ">=3.2.0,<5.0" mypy = "^1.16.0" types-deprecated = "^1.2.9.2" types-python-dateutil = "^2.8.19.13" From 8097cb3d102ec79641cc6e4a95628fdc54dab42a Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:25:30 +0800 Subject: [PATCH 181/275] test(init): check "pre-" is showing in outputs --- tests/commands/test_init_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 7feaf18ef2..26f67dfae4 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -32,7 +32,7 @@ def unsafe_ask(self): "rev": f"v{__version__}", "hooks": [ {"id": "commitizen"}, - {"id": "commitizen-branch", "stages": ["push"]}, + {"id": "commitizen-branch", "stages": ["pre-push"]}, ], } From 2df5098aa334f3323257fc709921f8d3385d9268 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 11 Jun 2025 15:41:05 +0800 Subject: [PATCH 182/275] refactor(Init): remove the variable values_to_add and the update_config function for readability --- commitizen/commands/init.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index a6efe0acc0..2ce3981f44 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -160,21 +160,6 @@ def __call__(self) -> None: self.config = JsonConfig(data="{}", path=config_path) elif "yaml" in config_path: self.config = YAMLConfig(data="", path=config_path) - values_to_add: dict[str, Any] = {} - values_to_add["name"] = cz_name - values_to_add["tag_format"] = tag_format - values_to_add["version_scheme"] = version_scheme - - if version_provider == "commitizen": - values_to_add["version"] = version.public - else: - values_to_add["version_provider"] = version_provider - - if update_changelog_on_bump: - values_to_add["update_changelog_on_bump"] = update_changelog_on_bump - - if major_version_zero: - values_to_add["major_version_zero"] = major_version_zero # Collect hook data hook_types = questionary.checkbox( @@ -192,7 +177,18 @@ def __call__(self) -> None: # Create and initialize config self.config.init_empty_config_content() - self._update_config_file(values_to_add) + + self.config.set_key("name", cz_name) + self.config.set_key("tag_format", tag_format) + self.config.set_key("version_scheme", version_scheme) + if version_provider == "commitizen": + self.config.set_key("version", version.public) + else: + self.config.set_key("version_provider", version_provider) + if update_changelog_on_bump: + self.config.set_key("update_changelog_on_bump", update_changelog_on_bump) + if major_version_zero: + self.config.set_key("major_version_zero", major_version_zero) out.write("\nYou can bump the version running:\n") out.info("\tcz bump\n") @@ -387,7 +383,3 @@ def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: hook_types = ["commit-msg", "pre-push"] self._exec_install_pre_commit_hook(hook_types) out.write("commitizen pre-commit hook is now installed in your '.git'\n") - - def _update_config_file(self, values: dict[str, Any]) -> None: - for key, value in values.items(): - self.config.set_key(key, value) From 3e94cec3d5c6f396b626c5b73536f0f2da010e89 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 11 Jun 2025 15:55:35 +0800 Subject: [PATCH 183/275] test(Init): improve test coverage on config initialization --- tests/commands/test_init_command.py | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 26f67dfae4..c156fb0234 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -420,3 +420,66 @@ def test_init_with_valid_tag_selection(config, mocker: MockFixture, tmpdir): content = toml_file.read() assert 'version = "0.9.0"' in content assert 'version_scheme = "semver"' in content + + +def test_init_configuration_settings(tmpdir, mocker: MockFixture, config): + """Test that all configuration settings are properly initialized.""" + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + + with open("pyproject.toml", encoding="utf-8") as toml_file: + config_data = toml_file.read() + + # Verify all expected settings are present + assert 'name = "cz_conventional_commits"' in config_data + assert 'tag_format = "$version"' in config_data + assert 'version_scheme = "semver"' in config_data + assert 'version = "0.0.1"' in config_data + assert "update_changelog_on_bump = true" in config_data + assert "major_version_zero = true" in config_data + + +def test_init_configuration_with_version_provider(tmpdir, mocker: MockFixture, config): + """Test configuration initialization with a different version provider.""" + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("pep621"), # Different version provider + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + + with open("pyproject.toml", encoding="utf-8") as toml_file: + config_data = toml_file.read() + + # Verify version provider is set instead of version + assert 'name = "cz_conventional_commits"' in config_data + assert 'tag_format = "$version"' in config_data + assert 'version_scheme = "semver"' in config_data + assert 'version_provider = "pep621"' in config_data + assert "update_changelog_on_bump = true" in config_data + assert "major_version_zero = true" in config_data + assert ( + "version = " not in config_data + ) # Version should not be set when using version_provider From 73f09ee4de0aee418be5d59c53a2b6e59831c248 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 16:59:11 +0800 Subject: [PATCH 184/275] refactor(changelog): shorten generate_tree_from_commits --- commitizen/changelog.py | 57 +++++++++++++++++------------------------ commitizen/git.py | 3 +++ 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 93b5339962..1a1938c7c3 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -32,6 +32,7 @@ from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence from dataclasses import dataclass from datetime import date +from itertools import chain from typing import TYPE_CHECKING, Any from jinja2 import ( @@ -88,33 +89,32 @@ def generate_tree_from_commits( pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL) - current_tag: GitTag | None = None rules = rules or TagRules() # Check if the latest commit is not tagged - if commits: - latest_commit = commits[0] - current_tag = get_commit_tag(latest_commit, tags) - - current_tag_name: str = unreleased_version or "Unreleased" - current_tag_date: str = "" - if unreleased_version is not None: - current_tag_date = date.today().isoformat() - if current_tag is not None and current_tag.name: - current_tag_name = current_tag.name - current_tag_date = current_tag.date + current_tag = get_commit_tag(commits[0], tags) if commits else None + current_tag_name = unreleased_version or "Unreleased" + current_tag_date = ( + date.today().isoformat() if unreleased_version is not None else "" + ) + + used_tags: set[GitTag] = set() + if current_tag: + used_tags.add(current_tag) + if current_tag.name: + current_tag_name = current_tag.name + current_tag_date = current_tag.date + + commit_tag: GitTag | None = None changes: dict = defaultdict(list) - used_tags: list = [current_tag] for commit in commits: - commit_tag = get_commit_tag(commit, tags) - if ( - commit_tag + (commit_tag := get_commit_tag(commit, tags)) and commit_tag not in used_tags and rules.include_in_changelog(commit_tag) ): - used_tags.append(commit_tag) + used_tags.add(commit_tag) release = { "version": current_tag_name, "date": current_tag_date, @@ -127,24 +127,15 @@ def generate_tree_from_commits( current_tag_date = commit_tag.date changes = defaultdict(list) - matches = pat.match(commit.message) - if not matches: + if not pat.match(commit.message): continue - # Process subject from commit message - if message := map_pat.match(commit.message): - process_commit_message( - changelog_message_builder_hook, - message, - commit, - changes, - change_type_map, - ) - - # Process body from commit message - body_parts = commit.body.split("\n\n") - for body_part in body_parts: - if message := body_map_pat.match(body_part): + # Process subject and body from commit message + for message in chain( + [map_pat.match(commit.message)], + (body_map_pat.match(block) for block in commit.body.split("\n\n")), + ): + if message: process_commit_message( changelog_message_builder_hook, message, diff --git a/commitizen/git.py b/commitizen/git.py index 4883b34e7d..c2bab176ea 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -49,6 +49,9 @@ class GitObject: def __eq__(self, other: object) -> bool: return hasattr(other, "rev") and self.rev == other.rev + def __hash__(self) -> int: + return hash(self.rev) + class GitCommit(GitObject): def __init__( From 9d392a4714aa24750241ea3a39d4a5df0326f70b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 3 Jun 2025 20:13:03 +0800 Subject: [PATCH 185/275] feat(check): add check against default branch --- commitizen/cli.py | 7 ++++ commitizen/commands/check.py | 34 ++++++++++++------- commitizen/git.py | 7 ++++ docs/commands/check.md | 24 ++++++++----- ...shows_description_when_use_help_option.txt | 5 ++- tests/test_git.py | 19 +++++++++++ 6 files changed, 74 insertions(+), 22 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 3862b8843e..ed4305ea1f 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -471,6 +471,13 @@ def __call__( "help": "a range of git rev to check. e.g, master..HEAD", "exclusive_group": "group1", }, + { + "name": ["-d", "--use-default-range"], + "action": "store_true", + "default": False, + "help": "check from the default branch to HEAD. e.g, refs/remotes/origin/master..HEAD", + "exclusive_group": "group1", + }, { "name": ["-m", "--message"], "help": "commit message that needs to be checked", diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 69147bcfbe..e6ebc928ed 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -21,6 +21,7 @@ class CheckArgs(TypedDict, total=False): message_length_limit: int allowed_prefixes: list[str] message: str + use_default_range: bool class Check: @@ -40,6 +41,7 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N self.allow_abort = bool( arguments.get("allow_abort", config.settings["allow_abort"]) ) + self.use_default_range = bool(arguments.get("use_default_range")) self.max_msg_length = arguments.get("message_length_limit", 0) # we need to distinguish between None and [], which is a valid value @@ -50,25 +52,28 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N else config.settings["allowed_prefixes"] ) - self._valid_command_argument() - - self.config: BaseConfig = config - self.encoding = config.settings["encoding"] - self.cz = factory.committer_factory(self.config) - - def _valid_command_argument(self) -> None: num_exclusive_args_provided = sum( arg is not None - for arg in (self.commit_msg_file, self.commit_msg, self.rev_range) + for arg in ( + self.commit_msg_file, + self.commit_msg, + self.rev_range, + ) ) - if num_exclusive_args_provided == 0 and not sys.stdin.isatty(): - self.commit_msg = sys.stdin.read() - elif num_exclusive_args_provided != 1: + + if num_exclusive_args_provided > 1: raise InvalidCommandArgumentError( "Only one of --rev-range, --message, and --commit-msg-file is permitted by check command! " "See 'cz check -h' for more information" ) + if num_exclusive_args_provided == 0 and not sys.stdin.isatty(): + self.commit_msg = sys.stdin.read() + + self.config: BaseConfig = config + self.encoding = config.settings["encoding"] + self.cz = factory.committer_factory(self.config) + def __call__(self) -> None: """Validate if commit messages follows the conventional pattern. @@ -109,7 +114,10 @@ def _get_commits(self) -> list[git.GitCommit]: return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))] # Get commit messages from git log (--rev-range) - return git.get_commits(end=self.rev_range) + return git.get_commits( + git.get_default_branch() if self.use_default_range else None, + self.rev_range, + ) @staticmethod def _filter_comments(msg: str) -> str: @@ -134,7 +142,7 @@ def _filter_comments(msg: str) -> str: The filtered commit message without comments. """ - lines = [] + lines: list[str] = [] for line in msg.split("\n"): if "# ------------------------ >8 ------------------------" in line: break diff --git a/commitizen/git.py b/commitizen/git.py index c2bab176ea..c124cd9371 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -332,3 +332,10 @@ def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]: if c.return_code != 0: raise GitCommandError(c.err) return c.out.split(f"{delimiter}\n") + + +def get_default_branch() -> str: + c = cmd.run("git symbolic-ref refs/remotes/origin/HEAD") + if c.return_code != 0: + raise GitCommandError(c.err) + return c.out.strip() diff --git a/docs/commands/check.md b/docs/commands/check.md index 33e41e04f8..320bffb881 100644 --- a/docs/commands/check.md +++ b/docs/commands/check.md @@ -27,19 +27,21 @@ $ cz check --rev-range REV_RANGE For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range ..HEAD`. +You can also use `--use-default-range` to check all commits from the default branch up to HEAD. This is equivalent to using `--rev-range ..HEAD`. + For more information on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). ### Commit Message There are two ways you can provide your plain message and check it. -#### Method 1: use -m or --message +#### Method 1: use `-m` or `--message` ```bash $ cz check --message MESSAGE ``` -In this option, MESSAGE is the commit message to be checked. +In this option, `MESSAGE` is the commit message to be checked. #### Method 2: use pipe to pipe it to `cz check` @@ -47,7 +49,7 @@ In this option, MESSAGE is the commit message to be checked. $ echo MESSAGE | cz check ``` -In this option, MESSAGE is piped to cz check and will be checked. +In this option, `MESSAGE` is piped to `cz check` and will be checked. ### Commit Message File @@ -55,7 +57,7 @@ In this option, MESSAGE is piped to cz check and will be checked. $ cz check --commit-msg-file COMMIT_MSG_FILE ``` -In this option, COMMIT_MSG_FILE is the path of the temporary file that contains the commit message. +In this option, `COMMIT_MSG_FILE` is the path of the temporary file that contains the commit message. This argument can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hooks. ### Allow Abort @@ -65,12 +67,18 @@ cz check --message MESSAGE --allow-abort ``` Empty commit messages typically instruct Git to abort a commit, so you can pass `--allow-abort` to -permit them. Since `git commit` accepts an `--allow-empty-message` flag (primarily for wrapper scripts), you may wish to disallow such commits in CI. `--allow-abort` may be used in conjunction with any of the other options. +permit them. Since `git commit` accepts the `--allow-empty-message` flag (primarily for wrapper scripts), you may wish to disallow such commits in CI. `--allow-abort` may be used in conjunction with any of the other options. ### Allowed Prefixes If the commit message starts with some specific prefixes, `cz check` returns `True` without checking the regex. -By default, the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`. +By default, the following prefixes are allowed: + +- `Merge` +- `Revert` +- `Pull request` +- `fixup!` +- `squash!` ```bash cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' @@ -83,5 +91,5 @@ For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESS By default, the limit is set to 0, which means no limit on the length. **Note that the limit applies only to the first line of the message.** -Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, -while the body and the footer are not counted. + +Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, while the body and the footer are not counted. diff --git a/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt index 85f42f6d2a..4066748557 100644 --- a/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt @@ -1,5 +1,5 @@ usage: cz check [-h] [--commit-msg-file COMMIT_MSG_FILE | - --rev-range REV_RANGE | -m MESSAGE] [--allow-abort] + --rev-range REV_RANGE | -d | -m MESSAGE] [--allow-abort] [--allowed-prefixes [ALLOWED_PREFIXES ...]] [-l MESSAGE_LENGTH_LIMIT] @@ -13,6 +13,9 @@ options: MSG_FILE=$1 --rev-range REV_RANGE a range of git rev to check. e.g, master..HEAD + -d, --use-default-range + check from the default branch to HEAD. e.g, + refs/remotes/origin/master..HEAD -m, --message MESSAGE commit message that needs to be checked --allow-abort allow empty commit messages, which typically abort a diff --git a/tests/test_git.py b/tests/test_git.py index e242b3a2ae..2a31d9c0b8 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -480,3 +480,22 @@ def test_create_commit_cmd_string(mocker, os_name, committer_date, expected_cmd) mocker.patch("os.name", os_name) result = git._create_commit_cmd_string("", committer_date, "temp.txt") assert result == expected_cmd + + +def test_get_default_branch_success(mocker: MockFixture): + mocker.patch( + "commitizen.cmd.run", return_value=FakeCommand(out="refs/remotes/origin/main\n") + ) + assert git.get_default_branch() == "refs/remotes/origin/main" + + +def test_get_default_branch_error(mocker: MockFixture): + mocker.patch( + "commitizen.cmd.run", + return_value=FakeCommand( + err="fatal: ref refs/remotes/origin/HEAD is not a symbolic ref", + return_code=1, + ), + ) + with pytest.raises(exceptions.GitCommandError): + git.get_default_branch() From 028ab2e33796fd94c1b2c075e9212b8836c72141 Mon Sep 17 00:00:00 2001 From: ongdisheng Date: Sun, 6 Jul 2025 22:11:09 +0800 Subject: [PATCH 186/275] test(changelog): ensure error on missing changelog template filename --- tests/commands/test_changelog_command.py | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index 0eb29cdb04..1f3dabd761 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -1914,6 +1914,32 @@ def test_export_changelog_template_from_plugin( assert target.read_text() == tpl +def test_export_changelog_template_fails_when_template_has_no_filename( + mocker: MockFixture, + tmp_commitizen_project: Path, +): + project_root = Path(tmp_commitizen_project) + target = project_root / "changelog.jinja" + + # Mock a template object with no filename + class FakeTemplate: + filename = None + + # Patch get_changelog_template to return a template without a filename + mocker.patch( + "commitizen.changelog.get_changelog_template", return_value=FakeTemplate() + ) + + args = ["cz", "changelog", "--export-template", str(target)] + mocker.patch.object(sys, "argv", args) + + with pytest.raises(NotAllowed) as exc_info: + cli.main() + + assert not target.exists() + assert "Template filename is not set" in str(exc_info.value) + + @skip_below_py_3_13 def test_changelog_command_shows_description_when_use_help_option( mocker: MockFixture, capsys, file_regression From 1fb11e00345bb6d475df07e32ccc233392e012af Mon Sep 17 00:00:00 2001 From: timsu92 <33785401+timsu92@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:49:58 +0800 Subject: [PATCH 187/275] fix(init): use pre-push as pre-commit stage Original "push" stage has been deprecated since pre-commit v3.2.0. See https://github.com/pre-commit/pre-commit/issues/2732 and https://github.com/pre-commit/pre-commit/pull/2808 for detailed information. --- poetry.lock | 192 ++++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/poetry.lock b/poetry.lock index 829789632f..42af6b9c99 100644 --- a/poetry.lock +++ b/poetry.lock @@ -232,100 +232,100 @@ files = [ [[package]] name = "coverage" -version = "7.10.5" +version = "7.10.6" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "coverage-7.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c6a5c3414bfc7451b879141ce772c546985163cf553f08e0f135f0699a911801"}, - {file = "coverage-7.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bc8e4d99ce82f1710cc3c125adc30fd1487d3cf6c2cd4994d78d68a47b16989a"}, - {file = "coverage-7.10.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:02252dc1216e512a9311f596b3169fad54abcb13827a8d76d5630c798a50a754"}, - {file = "coverage-7.10.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73269df37883e02d460bee0cc16be90509faea1e3bd105d77360b512d5bb9c33"}, - {file = "coverage-7.10.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f8a81b0614642f91c9effd53eec284f965577591f51f547a1cbeb32035b4c2f"}, - {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6a29f8e0adb7f8c2b95fa2d4566a1d6e6722e0a637634c6563cb1ab844427dd9"}, - {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fcf6ab569436b4a647d4e91accba12509ad9f2554bc93d3aee23cc596e7f99c3"}, - {file = "coverage-7.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:90dc3d6fb222b194a5de60af8d190bedeeddcbc7add317e4a3cd333ee6b7c879"}, - {file = "coverage-7.10.5-cp310-cp310-win32.whl", hash = "sha256:414a568cd545f9dc75f0686a0049393de8098414b58ea071e03395505b73d7a8"}, - {file = "coverage-7.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:e551f9d03347196271935fd3c0c165f0e8c049220280c1120de0084d65e9c7ff"}, - {file = "coverage-7.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c177e6ffe2ebc7c410785307758ee21258aa8e8092b44d09a2da767834f075f2"}, - {file = "coverage-7.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:14d6071c51ad0f703d6440827eaa46386169b5fdced42631d5a5ac419616046f"}, - {file = "coverage-7.10.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:61f78c7c3bc272a410c5ae3fde7792b4ffb4acc03d35a7df73ca8978826bb7ab"}, - {file = "coverage-7.10.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f39071caa126f69d63f99b324fb08c7b1da2ec28cbb1fe7b5b1799926492f65c"}, - {file = "coverage-7.10.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343a023193f04d46edc46b2616cdbee68c94dd10208ecd3adc56fcc54ef2baa1"}, - {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:585ffe93ae5894d1ebdee69fc0b0d4b7c75d8007983692fb300ac98eed146f78"}, - {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0ef4e66f006ed181df29b59921bd8fc7ed7cd6a9289295cd8b2824b49b570df"}, - {file = "coverage-7.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb7b0bbf7cc1d0453b843eca7b5fa017874735bef9bfdfa4121373d2cc885ed6"}, - {file = "coverage-7.10.5-cp311-cp311-win32.whl", hash = "sha256:1d043a8a06987cc0c98516e57c4d3fc2c1591364831e9deb59c9e1b4937e8caf"}, - {file = "coverage-7.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:fefafcca09c3ac56372ef64a40f5fe17c5592fab906e0fdffd09543f3012ba50"}, - {file = "coverage-7.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:7e78b767da8b5fc5b2faa69bb001edafcd6f3995b42a331c53ef9572c55ceb82"}, - {file = "coverage-7.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2d05c7e73c60a4cecc7d9b60dbfd603b4ebc0adafaef371445b47d0f805c8a9"}, - {file = "coverage-7.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32ddaa3b2c509778ed5373b177eb2bf5662405493baeff52278a0b4f9415188b"}, - {file = "coverage-7.10.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd382410039fe062097aa0292ab6335a3f1e7af7bba2ef8d27dcda484918f20c"}, - {file = "coverage-7.10.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7fa22800f3908df31cea6fb230f20ac49e343515d968cc3a42b30d5c3ebf9b5a"}, - {file = "coverage-7.10.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f366a57ac81f5e12797136552f5b7502fa053c861a009b91b80ed51f2ce651c6"}, - {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1dc8f1980a272ad4a6c84cba7981792344dad33bf5869361576b7aef42733a"}, - {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2285c04ee8676f7938b02b4936d9b9b672064daab3187c20f73a55f3d70e6b4a"}, - {file = "coverage-7.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2492e4dd9daab63f5f56286f8a04c51323d237631eb98505d87e4c4ff19ec34"}, - {file = "coverage-7.10.5-cp312-cp312-win32.whl", hash = "sha256:38a9109c4ee8135d5df5505384fc2f20287a47ccbe0b3f04c53c9a1989c2bbaf"}, - {file = "coverage-7.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:6b87f1ad60b30bc3c43c66afa7db6b22a3109902e28c5094957626a0143a001f"}, - {file = "coverage-7.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:672a6c1da5aea6c629819a0e1461e89d244f78d7b60c424ecf4f1f2556c041d8"}, - {file = "coverage-7.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef3b83594d933020f54cf65ea1f4405d1f4e41a009c46df629dd964fcb6e907c"}, - {file = "coverage-7.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b96bfdf7c0ea9faebce088a3ecb2382819da4fbc05c7b80040dbc428df6af44"}, - {file = "coverage-7.10.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:63df1fdaffa42d914d5c4d293e838937638bf75c794cf20bee12978fc8c4e3bc"}, - {file = "coverage-7.10.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8002dc6a049aac0e81ecec97abfb08c01ef0c1fbf962d0c98da3950ace89b869"}, - {file = "coverage-7.10.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63d4bb2966d6f5f705a6b0c6784c8969c468dbc4bcf9d9ded8bff1c7e092451f"}, - {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1f672efc0731a6846b157389b6e6d5d5e9e59d1d1a23a5c66a99fd58339914d5"}, - {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3f39cef43d08049e8afc1fde4a5da8510fc6be843f8dea350ee46e2a26b2f54c"}, - {file = "coverage-7.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2968647e3ed5a6c019a419264386b013979ff1fb67dd11f5c9886c43d6a31fc2"}, - {file = "coverage-7.10.5-cp313-cp313-win32.whl", hash = "sha256:0d511dda38595b2b6934c2b730a1fd57a3635c6aa2a04cb74714cdfdd53846f4"}, - {file = "coverage-7.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:9a86281794a393513cf117177fd39c796b3f8e3759bb2764259a2abba5cce54b"}, - {file = "coverage-7.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:cebd8e906eb98bb09c10d1feed16096700b1198d482267f8bf0474e63a7b8d84"}, - {file = "coverage-7.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0520dff502da5e09d0d20781df74d8189ab334a1e40d5bafe2efaa4158e2d9e7"}, - {file = "coverage-7.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d9cd64aca68f503ed3f1f18c7c9174cbb797baba02ca8ab5112f9d1c0328cd4b"}, - {file = "coverage-7.10.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0913dd1613a33b13c4f84aa6e3f4198c1a21ee28ccb4f674985c1f22109f0aae"}, - {file = "coverage-7.10.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b7181c0feeb06ed8a02da02792f42f829a7b29990fef52eff257fef0885d760"}, - {file = "coverage-7.10.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d42b7396b605f774d4372dd9c49bed71cbabce4ae1ccd074d155709dd8f235"}, - {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4fdc777e05c4940b297bf47bf7eedd56a39a61dc23ba798e4b830d585486ca5"}, - {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:42144e8e346de44a6f1dbd0a56575dd8ab8dfa7e9007da02ea5b1c30ab33a7db"}, - {file = "coverage-7.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:66c644cbd7aed8fe266d5917e2c9f65458a51cfe5eeff9c05f15b335f697066e"}, - {file = "coverage-7.10.5-cp313-cp313t-win32.whl", hash = "sha256:2d1b73023854068c44b0c554578a4e1ef1b050ed07cf8b431549e624a29a66ee"}, - {file = "coverage-7.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:54a1532c8a642d8cc0bd5a9a51f5a9dcc440294fd06e9dda55e743c5ec1a8f14"}, - {file = "coverage-7.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:74d5b63fe3f5f5d372253a4ef92492c11a4305f3550631beaa432fc9df16fcff"}, - {file = "coverage-7.10.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:68c5e0bc5f44f68053369fa0d94459c84548a77660a5f2561c5e5f1e3bed7031"}, - {file = "coverage-7.10.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cf33134ffae93865e32e1e37df043bef15a5e857d8caebc0099d225c579b0fa3"}, - {file = "coverage-7.10.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad8fa9d5193bafcf668231294241302b5e683a0518bf1e33a9a0dfb142ec3031"}, - {file = "coverage-7.10.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:146fa1531973d38ab4b689bc764592fe6c2f913e7e80a39e7eeafd11f0ef6db2"}, - {file = "coverage-7.10.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6013a37b8a4854c478d3219ee8bc2392dea51602dd0803a12d6f6182a0061762"}, - {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:eb90fe20db9c3d930fa2ad7a308207ab5b86bf6a76f54ab6a40be4012d88fcae"}, - {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:384b34482272e960c438703cafe63316dfbea124ac62006a455c8410bf2a2262"}, - {file = "coverage-7.10.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:467dc74bd0a1a7de2bedf8deaf6811f43602cb532bd34d81ffd6038d6d8abe99"}, - {file = "coverage-7.10.5-cp314-cp314-win32.whl", hash = "sha256:556d23d4e6393ca898b2e63a5bca91e9ac2d5fb13299ec286cd69a09a7187fde"}, - {file = "coverage-7.10.5-cp314-cp314-win_amd64.whl", hash = "sha256:f4446a9547681533c8fa3e3c6cf62121eeee616e6a92bd9201c6edd91beffe13"}, - {file = "coverage-7.10.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e78bd9cf65da4c303bf663de0d73bf69f81e878bf72a94e9af67137c69b9fe9"}, - {file = "coverage-7.10.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5661bf987d91ec756a47c7e5df4fbcb949f39e32f9334ccd3f43233bbb65e508"}, - {file = "coverage-7.10.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a46473129244db42a720439a26984f8c6f834762fc4573616c1f37f13994b357"}, - {file = "coverage-7.10.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1f64b8d3415d60f24b058b58d859e9512624bdfa57a2d1f8aff93c1ec45c429b"}, - {file = "coverage-7.10.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:44d43de99a9d90b20e0163f9770542357f58860a26e24dc1d924643bd6aa7cb4"}, - {file = "coverage-7.10.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a931a87e5ddb6b6404e65443b742cb1c14959622777f2a4efd81fba84f5d91ba"}, - {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9559b906a100029274448f4c8b8b0a127daa4dade5661dfd821b8c188058842"}, - {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b08801e25e3b4526ef9ced1aa29344131a8f5213c60c03c18fe4c6170ffa2874"}, - {file = "coverage-7.10.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed9749bb8eda35f8b636fb7632f1c62f735a236a5d4edadd8bbcc5ea0542e732"}, - {file = "coverage-7.10.5-cp314-cp314t-win32.whl", hash = "sha256:609b60d123fc2cc63ccee6d17e4676699075db72d14ac3c107cc4976d516f2df"}, - {file = "coverage-7.10.5-cp314-cp314t-win_amd64.whl", hash = "sha256:0666cf3d2c1626b5a3463fd5b05f5e21f99e6aec40a3192eee4d07a15970b07f"}, - {file = "coverage-7.10.5-cp314-cp314t-win_arm64.whl", hash = "sha256:bc85eb2d35e760120540afddd3044a5bf69118a91a296a8b3940dfc4fdcfe1e2"}, - {file = "coverage-7.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:62835c1b00c4a4ace24c1a88561a5a59b612fbb83a525d1c70ff5720c97c0610"}, - {file = "coverage-7.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5255b3bbcc1d32a4069d6403820ac8e6dbcc1d68cb28a60a1ebf17e47028e898"}, - {file = "coverage-7.10.5-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3876385722e335d6e991c430302c24251ef9c2a9701b2b390f5473199b1b8ebf"}, - {file = "coverage-7.10.5-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8048ce4b149c93447a55d279078c8ae98b08a6951a3c4d2d7e87f4efc7bfe100"}, - {file = "coverage-7.10.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4028e7558e268dd8bcf4d9484aad393cafa654c24b4885f6f9474bf53183a82a"}, - {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03f47dc870eec0367fcdd603ca6a01517d2504e83dc18dbfafae37faec66129a"}, - {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2d488d7d42b6ded7ea0704884f89dcabd2619505457de8fc9a6011c62106f6e5"}, - {file = "coverage-7.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3dcf2ead47fa8be14224ee817dfc1df98043af568fe120a22f81c0eb3c34ad2"}, - {file = "coverage-7.10.5-cp39-cp39-win32.whl", hash = "sha256:02650a11324b80057b8c9c29487020073d5e98a498f1857f37e3f9b6ea1b2426"}, - {file = "coverage-7.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:b45264dd450a10f9e03237b41a9a24e85cbb1e278e5a32adb1a303f58f0017f3"}, - {file = "coverage-7.10.5-py3-none-any.whl", hash = "sha256:0be24d35e4db1d23d0db5c0f6a74a962e2ec83c426b5cac09f4234aadef38e4a"}, - {file = "coverage-7.10.5.tar.gz", hash = "sha256:f2e57716a78bc3ae80b2207be0709a3b2b63b9f2dcf9740ee6ac03588a2015b6"}, + {file = "coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356"}, + {file = "coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301"}, + {file = "coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460"}, + {file = "coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd"}, + {file = "coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb"}, + {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6"}, + {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945"}, + {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e"}, + {file = "coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1"}, + {file = "coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528"}, + {file = "coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f"}, + {file = "coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc"}, + {file = "coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a"}, + {file = "coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a"}, + {file = "coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62"}, + {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153"}, + {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5"}, + {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619"}, + {file = "coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba"}, + {file = "coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e"}, + {file = "coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c"}, + {file = "coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea"}, + {file = "coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634"}, + {file = "coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6"}, + {file = "coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9"}, + {file = "coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c"}, + {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a"}, + {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5"}, + {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972"}, + {file = "coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d"}, + {file = "coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629"}, + {file = "coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80"}, + {file = "coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6"}, + {file = "coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80"}, + {file = "coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003"}, + {file = "coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27"}, + {file = "coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4"}, + {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d"}, + {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc"}, + {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc"}, + {file = "coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e"}, + {file = "coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32"}, + {file = "coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2"}, + {file = "coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b"}, + {file = "coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393"}, + {file = "coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27"}, + {file = "coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df"}, + {file = "coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb"}, + {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282"}, + {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4"}, + {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21"}, + {file = "coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0"}, + {file = "coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5"}, + {file = "coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b"}, + {file = "coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e"}, + {file = "coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb"}, + {file = "coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034"}, + {file = "coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1"}, + {file = "coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a"}, + {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb"}, + {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d"}, + {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747"}, + {file = "coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5"}, + {file = "coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713"}, + {file = "coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32"}, + {file = "coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65"}, + {file = "coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6"}, + {file = "coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0"}, + {file = "coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e"}, + {file = "coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5"}, + {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7"}, + {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5"}, + {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0"}, + {file = "coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7"}, + {file = "coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930"}, + {file = "coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b"}, + {file = "coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352"}, + {file = "coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612"}, + {file = "coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b"}, + {file = "coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144"}, + {file = "coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b"}, + {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862"}, + {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2"}, + {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78"}, + {file = "coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c"}, + {file = "coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf"}, + {file = "coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3"}, + {file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"}, ] [package.dependencies] @@ -1446,14 +1446,14 @@ pyyaml = "*" [[package]] name = "questionary" -version = "2.1.0" +version = "2.1.1" description = "Python library to build pretty command line user prompts ⭐️" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, - {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, + {file = "questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59"}, + {file = "questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d"}, ] [package.dependencies] @@ -1633,14 +1633,14 @@ files = [ [[package]] name = "tox" -version = "4.28.4" +version = "4.29.0" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tox-4.28.4-py3-none-any.whl", hash = "sha256:8d4ad9ee916ebbb59272bb045e154a10fa12e3bbdcf94cc5185cbdaf9b241f99"}, - {file = "tox-4.28.4.tar.gz", hash = "sha256:b5b14c6307bd8994ff1eba5074275826620325ee1a4f61316959d562bfd70b9d"}, + {file = "tox-4.29.0-py3-none-any.whl", hash = "sha256:b914f134176cea74c5e01c29cb4befc8afa4cd38b356c3756eff85832d27b5c0"}, + {file = "tox-4.29.0.tar.gz", hash = "sha256:7b3a2bb43974285110eee35a859f2b3f2e87a24f6e1011d83f466b7c75835bd2"}, ] [package.dependencies] From 2d5f7bfb7b287bde6f2dfc5770c6357920534893 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 16:31:42 +0800 Subject: [PATCH 188/275] refactor(changelog): simplify logic for get_oldest_and_newest_rev --- commitizen/changelog.py | 45 ++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 1a1938c7c3..7f5fe471ff 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -313,39 +313,28 @@ def get_oldest_and_newest_rev( - `0.1.0..0.4.0`: as a range - `0.3.0`: as a single version """ - oldest: str | None = None - newest: str | None = None - try: - oldest, newest = version.split("..") - except ValueError: - newest = version - if not (newest_tag := rules.find_tag_for(tags, newest)): + oldest_version, sep, newest_version = version.partition("..") + if not sep: + newest_version = version + oldest_version = "" + + def get_tag_name(v: str) -> str: + if tag := rules.find_tag_for(tags, v): + return tag.name raise NoCommitsFoundError("Could not find a valid revision range.") - oldest_tag = None - oldest_tag_name = None - if oldest: - if not (oldest_tag := rules.find_tag_for(tags, oldest)): - raise NoCommitsFoundError("Could not find a valid revision range.") - oldest_tag_name = oldest_tag.name + newest_tag_name = get_tag_name(newest_version) + oldest_tag_name = get_tag_name(oldest_version) if oldest_version else None - tags_range = get_smart_tag_range( - tags, newest=newest_tag.name, oldest=oldest_tag_name - ) + tags_range = get_smart_tag_range(tags, newest_tag_name, oldest_tag_name) if not tags_range: raise NoCommitsFoundError("Could not find a valid revision range.") oldest_rev: str | None = tags_range[-1].name - newest_rev = newest_tag.name - - # check if it's the first tag created - # and it's also being requested as part of the range - if oldest_rev == tags[-1].name and oldest_rev == oldest_tag_name: - return None, newest_rev - - # when they are the same, and it's also the - # first tag created - if oldest_rev == newest_rev: - return None, newest_rev - return oldest_rev, newest_rev + # Return None for oldest_rev if: + # 1. The oldest tag is the last tag in the list and matches the requested oldest tag + # 2. The oldest and the newest tag are the same + if oldest_rev == oldest_tag_name == tags[-1].name or oldest_rev == newest_tag_name: + oldest_rev = None + return oldest_rev, newest_tag_name From 8172be7eeb03863055b39ca31f7b464d79a87423 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 12 Jun 2025 21:24:30 +0800 Subject: [PATCH 189/275] refactor(changelog): add get_next_tag_name_after_version and test, mark unused for get_smart_tag_range --- commitizen/changelog.py | 24 ++++++++++++++++-------- tests/test_changelog.py | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 7f5fe471ff..df04ad5c09 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -276,6 +276,16 @@ def incremental_build( return output_lines +def get_next_tag_name_after_version(tags: Iterable[GitTag], version: str) -> str | None: + it = iter(tag.name for tag in tags) + for name in it: + if name == version: + return next(it, None) + + raise NoCommitsFoundError(f"Could not find a valid revision range. {version=}") + + +# TODO: unused, deprecate this? def get_smart_tag_range( tags: Sequence[GitTag], newest: str, oldest: str | None = None ) -> list[GitTag]: @@ -303,7 +313,7 @@ def get_smart_tag_range( def get_oldest_and_newest_rev( - tags: Sequence[GitTag], + tags: Iterable[GitTag], version: str, rules: TagRules, ) -> tuple[str | None, str]: @@ -326,15 +336,13 @@ def get_tag_name(v: str) -> str: newest_tag_name = get_tag_name(newest_version) oldest_tag_name = get_tag_name(oldest_version) if oldest_version else None - tags_range = get_smart_tag_range(tags, newest_tag_name, oldest_tag_name) - if not tags_range: - raise NoCommitsFoundError("Could not find a valid revision range.") - - oldest_rev: str | None = tags_range[-1].name + oldest_rev = get_next_tag_name_after_version( + tags, oldest_tag_name or newest_tag_name + ) # Return None for oldest_rev if: # 1. The oldest tag is the last tag in the list and matches the requested oldest tag # 2. The oldest and the newest tag are the same - if oldest_rev == oldest_tag_name == tags[-1].name or oldest_rev == newest_tag_name: - oldest_rev = None + if oldest_rev == newest_tag_name: + return None, newest_tag_name return oldest_rev, newest_tag_name diff --git a/tests/test_changelog.py b/tests/test_changelog.py index ed90ed08e4..4465fcccbc 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1535,6 +1535,24 @@ def test_get_smart_tag_range_returns_an_extra_for_a_single_tag(tags): assert 2 == len(res) +def test_get_next_tag_name_after_version(tags): + # Test finding next tag after a version + next_tag_name = changelog.get_next_tag_name_after_version(tags, "v1.2.0") + assert next_tag_name == "v1.1.1" + + next_tag_name = changelog.get_next_tag_name_after_version(tags, "v1.1.0") + assert next_tag_name == "v1.0.0" + + # Test finding last tag when given version is last + last_tag_name = changelog.get_next_tag_name_after_version(tags, "v0.9.1") + assert last_tag_name is None + + # Test error when version not found + with pytest.raises(changelog.NoCommitsFoundError) as exc_info: + changelog.get_next_tag_name_after_version(tags, "nonexistent") + assert "Could not find a valid revision range" in str(exc_info.value) + + @dataclass class TagDef: name: str From 67a828c01d3614c789d0ce43d71ab6a718ff670a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 24 Aug 2025 21:03:13 +0800 Subject: [PATCH 190/275] fix(changelog): mark get_smart_tag_range as deprecated --- commitizen/changelog.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index df04ad5c09..bdf11326be 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -35,6 +35,7 @@ from itertools import chain from typing import TYPE_CHECKING, Any +from deprecated import deprecated from jinja2 import ( BaseLoader, ChoiceLoader, @@ -285,7 +286,11 @@ def get_next_tag_name_after_version(tags: Iterable[GitTag], version: str) -> str raise NoCommitsFoundError(f"Could not find a valid revision range. {version=}") -# TODO: unused, deprecate this? +@deprecated( + reason="This function is unused and will be removed in v5", + version="5.0.0", + category=DeprecationWarning, +) def get_smart_tag_range( tags: Sequence[GitTag], newest: str, oldest: str | None = None ) -> list[GitTag]: From 73136ed6f38bd7bff1f37835c47b9753a40e956c Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 30 Aug 2025 12:12:20 +0800 Subject: [PATCH 191/275] style(pyproject.toml): fix toml format --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6ff5219328..99f78caa36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,8 @@ readme = "docs/README.md" requires-python = ">=3.9,<4.0" dependencies = [ "questionary (>=2.0,<3.0)", - "prompt_toolkit!=3.0.52", # Exclude transitive dependency due to known issue in questionary: https://github.com/tmbo/questionary/issues/454 + # Exclude transitive dependency due to known issue in questionary: https://github.com/tmbo/questionary/issues/454 + "prompt_toolkit!=3.0.52", "decli (>=0.6.0,<1.0)", "colorama (>=0.4.1,<1.0)", "termcolor (>=1.1.0,<4.0.0)", From 98bba31fe6c9214cd49767e41802fa099d0c47a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 06:09:44 +0000 Subject: [PATCH 192/275] =?UTF-8?q?bump:=20version=204.8.4=20=E2=86=92=204?= =?UTF-8?q?.9.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 46f6d04e34..10182d08e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.8.4 # automatically updated by Commitizen + rev: v4.9.0 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 5723e11274..6b528c0f83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +## v4.9.0 (2025-09-09) + +### Feat + +- **check**: add check against default branch + +### Fix + +- **changelog**: mark get_smart_tag_range as deprecated +- **init**: use pre-push as pre-commit stage +- **init**: use pre-push as pre-commit stage +- **init**: make welcome message easier to read +- **Init**: fix a typo in _ask_version_provider options and remove unnecessary filter, use named tuple for options +- **ExitCode**: add from_str in ExitCode and replace parse_no_raise with it +- raise NoVersionSpecifiedError if version is None, and adjust call sites of get_version +- **Changelog**: fix _export_template variable type +- **Bump**: rewrite --get-next NotAllowed error message for consistency + +### Refactor + +- **changelog**: add get_next_tag_name_after_version and test, mark unused for get_smart_tag_range +- **changelog**: simplify logic for get_oldest_and_newest_rev +- **changelog**: shorten generate_tree_from_commits +- **Init**: remove the variable values_to_add and the update_config function for readability +- **Init**: remove unnecessary methods from ProjectInfo and refactor _ask_tag +- **Init**: fix unbounded variable in _ask_tag_format +- **init**: remote extra words +- **process_commit_message**: better type and early return +- **Init**: extract _get_config_data for readability +- **changelog**: shorten condition expression and early return +- **Changelog**: remove unnecessary intermediate variables for better readability +- **bump**: use a loop to shorten a series of similar NotAllowed exceptions +- **Init**: use ternary operator +- **TagRules**: extract tag_formats property and simplify list comprehension +- **git**: remove redundant if branch +- **ScmProvider**: replace sorted with max +- **ExpectedExit**: make the constructor more compact +- **ParseArgs**: simplify __call__ function body + ## v4.8.4 (2025-09-05) ### Fix diff --git a/commitizen/__version__.py b/commitizen/__version__.py index adba53e1e0..454a2edae2 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.8.4" +__version__ = "4.9.0" diff --git a/pyproject.toml b/pyproject.toml index 99f78caa36..986ac465c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.8.4" +version = "4.9.0" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -89,7 +89,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.8.4" +version = "4.9.0" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 8f27018587f442c8e574a206dc14b5386e951fd8 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 9 Sep 2025 00:36:18 -0600 Subject: [PATCH 193/275] fix(dependency): move deprecated to project.dependencies --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 42af6b9c99..1a731e4f06 100644 --- a/poetry.lock +++ b/poetry.lock @@ -364,7 +364,7 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["test"] +groups = ["main"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -1846,7 +1846,7 @@ version = "1.17.3" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["test"] +groups = ["main"] files = [ {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, @@ -1955,4 +1955,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "79d126ebbb9e2d0976e3b2853980826e4b32b62ca9d982507bf21aff9bffacd0" +content-hash = "bdc8773ed978a4265a2a099265db7e116d2f65c467c4980d984e546716cea244" diff --git a/pyproject.toml b/pyproject.toml index 986ac465c3..d033fc245e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dependencies = [ "argcomplete >=1.12.1,<3.7", "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'", "charset-normalizer (>=2.1.0,<4)", + "deprecated (>=1.2.13, <2)", # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility "importlib-metadata >=8.0.0,<8.7.0 ; python_version < '3.10'", ] @@ -117,7 +118,6 @@ pytest-mock = "^3.10" pytest-regressions = "^2.4.0" pytest-freezer = "^0.4.6" pytest-xdist = "^3.1.0" -deprecated = "^1.2.13" [tool.poetry.group.linters.dependencies] ruff = "^0.11.5" From 8f903e7a6130aabb8836a60ebb747b07cf40ef9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 14:19:09 +0000 Subject: [PATCH 194/275] docs(cli/screenshots): update CLI screenshots [skip ci] --- docs/images/cli_help/cz_check___help.svg | 142 ++++++++++++----------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/docs/images/cli_help/cz_check___help.svg b/docs/images/cli_help/cz_check___help.svg index 690bfec684..397c0a2eeb 100644 --- a/docs/images/cli_help/cz_check___help.svg +++ b/docs/images/cli_help/cz_check___help.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + - + - + - - $ cz check --help -usage: cz check [-h][--commit-msg-file COMMIT_MSG_FILE | -                --rev-range REV_RANGE | -m MESSAGE][--allow-abort] -[--allowed-prefixes [ALLOWED_PREFIXES ...]] -[-l MESSAGE_LENGTH_LIMIT] - -validates that a commit message matches the commitizen schema - -options: -  -h, --help            show this help message and exit -  --commit-msg-file COMMIT_MSG_FILE -                        ask for the name of the temporal file that contains -                        the commit message. Using it in a git hook script: -MSG_FILE=$1 -  --rev-range REV_RANGE -                        a range of git rev to check. e.g, master..HEAD -  -m, --message MESSAGE -                        commit message that needs to be checked -  --allow-abort         allow empty commit messages, which typically abort a -                        commit -  --allowed-prefixes [ALLOWED_PREFIXES ...] -                        allowed commit message prefixes. If the message starts -                        by one of these prefixes, the message won't be checked -                        against the regex -  -l, --message-length-limit MESSAGE_LENGTH_LIMIT -                        length limit of the commit message; 0 for no limit - + + $ cz check --help +usage: cz check [-h][--commit-msg-file COMMIT_MSG_FILE | +                --rev-range REV_RANGE | -d | -m MESSAGE][--allow-abort] +[--allowed-prefixes [ALLOWED_PREFIXES ...]] +[-l MESSAGE_LENGTH_LIMIT] + +validates that a commit message matches the commitizen schema + +options: +  -h, --help            show this help message and exit +  --commit-msg-file COMMIT_MSG_FILE +                        ask for the name of the temporal file that contains +                        the commit message. Using it in a git hook script: +MSG_FILE=$1 +  --rev-range REV_RANGE +                        a range of git rev to check. e.g, master..HEAD +  -d, --use-default-range +                        check from the default branch to HEAD. e.g, +                        refs/remotes/origin/master..HEAD +  -m, --message MESSAGE +                        commit message that needs to be checked +  --allow-abort         allow empty commit messages, which typically abort a +                        commit +  --allowed-prefixes [ALLOWED_PREFIXES ...] +                        allowed commit message prefixes. If the message starts +                        by one of these prefixes, the message won't be checked +                        against the regex +  -l, --message-length-limit MESSAGE_LENGTH_LIMIT +                        length limit of the commit message; 0 for no limit + From cc981fcb065527b6de0033b0cbf0432e69781901 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 14:19:10 +0000 Subject: [PATCH 195/275] =?UTF-8?q?bump:=20version=204.9.0=20=E2=86=92=204?= =?UTF-8?q?.9.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 10182d08e2..4af318e4eb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.9.0 # automatically updated by Commitizen + rev: v4.9.1 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b528c0f83..a72dc0fda0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.9.1 (2025-09-10) + +### Fix + +- **dependency**: move deprecated to project.dependencies + ## v4.9.0 (2025-09-09) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 454a2edae2..0139b2ebd5 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.9.0" +__version__ = "4.9.1" diff --git a/pyproject.toml b/pyproject.toml index d033fc245e..f16ad596bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.9.0" +version = "4.9.1" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -90,7 +90,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.9.0" +version = "4.9.1" tag_format = "v$version" version_files = [ "pyproject.toml:version", From b76301d235b85610576dfd2c9f76933cb73183a1 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 11:26:27 +0800 Subject: [PATCH 196/275] test(Init): add type hint and rename variable --- tests/commands/test_init_command.py | 71 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index c156fb0234..ba4a150622 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -11,6 +11,7 @@ from commitizen import cli, commands from commitizen.__version__ import __version__ +from commitizen.config.base_config import BaseConfig from commitizen.exceptions import InitFailedError, NoAnswersError from tests.utils import skip_below_py_3_10 @@ -58,7 +59,9 @@ def unsafe_ask(self): } -def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config): +def test_init_without_setup_pre_commit_hook( + tmpdir, mocker: MockFixture, config: BaseConfig +): mocker.patch( "questionary.select", side_effect=[ @@ -72,7 +75,6 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) mocker.patch("questionary.text", return_value=FakeQuestion("$version")) # Return None to skip hook installation mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) - with tmpdir.as_cwd(): commands.Init(config)() @@ -83,7 +85,7 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) assert not os.path.isfile(pre_commit_config_filename) -def test_init_when_config_already_exists(config, capsys): +def test_init_when_config_already_exists(config: BaseConfig, capsys): # Set config path path = os.sep.join(["tests", "pyproject.toml"]) config.path = path @@ -93,7 +95,7 @@ def test_init_when_config_already_exists(config, capsys): assert captured.out == f"Config file {path} already exists\n" -def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): +def test_init_without_choosing_tag(config: BaseConfig, mocker: MockFixture, tmpdir): mocker.patch( "commitizen.commands.init.get_tag_names", return_value=["0.0.2", "0.0.1"] ) @@ -115,7 +117,7 @@ def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): commands.Init(config)() -def test_executed_pre_commit_command(config): +def test_executed_pre_commit_command(config: BaseConfig): init = commands.Init(config) expected_cmd = "pre-commit install --hook-type commit-msg --hook-type pre-push" assert init._gen_pre_commit_cmd(["commit-msg", "pre-push"]) == expected_cmd @@ -155,17 +157,14 @@ def default_choice(request, mocker: MockFixture): yield request.param -def check_cz_config(config: str): +def check_cz_config(config_filepath: str): """ Check the content of commitizen config is as expected - - Args: - config: The config path """ - with open(config) as file: - if "json" in config: + with open(config_filepath) as file: + if "json" in config_filepath: assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in config: + elif "yaml" in config_filepath: assert yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG else: config_data = file.read() @@ -183,13 +182,17 @@ def check_pre_commit_config(expected: list[dict[str, Any]]): @pytest.mark.usefixtures("pre_commit_installed") class TestPreCommitCases: - def test_no_existing_pre_commit_config(_, default_choice, tmpdir, config): + def test_no_existing_pre_commit_config( + _, default_choice: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): commands.Init(config)() check_cz_config(default_choice) check_pre_commit_config([cz_hook_config]) - def test_empty_pre_commit_config(_, default_choice, tmpdir, config): + def test_empty_pre_commit_config( + _, default_choice: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): p = tmpdir.join(pre_commit_config_filename) p.write("") @@ -198,7 +201,9 @@ def test_empty_pre_commit_config(_, default_choice, tmpdir, config): check_cz_config(default_choice) check_pre_commit_config([cz_hook_config]) - def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): + def test_pre_commit_config_without_cz_hook( + _, default_choice: str, tmpdir, config: BaseConfig + ): existing_hook_config = { "repo": "https://github.com/pre-commit/pre-commit-hooks", "rev": "v1.2.3", @@ -213,7 +218,9 @@ def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): check_cz_config(default_choice) check_pre_commit_config([existing_hook_config, cz_hook_config]) - def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): + def test_cz_hook_exists_in_pre_commit_config( + _, default_choice: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): p = tmpdir.join(pre_commit_config_filename) p.write(yaml.safe_dump({"repos": [cz_hook_config]})) @@ -226,7 +233,7 @@ def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): class TestNoPreCommitInstalled: def test_pre_commit_not_installed( - _, mocker: MockFixture, config, default_choice, tmpdir + _, mocker: MockFixture, config: BaseConfig, default_choice: str, tmpdir ): # Assume `pre-commit` is not installed mocker.patch( @@ -238,7 +245,7 @@ def test_pre_commit_not_installed( commands.Init(config)() def test_pre_commit_exec_failed( - _, mocker: MockFixture, config, default_choice, tmpdir + _, mocker: MockFixture, config: BaseConfig, default_choice: str, tmpdir ): # Assume `pre-commit` is installed mocker.patch( @@ -256,14 +263,14 @@ def test_pre_commit_exec_failed( class TestAskTagFormat: - def test_confirm_v_tag_format(self, mocker: MockFixture, config): + def test_confirm_v_tag_format(self, mocker: MockFixture, config: BaseConfig): init = commands.Init(config) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) result = init._ask_tag_format("v1.0.0") assert result == r"v$version" - def test_reject_v_tag_format(self, mocker: MockFixture, config): + def test_reject_v_tag_format(self, mocker: MockFixture, config: BaseConfig): init = commands.Init(config) mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) @@ -271,14 +278,14 @@ def test_reject_v_tag_format(self, mocker: MockFixture, config): result = init._ask_tag_format("v1.0.0") assert result == "custom-$version" - def test_non_v_tag_format(self, mocker: MockFixture, config): + def test_non_v_tag_format(self, mocker: MockFixture, config: BaseConfig): init = commands.Init(config) mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) result = init._ask_tag_format("1.0.0") assert result == "custom-$version" - def test_empty_input_returns_default(self, mocker: MockFixture, config): + def test_empty_input_returns_default(self, mocker: MockFixture, config: BaseConfig): init = commands.Init(config) mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) mocker.patch("questionary.text", return_value=FakeQuestion("")) @@ -300,7 +307,9 @@ def test_init_command_shows_description_when_use_help_option( file_regression.check(out, extension=".txt") -def test_init_with_confirmed_tag_format(config, mocker: MockFixture, tmpdir): +def test_init_with_confirmed_tag_format( + config: BaseConfig, mocker: MockFixture, tmpdir +): mocker.patch( "commitizen.commands.init.get_tag_names", return_value=["v0.0.2", "v0.0.1"] ) @@ -324,7 +333,7 @@ def test_init_with_confirmed_tag_format(config, mocker: MockFixture, tmpdir): assert 'tag_format = "v$version"' in toml_file.read() -def test_init_with_no_existing_tags(config, mocker: MockFixture, tmpdir): +def test_init_with_no_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): mocker.patch("commitizen.commands.init.get_tag_names", return_value=[]) mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") mocker.patch( @@ -346,7 +355,9 @@ def test_init_with_no_existing_tags(config, mocker: MockFixture, tmpdir): assert 'version = "0.0.1"' in toml_file.read() -def test_init_with_no_existing_latest_tag(config, mocker: MockFixture, tmpdir): +def test_init_with_no_existing_latest_tag( + config: BaseConfig, mocker: MockFixture, tmpdir +): mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value=None) mocker.patch( "questionary.select", @@ -367,7 +378,7 @@ def test_init_with_no_existing_latest_tag(config, mocker: MockFixture, tmpdir): assert 'version = "0.0.1"' in toml_file.read() -def test_init_with_existing_tags(config, mocker: MockFixture, tmpdir): +def test_init_with_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") @@ -391,7 +402,7 @@ def test_init_with_existing_tags(config, mocker: MockFixture, tmpdir): assert 'version = "1.0.0"' in toml_file.read() -def test_init_with_valid_tag_selection(config, mocker: MockFixture, tmpdir): +def test_init_with_valid_tag_selection(config: BaseConfig, mocker: MockFixture, tmpdir): expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") @@ -422,7 +433,7 @@ def test_init_with_valid_tag_selection(config, mocker: MockFixture, tmpdir): assert 'version_scheme = "semver"' in content -def test_init_configuration_settings(tmpdir, mocker: MockFixture, config): +def test_init_configuration_settings(tmpdir, mocker: MockFixture, config: BaseConfig): """Test that all configuration settings are properly initialized.""" mocker.patch( "questionary.select", @@ -452,7 +463,9 @@ def test_init_configuration_settings(tmpdir, mocker: MockFixture, config): assert "major_version_zero = true" in config_data -def test_init_configuration_with_version_provider(tmpdir, mocker: MockFixture, config): +def test_init_configuration_with_version_provider( + tmpdir, mocker: MockFixture, config: BaseConfig +): """Test configuration initialization with a different version provider.""" mocker.patch( "questionary.select", From 6a2c806aac8fc3c382665a477fb29f712003bb99 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 21 Sep 2025 12:18:14 +0800 Subject: [PATCH 197/275] docs: enable blank issue and add url to contact links --- .github/ISSUE_TEMPLATE/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 884fe1663a..e624b37579 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,4 +3,5 @@ blank_issues_enabled: false contact_links: - name: Security Contact + url: https://github.com/woile about: Please report security vulnerabilities to santiwilly@gmail.com From d3893307eadb3676c60c571b18f185b3e75979b6 Mon Sep 17 00:00:00 2001 From: Philip Nelson Date: Tue, 28 Oct 2025 12:38:37 -0600 Subject: [PATCH 198/275] docs: fix typo in bump command hooks description --- docs/commands/bump.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 548f6e3616..47e64dfc6e 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -589,7 +589,7 @@ execution of the script, some environment variables are available: | `CZ_PRE_NEW_VERSION` | New version, after the bump | | `CZ_PRE_NEW_TAG_VERSION` | New version tag, after the bump | | `CZ_PRE_MESSAGE` | Commit message of the bump | -| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATH` release | +| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATCH` release | | `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | ```toml title="pyproject.toml" @@ -616,7 +616,7 @@ release. During execution of the script, some environment variables are availabl | `CZ_POST_CURRENT_VERSION` | Current version, after the bump | | `CZ_POST_CURRENT_TAG_VERSION` | Current version tag, after the bump | | `CZ_POST_MESSAGE` | Commit message of the bump | -| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATH` release | +| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATCH` release | | `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | ```toml title="pyproject.toml" From ef8e4bcdd077b02440581a83ee44f56f6adff5ce Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 19:44:16 +0800 Subject: [PATCH 199/275] refactor(BaseCommitizen): construct Style object directly to get rid of potential type error --- commitizen/cz/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index cdc1476694..75981f0747 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -5,7 +5,7 @@ from typing import Any, Callable, Protocol from jinja2 import BaseLoader, PackageLoader -from prompt_toolkit.styles import Style, merge_styles +from prompt_toolkit.styles import Style from commitizen import git from commitizen.config.base_config import BaseConfig @@ -77,12 +77,12 @@ def message(self, answers: Mapping[str, Any]) -> str: @property def style(self) -> Style: - return merge_styles( + return Style( [ - Style(BaseCommitizen.default_style_config), - Style(self.config.settings["style"]), + *BaseCommitizen.default_style_config, + *self.config.settings["style"], ] - ) # type: ignore[return-value] + ) def example(self) -> str: """Example of the commit message.""" From bdcf2aa07c556fbb1462da45d6b52c867bcdf6a6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 20:04:10 +0800 Subject: [PATCH 200/275] refactor(BaseCommitizen): remove NotImplementedError and make them abstract method --- commitizen/cz/base.py | 8 ++++---- tests/conftest.py | 24 ++++++++++++++++++++++ tests/test_cz_base.py | 46 ------------------------------------------- 3 files changed, 28 insertions(+), 50 deletions(-) delete mode 100644 tests/test_cz_base.py diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 75981f0747..ecb1a21962 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -84,18 +84,18 @@ def style(self) -> Style: ] ) + @abstractmethod def example(self) -> str: """Example of the commit message.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def schema(self) -> str: """Schema definition of the commit message.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def schema_pattern(self) -> str: """Regex matching the schema used for message validation.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def info(self) -> str: """Information about the standardized commit message.""" - raise NotImplementedError("Not Implemented yet") diff --git a/tests/conftest.py b/tests/conftest.py index 324ef9bebb..61b64ae8d2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -215,6 +215,18 @@ def message(self, answers: Mapping) -> str: subject = answers.get("subject", "default message").trim() return f"{prefix}: {subject}" + def example(self) -> str: + return "" + + def schema(self) -> str: + return "" + + def schema_pattern(self) -> str: + return "" + + def info(self) -> str: + return "" + @pytest.fixture() def use_cz_semver(mocker): @@ -229,6 +241,18 @@ def questions(self) -> list[CzQuestion]: def message(self, answers: Mapping) -> str: return "" + def example(self) -> str: + return "" + + def schema(self) -> str: + return "" + + def schema_pattern(self) -> str: + return "" + + def info(self) -> str: + return "" + @pytest.fixture def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen: diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py deleted file mode 100644 index 0ee5a23fb8..0000000000 --- a/tests/test_cz_base.py +++ /dev/null @@ -1,46 +0,0 @@ -from collections.abc import Mapping - -import pytest - -from commitizen.cz.base import BaseCommitizen - - -class DummyCz(BaseCommitizen): - def questions(self): - return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}] - - def message(self, answers: Mapping): - return answers["commit"] - - -def test_base_raises_error(config): - with pytest.raises(TypeError): - BaseCommitizen(config) - - -def test_questions(config): - cz = DummyCz(config) - assert isinstance(cz.questions(), list) - - -def test_message(config): - cz = DummyCz(config) - assert cz.message({"commit": "holis"}) == "holis" - - -def test_example(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.example() - - -def test_schema(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.schema() - - -def test_info(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.info() From 5db95efd15baa3ed1ce32df572d115be7d772eba Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Tue, 16 Sep 2025 21:21:53 +0800 Subject: [PATCH 201/275] style(UvProvider): fix typo in comment --- commitizen/providers/uv_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/providers/uv_provider.py b/commitizen/providers/uv_provider.py index 36c8a49ad3..21e8322d94 100644 --- a/commitizen/providers/uv_provider.py +++ b/commitizen/providers/uv_provider.py @@ -13,7 +13,7 @@ class UvProvider(TomlProvider): """ - uv.lock and pyproject.tom version management + uv.lock and pyproject.toml version management """ filename = "pyproject.toml" From 319d89e494baa352f0c92b42c9683b8385e49e17 Mon Sep 17 00:00:00 2001 From: Christian Heissenberger Date: Fri, 5 Sep 2025 20:20:52 +0200 Subject: [PATCH 202/275] feat: allow `amend!` prefix as created by `git --fixup=reword:` https://git-scm.com/docs/git-commit/2.32.0#Documentation/git-commit.txt---fixupamendrewordcommit --- commitizen/defaults.py | 1 + tests/commands/test_check_command.py | 8 ++++++++ tests/test_conf.py | 18 ++++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 94d4d97b22..4840a69b6a 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -92,6 +92,7 @@ class Settings(TypedDict, total=False): "Pull request", "fixup!", "squash!", + "amend!", ], "changelog_file": "CHANGELOG.md", "changelog_format": None, # default guessed from changelog_file diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index d95a173d8a..365a556dda 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -452,3 +452,11 @@ def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFi with pytest.raises(InvalidCommitMessageError): check_cmd() error_mock.assert_called_once() + + +def test_check_command_with_amend_prefix_default(config, mocker: MockFixture): + success_mock = mocker.patch("commitizen.out.success") + check_cmd = commands.Check(config=config, arguments={"message": "amend! test"}) + + check_cmd() + success_mock.assert_called_once() diff --git a/tests/test_conf.py b/tests/test_conf.py index f89a0049f4..cc38bf4b05 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -77,7 +77,14 @@ "bump_message": None, "retry_after_failure": False, "allow_abort": False, - "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!", + ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", @@ -108,7 +115,14 @@ "bump_message": None, "retry_after_failure": False, "allow_abort": False, - "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!", + ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", From 334b8405d3f95742fe20effe53c351bf9bec4a00 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 13:25:40 +0800 Subject: [PATCH 203/275] fix(Init): raise InitFailedError on keyboard interrupt on pre-commit hook question, simplify logic, remove unreachable code path --- commitizen/commands/init.py | 82 ++++++++++++----------------- tests/commands/test_init_command.py | 29 ++-------- 2 files changed, 37 insertions(+), 74 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 2ce3981f44..92e7d06d7d 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -150,9 +150,43 @@ def __call__(self) -> None: tag_format = self._ask_tag_format(tag) # confirm & text update_changelog_on_bump = self._ask_update_changelog_on_bump() # confirm major_version_zero = self._ask_major_version_zero(version) # confirm + hook_types: list[str] | None = questionary.checkbox( + "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", + choices=[ + questionary.Choice("commit-msg", checked=False), + questionary.Choice("pre-push", checked=False), + ], + ).unsafe_ask() except KeyboardInterrupt: raise InitFailedError("Stopped by user") + if hook_types: + config_data = self._get_config_data() + with smart_open( + self._PRE_COMMIT_CONFIG_PATH, "w", encoding=self.encoding + ) as config_file: + yaml.safe_dump(config_data, stream=config_file) + + if not self.project_info.is_pre_commit_installed: + raise InitFailedError( + "Failed to install pre-commit hook.\n" + "pre-commit is not installed in current environment." + ) + + cmd_str = "pre-commit install " + " ".join( + f"--hook-type {ty}" for ty in hook_types + ) + c = cmd.run(cmd_str) + if c.return_code != 0: + raise InitFailedError( + "Failed to install pre-commit hook.\n" + f"Error running {cmd_str}." + "Outputs are attached below:\n" + f"stdout: {c.out}\n" + f"stderr: {c.err}" + ) + out.write("commitizen pre-commit hook is now installed in your '.git'\n") + # Initialize configuration if "toml" in config_path: self.config = TomlConfig(data="", path=config_path) @@ -161,20 +195,6 @@ def __call__(self) -> None: elif "yaml" in config_path: self.config = YAMLConfig(data="", path=config_path) - # Collect hook data - hook_types = questionary.checkbox( - "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", - choices=[ - questionary.Choice("commit-msg", checked=False), - questionary.Choice("pre-push", checked=False), - ], - ).unsafe_ask() - if hook_types: - try: - self._install_pre_commit_hook(hook_types) - except InitFailedError as e: - raise InitFailedError(f"Failed to install pre-commit hook.\n{e}") - # Create and initialize config self.config.init_empty_config_content() @@ -321,26 +341,6 @@ def _ask_update_changelog_on_bump(self) -> bool: ).unsafe_ask() return update_changelog_on_bump - def _exec_install_pre_commit_hook(self, hook_types: list[str]) -> None: - cmd_str = self._gen_pre_commit_cmd(hook_types) - c = cmd.run(cmd_str) - if c.return_code != 0: - err_msg = ( - f"Error running {cmd_str}." - "Outputs are attached below:\n" - f"stdout: {c.out}\n" - f"stderr: {c.err}" - ) - raise InitFailedError(err_msg) - - def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str: - """Generate pre-commit command according to given hook types""" - if not hook_types: - raise ValueError("At least 1 hook type should be provided.") - return "pre-commit install " + " ".join( - f"--hook-type {ty}" for ty in hook_types - ) - def _get_config_data(self) -> dict[str, Any]: CZ_HOOK_CONFIG = { "repo": "https://github.com/commitizen-tools/commitizen", @@ -369,17 +369,3 @@ def _get_config_data(self) -> dict[str, Any]: else: repos.append(CZ_HOOK_CONFIG) return config_data - - def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None: - config_data = self._get_config_data() - with smart_open( - self._PRE_COMMIT_CONFIG_PATH, "w", encoding=self.encoding - ) as config_file: - yaml.safe_dump(config_data, stream=config_file) - - if not self.project_info.is_pre_commit_installed: - raise InitFailedError("pre-commit is not installed in current environment.") - if hook_types is None: - hook_types = ["commit-msg", "pre-push"] - self._exec_install_pre_commit_hook(hook_types) - out.write("commitizen pre-commit hook is now installed in your '.git'\n") diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index ba4a150622..8c632a2b67 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -9,7 +9,7 @@ import yaml from pytest_mock import MockFixture -from commitizen import cli, commands +from commitizen import cli, cmd, commands from commitizen.__version__ import __version__ from commitizen.config.base_config import BaseConfig from commitizen.exceptions import InitFailedError, NoAnswersError @@ -117,12 +117,6 @@ def test_init_without_choosing_tag(config: BaseConfig, mocker: MockFixture, tmpd commands.Init(config)() -def test_executed_pre_commit_command(config: BaseConfig): - init = commands.Init(config) - expected_cmd = "pre-commit install --hook-type commit-msg --hook-type pre-push" - assert init._gen_pre_commit_cmd(["commit-msg", "pre-push"]) == expected_cmd - - @pytest.fixture(scope="function") def pre_commit_installed(mocker: MockFixture): # Assume the `pre-commit` is installed @@ -132,8 +126,8 @@ def pre_commit_installed(mocker: MockFixture): ) # And installation success (i.e. no exception raised) mocker.patch( - "commitizen.commands.init.Init._exec_install_pre_commit_hook", - return_value=None, + "commitizen.cmd.run", + return_value=cmd.Command("0.0.1", "", b"", b"", 0), ) @@ -244,23 +238,6 @@ def test_pre_commit_not_installed( with pytest.raises(InitFailedError): commands.Init(config)() - def test_pre_commit_exec_failed( - _, mocker: MockFixture, config: BaseConfig, default_choice: str, tmpdir - ): - # Assume `pre-commit` is installed - mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", - return_value=True, - ) - # But pre-commit installation will fail - mocker.patch( - "commitizen.commands.init.Init._exec_install_pre_commit_hook", - side_effect=InitFailedError("Mock init failed error."), - ) - with tmpdir.as_cwd(): - with pytest.raises(InitFailedError): - commands.Init(config)() - class TestAskTagFormat: def test_confirm_v_tag_format(self, mocker: MockFixture, config: BaseConfig): From 0a77488e240ab0742436a5cdbd800d8db4cba592 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 21 Sep 2025 00:43:07 +0800 Subject: [PATCH 204/275] test(changelog): remove unused timer fixture --- tests/commands/test_changelog_command.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index 1f3dabd761..f147c419b8 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -884,7 +884,6 @@ def test_changelog_with_filename_as_empty_string( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_first_version_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -918,7 +917,6 @@ def test_changelog_from_rev_first_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -954,7 +952,6 @@ def test_changelog_from_rev_latest_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") @pytest.mark.parametrize( "rev_range,tag", ( @@ -986,7 +983,6 @@ def test_changelog_from_rev_range_not_found( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_multiple_matching_tags( mocker: MockFixture, config_path, changelog_path ): @@ -1016,7 +1012,6 @@ def test_changelog_multiple_matching_tags( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_range_default_tag_format( mocker, config_path, changelog_path ): @@ -1047,7 +1042,6 @@ def test_changelog_from_rev_range_default_tag_format( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_including_first_tag( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1079,7 +1073,6 @@ def test_changelog_from_rev_version_range_including_first_tag( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1119,7 +1112,6 @@ def test_changelog_from_rev_version_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_with_legacy_tags( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1154,7 +1146,6 @@ def test_changelog_from_rev_version_range_with_legacy_tags( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_with_big_range_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1214,7 +1205,6 @@ def test_changelog_from_rev_version_with_big_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_dry_run( mocker: MockFixture, capsys, config_path, changelog_path, file_regression ): @@ -1267,7 +1257,6 @@ def test_invalid_subject_is_skipped(mocker: MockFixture, capsys): assert out == ("## Unreleased\n\n### Feat\n\n- a new world\n\n") -@pytest.mark.freeze_time("2022-02-13") @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_with_customized_change_type_order( mocker, config_path, changelog_path, file_regression @@ -1325,7 +1314,6 @@ def test_empty_commit_list(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_prerelease_rev_with_use_scheme_semver( mocker: MockFixture, capsys, config_path, changelog_path, file_regression ): From e34bc55f2d8fef6cbc21c6a28204f284cff2e7e9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 22 Sep 2025 17:20:51 +0800 Subject: [PATCH 205/275] refactor(cmd): unnest try except --- commitizen/cmd.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/commitizen/cmd.py b/commitizen/cmd.py index 3f13087233..c8d4f33010 100644 --- a/commitizen/cmd.py +++ b/commitizen/cmd.py @@ -22,13 +22,15 @@ def _try_decode(bytes_: bytes) -> str: try: return bytes_.decode("utf-8") except UnicodeDecodeError: - charset_match = from_bytes(bytes_).best() - if charset_match is None: - raise CharacterSetDecodeError() - try: - return bytes_.decode(charset_match.encoding) - except UnicodeDecodeError as e: - raise CharacterSetDecodeError() from e + pass + + charset_match = from_bytes(bytes_).best() + if charset_match is None: + raise CharacterSetDecodeError() + try: + return bytes_.decode(charset_match.encoding) + except UnicodeDecodeError as e: + raise CharacterSetDecodeError() from e def run(cmd: str, env: Mapping[str, str] | None = None) -> Command: From 293645f7d58fc89542dd61a3d9685a3ee80aa468 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 21 Sep 2025 11:40:37 +0800 Subject: [PATCH 206/275] refactor(ConventionalCommitsCz): rewrite message method to make the pattern more clear --- .../cz/conventional_commits/conventional_commits.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 6893423478..c827cebb78 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -155,15 +155,16 @@ def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[o is_breaking_change = answers["is_breaking_change"] if scope: - scope = f"({scope})" - if body: - body = f"\n\n{body}" + # example: "fix(users): email pattern corrected" + first_line = f"{prefix}({scope}): {subject}" + else: + # example: "fix: email pattern corrected" + first_line = f"{prefix}: {subject}" + if is_breaking_change: footer = f"BREAKING CHANGE: {footer}" - if footer: - footer = f"\n\n{footer}" - return f"{prefix}{scope}: {subject}{body}{footer}" + return "\n\n".join(s for s in (first_line, body, footer) if s) def example(self) -> str: return ( From 3fdd15f82eb9f467377dcb275ff3f473d4ad698b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 21 Sep 2025 00:24:12 +0800 Subject: [PATCH 207/275] refactor(Bump): remove use of getattr --- commitizen/commands/bump.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 07338afb86..fc9649c6c5 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -216,8 +216,8 @@ def __call__(self) -> None: rules = TagRules.from_settings(cast(Settings, self.bump_settings)) current_tag = rules.find_tag_for(git.get_tags(), current_version) - current_tag_version = getattr( - current_tag, "name", rules.normalize_tag(current_version) + current_tag_version = ( + current_tag.name if current_tag else rules.normalize_tag(current_version) ) is_initial = self._is_initial_tag(current_tag, self.arguments["yes"]) From 9c616c36402e692ec7ec2f39e25e2a02e997c6d5 Mon Sep 17 00:00:00 2001 From: Santiago Fraire Willemoes Date: Thu, 30 Oct 2025 07:47:06 +0000 Subject: [PATCH 208/275] docs: add contributing for nix users --- docs/contributing.md | 16 +++++++++++++++- docs/contributing_tldr.md | 13 ++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 8389e9370d..4a068cf640 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -38,7 +38,7 @@ If you're a first-time contributor, please check out issues labeled [good first ```bash git remote add upstream https://github.com/commitizen-tools/commitizen.git ``` -4. Set up the development environment: +4. Set up the development environment (nix users go to [nix section](#nix)): ```bash poetry install ``` @@ -70,6 +70,7 @@ If you're a first-time contributor, please check out issues labeled [good first 5. **Documentation** - Update `docs/README.md` if needed - For CLI help screenshots: `poetry doc:screenshots` + - Prefer [Google style documentation](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings), which works well with editors like VSCode and PyCharm - **DO NOT** update `CHANGELOG.md` (automatically generated) - **DO NOT** update version numbers (automatically handled) 6. **Pull Request** @@ -153,3 +154,16 @@ flowchart TD --modification-received--> review ``` + +## Nix + +If you have installed poetry globally, the project won't work because it requries `poethepoet` for command management. + +You'll have to install poetry locally. + +```sh +python -m venv .venv +. .venv/bin/activate +pip install -U pip && pip install poetry +poetry install +``` diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md index f5483cddcd..9eecc3efa0 100644 --- a/docs/contributing_tldr.md +++ b/docs/contributing_tldr.md @@ -12,7 +12,7 @@ Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen ### Code Changes ```bash -# Ensure you have the correct dependencies +# Ensure you have the correct dependencies, for nix user's see below poetry install # Make ruff happy @@ -35,3 +35,14 @@ pytest -n auto # Build the documentation locally and check for broken links poetry doc ``` + +### Nix Users + +If you are using Nix, you can install poetry locally by running: + +```sh +python -m venv .venv +. .venv/bin/activate +pip install -U pip && pip install poetry +poetry install +``` From 459d83a647904987c2bf1967541321208f07a9d0 Mon Sep 17 00:00:00 2001 From: Santiago Fraire Willemoes Date: Thu, 30 Oct 2025 08:28:48 +0000 Subject: [PATCH 209/275] feat(version): add the ability to just print major or minor version --- commitizen/cli.py | 12 +++ commitizen/commands/version.py | 16 +++- commitizen/version_schemes.py | 4 +- docs/contributing.md | 2 +- docs/images/cli_help/cz_version___help.svg | 82 ++++++++++--------- tests/commands/test_version_command.py | 37 +++++++++ ...shows_description_when_use_help_option.txt | 4 +- 7 files changed, 116 insertions(+), 41 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index ed4305ea1f..e9689d75f3 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -543,6 +543,18 @@ def __call__( "action": "store_true", "exclusive_group": "group1", }, + { + "name": ["--major"], + "help": "get just the major version", + "action": "store_true", + "exclusive_group": "group2", + }, + { + "name": ["--minor"], + "help": "get just the minor version", + "action": "store_true", + "exclusive_group": "group2", + }, ], }, ], diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 35e9aa6cd6..04fa664f25 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -5,14 +5,17 @@ from commitizen import out from commitizen.__version__ import __version__ from commitizen.config import BaseConfig -from commitizen.exceptions import NoVersionSpecifiedError +from commitizen.exceptions import NoVersionSpecifiedError, VersionSchemeUnknown from commitizen.providers import get_provider +from commitizen.version_schemes import get_version_scheme class VersionArgs(TypedDict, total=False): report: bool project: bool verbose: bool + major: bool + minor: bool class Version: @@ -41,6 +44,17 @@ def __call__(self) -> None: out.error("No project information in this project.") return + try: + version_scheme = get_version_scheme(self.config.settings) + except VersionSchemeUnknown: + out.error("Unknown version scheme.") + _version = version_scheme(version) + + if self.parameter.get("major"): + version = f"{_version.major}" + elif self.parameter.get("minor"): + version = f"{_version.minor}" + out.write(f"Project Version: {version}" if verbose else version) return diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index e9f99c5514..0696d85aa5 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -410,7 +410,9 @@ def _get_prerelease(self) -> str: def get_version_scheme(settings: Settings, name: str | None = None) -> VersionScheme: """ Get the version scheme as defined in the configuration - or from an overridden `name` + or from an overridden `name`. + + :raises VersionSchemeUnknown: if the version scheme is not found. """ diff --git a/docs/contributing.md b/docs/contributing.md index 4a068cf640..16db930626 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -157,7 +157,7 @@ flowchart TD ## Nix -If you have installed poetry globally, the project won't work because it requries `poethepoet` for command management. +If you have installed poetry globally, the project won't work because it requires `poethepoet` for command management. You'll have to install poetry locally. diff --git a/docs/images/cli_help/cz_version___help.svg b/docs/images/cli_help/cz_version___help.svg index c7777db4df..7ddec177d8 100644 --- a/docs/images/cli_help/cz_version___help.svg +++ b/docs/images/cli_help/cz_version___help.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - - $ cz version --help -usage: cz version [-h][-r | -p | -c | -v] - -get the version of the installed commitizen or the current project (default: -installed commitizen) - -options: -  -h, --help        show this help message and exit -  -r, --report      get system information for reporting bugs -  -p, --project     get the version of the current project -  -c, --commitizen  get the version of the installed commitizen -  -v, --verbose     get the version of both the installed commitizen and the -                    current project - + + $ cz version --help +usage: cz version [-h][-r | -p | -c | -v][--major | --minor] + +get the version of the installed commitizen or the current project (default: +installed commitizen) + +options: +  -h, --help        show this help message and exit +  -r, --report      get system information for reporting bugs +  -p, --project     get the version of the current project +  -c, --commitizen  get the version of the installed commitizen +  -v, --verbose     get the version of both the installed commitizen and the +                    current project +  --major           get just the major version +  --minor           get just the minor version + diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 3dcbed168b..cd2e7f77e0 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -119,3 +119,40 @@ def test_version_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +@pytest.mark.parametrize( + "version, expected_version", (("1.0.0", "1\n"), ("2.1.3", "2\n"), ("0.0.1", "0\n")) +) +def test_version_just_major(config, capsys, version: str, expected_version: str): + config.settings["version"] = version + commands.Version( + config, + { + "report": False, + "project": True, + "verbose": False, + "major": True, + }, + )() + captured = capsys.readouterr() + assert expected_version == captured.out + + +@pytest.mark.parametrize( + "version, expected_version", + (("1.0.0", "0\n"), ("2.1.3", "1\n"), ("0.0.1", "0\n"), ("0.1.0", "1\n")), +) +def test_version_just_minor(config, capsys, version: str, expected_version: str): + config.settings["version"] = version + commands.Version( + config, + { + "report": False, + "project": True, + "verbose": False, + "minor": True, + }, + )() + captured = capsys.readouterr() + assert expected_version == captured.out diff --git a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt index c461b10bcd..b1ed94124e 100644 --- a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt @@ -1,4 +1,4 @@ -usage: cz version [-h] [-r | -p | -c | -v] +usage: cz version [-h] [-r | -p | -c | -v] [--major | --minor] get the version of the installed commitizen or the current project (default: installed commitizen) @@ -10,3 +10,5 @@ options: -c, --commitizen get the version of the installed commitizen -v, --verbose get the version of both the installed commitizen and the current project + --major get just the major version + --minor get just the minor version From 056a02608babdf7bd849039545c5ef628f906b8e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 20 Sep 2025 01:17:54 +0800 Subject: [PATCH 210/275] refactor: remove unnecessary class member tag_format --- commitizen/changelog_formats/base.py | 3 +-- commitizen/commands/changelog.py | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index cb5d385bf8..0a76ad68f9 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -25,10 +25,9 @@ def __init__(self, config: BaseConfig) -> None: # See: https://bugs.python.org/issue44807 self.config = config self.encoding = self.config.settings["encoding"] - self.tag_format = self.config.settings["tag_format"] self.tag_rules = TagRules( scheme=get_version_scheme(self.config.settings), - tag_format=self.tag_format, + tag_format=self.config.settings["tag_format"], legacy_tag_formats=self.config.settings["legacy_tag_formats"], ignored_tag_formats=self.config.settings["ignored_tag_formats"], ) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 0fffd8ed7c..c739419b8d 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -104,12 +104,10 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: or defaults.CHANGE_TYPE_ORDER, ) self.rev_range = arguments.get("rev_range") - self.tag_format = ( - arguments.get("tag_format") or self.config.settings["tag_format"] - ) self.tag_rules = TagRules( scheme=self.scheme, - tag_format=self.tag_format, + tag_format=arguments.get("tag_format") + or self.config.settings["tag_format"], legacy_tag_formats=self.config.settings["legacy_tag_formats"], ignored_tag_formats=self.config.settings["ignored_tag_formats"], merge_prereleases=arguments.get("merge_prerelease") From 71eba5a0d5222ff518eece792b771a415a15e30e Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 29 Sep 2025 11:42:31 +0800 Subject: [PATCH 211/275] refactor(utils): make get_backup_file_path to return a path for semantic correctness --- commitizen/commands/commit.py | 28 +++++++++++++-------------- commitizen/cz/utils.py | 6 +++--- hooks/post-commit.py | 7 +++---- hooks/prepare-commit-msg.py | 9 ++++----- tests/commands/test_commit_command.py | 8 ++++---- 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 19bb72fb00..0a6349303c 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -51,15 +51,15 @@ def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None: self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) self.arguments = arguments - self.temp_file: str = get_backup_file_path() + self.backup_file_path = get_backup_file_path() def _read_backup_message(self) -> str | None: # Check the commit backup file exists - if not os.path.isfile(self.temp_file): + if not self.backup_file_path.is_file(): return None # Read commit message from backup - with open(self.temp_file, encoding=self.encoding) as f: + with open(self.backup_file_path, encoding=self.encoding) as f: return f.read().strip() def _prompt_commit_questions(self) -> str: @@ -108,10 +108,10 @@ def manual_edit(self, message: str) -> str: def _get_message(self) -> str: if self.arguments.get("retry"): - m = self._read_backup_message() - if m is None: + commit_message = self._read_backup_message() + if commit_message is None: raise NoCommitBackupError() - return m + return commit_message if self.config.settings.get("retry_after_failure") and not self.arguments.get( "no_retry" @@ -139,15 +139,15 @@ def __call__(self) -> None: if write_message_to_file is not None and write_message_to_file.is_dir(): raise NotAllowed(f"{write_message_to_file} is a directory") - m = self._get_message() + commit_message = self._get_message() if self.arguments.get("edit"): - m = self.manual_edit(m) + commit_message = self.manual_edit(commit_message) - out.info(f"\n{m}\n") + out.info(f"\n{commit_message}\n") if write_message_to_file: with smart_open(write_message_to_file, "w", encoding=self.encoding) as file: - file.write(m) + file.write(commit_message) if dry_run: raise DryRunExit() @@ -155,13 +155,13 @@ def __call__(self) -> None: if self.config.settings["always_signoff"] or signoff: extra_args = f"{extra_args} -s".strip() - c = git.commit(m, args=extra_args) + c = git.commit(commit_message, args=extra_args) if c.return_code != 0: out.error(c.err) # Create commit backup - with smart_open(self.temp_file, "w", encoding=self.encoding) as f: - f.write(m) + with smart_open(self.backup_file_path, "w", encoding=self.encoding) as f: + f.write(commit_message) raise CommitError() @@ -170,7 +170,7 @@ def __call__(self) -> None: return with contextlib.suppress(FileNotFoundError): - os.remove(self.temp_file) + self.backup_file_path.unlink() out.write(c.err) out.write(c.out) out.success("Commit successful!") diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index a6f687226c..ba5eace44a 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -1,6 +1,7 @@ import os import re import tempfile +from pathlib import Path from commitizen import git from commitizen.cz import exceptions @@ -22,10 +23,9 @@ def strip_local_version(version: str) -> str: return _RE_LOCAL_VERSION.sub("", version) -def get_backup_file_path() -> str: +def get_backup_file_path() -> Path: project_root = git.find_git_project_root() project = project_root.as_posix().replace("/", "%") if project_root else "" user = os.environ.get("USER", "") - - return os.path.join(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup") + return Path(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup") diff --git a/hooks/post-commit.py b/hooks/post-commit.py index c7dea825bd..d242b6b354 100755 --- a/hooks/post-commit.py +++ b/hooks/post-commit.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from pathlib import Path try: from commitizen.cz.utils import get_backup_file_path @@ -9,11 +8,11 @@ def post_commit() -> None: - backup_file = Path(get_backup_file_path()) + backup_file_path = get_backup_file_path() # remove backup file if it exists - if backup_file.is_file(): - backup_file.unlink() + if backup_file_path.is_file(): + backup_file_path.unlink() if __name__ == "__main__": diff --git a/hooks/prepare-commit-msg.py b/hooks/prepare-commit-msg.py index e666fa673b..a100f13afa 100755 --- a/hooks/prepare-commit-msg.py +++ b/hooks/prepare-commit-msg.py @@ -2,7 +2,6 @@ import shutil import subprocess import sys -from pathlib import Path from subprocess import CalledProcessError try: @@ -25,12 +24,12 @@ def prepare_commit_msg(commit_msg_file: str) -> int: capture_output=True, ).returncode if exit_code != 0: - backup_file = Path(get_backup_file_path()) - if backup_file.is_file(): + backup_file_path = get_backup_file_path() + if backup_file_path.is_file(): # confirm if commit message from backup file should be reused answer = input("retry with previous message? [y/N]: ") if answer.lower() == "y": - shutil.copyfile(backup_file, commit_msg_file) + shutil.copyfile(backup_file_path, commit_msg_file) return 0 # use commitizen to generate the commit message @@ -50,7 +49,7 @@ def prepare_commit_msg(commit_msg_file: str) -> int: return error.returncode # write message to backup file - shutil.copyfile(commit_msg_file, backup_file) + shutil.copyfile(commit_msg_file, backup_file_path) return 0 diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 930e1a7a9b..8d0181f3e0 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -73,7 +73,7 @@ def test_commit_backup_on_failure(config, mocker: MockFixture): with pytest.raises(CommitError): commit_cmd = commands.Commit(config, {}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() prompt_mock.assert_called_once() @@ -101,7 +101,7 @@ def test_commit_retry_works(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") commit_cmd = commands.Commit(config, {"retry": True}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("backup commit", args="") @@ -144,7 +144,7 @@ def test_commit_retry_after_failure_works(config, mocker: MockFixture): config.settings["retry_after_failure"] = True commit_cmd = commands.Commit(config, {}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("backup commit", args="") @@ -171,7 +171,7 @@ def test_commit_retry_after_failure_with_no_retry_works(config, mocker: MockFixt config.settings["retry_after_failure"] = True commit_cmd = commands.Commit(config, {"no_retry": True}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="") From 41835eacb07305ecf16df30bcdc099d22bd0371d Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 20 Sep 2025 01:02:02 +0800 Subject: [PATCH 212/275] docs(Check): add missing raise exception in __call__ --- commitizen/commands/check.py | 1 + 1 file changed, 1 insertion(+) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index e6ebc928ed..b79e6609e0 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -79,6 +79,7 @@ def __call__(self) -> None: Raises: InvalidCommitMessageError: if the commit provided not follows the conventional pattern + NoCommitsFoundError: if no commit is found with the given range """ commits = self._get_commits() if not commits: From 183301a65deb11f005ffdf6d1d6ce3165cd55712 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 13 Sep 2025 17:25:15 +0800 Subject: [PATCH 213/275] test(SemVer): refine tests and remove unnecessary tests which test the behavior of library, improve readability with NamedTuple --- tests/test_version_scheme_pep440.py | 1629 +++++++++++++++++++++----- tests/test_version_scheme_semver.py | 1084 +++++++++++++---- tests/test_version_scheme_semver2.py | 824 ++++++++++--- tests/utils.py | 10 + 4 files changed, 2831 insertions(+), 716 deletions(-) diff --git a/tests/test_version_scheme_pep440.py b/tests/test_version_scheme_pep440.py index a983dad14a..0ce4f81545 100644 --- a/tests/test_version_scheme_pep440.py +++ b/tests/test_version_scheme_pep440.py @@ -1,258 +1,1356 @@ -import itertools -import random - import pytest from commitizen.version_schemes import Pep440, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1.dev1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0.dev1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1a0"), - (("0.3.1a0", None, "alpha", 0, None), "0.3.1a1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1a1"), - (("0.3.1a0", None, "alpha", 1, None), "0.3.1a1"), - (("0.3.1a0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0a0"), - (("1.0.0a0", None, "alpha", 0, None), "1.0.0a1"), - (("1.0.0a1", None, "alpha", 0, None), "1.0.0a2"), - (("1.0.0a1", None, "alpha", 0, 1), "1.0.0a2.dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 1), "1.0.0a3.dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 0), "1.0.0a3.dev0"), - (("1.0.0a1", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0b0", None, "beta", 0, None), "1.0.0b1"), - (("1.0.0b1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc0", None, "rc", 0, None), "1.0.0rc1"), - (("1.0.0rc0", None, "rc", 0, 1), "1.0.0rc1.dev1"), - (("1.0.0rc0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0a3.dev0", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1b1", None, "alpha", 0, None), "0.1.1b2"), - (("0.1.1rc0", None, "alpha", 0, None), "0.1.1rc1"), - (("0.1.1rc0", None, "beta", 0, None), "0.1.1rc1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1a0", None, "alpha", 0, None), "1.0.0a1"), - (("1a0", None, "alpha", 1, None), "1.0.0a1"), - (("1", None, "beta", 0, None), "1.0.0b0"), - (("1", None, "beta", 1, None), "1.0.0b1"), - (("1beta", None, "beta", 0, None), "1.0.0b1"), - (("1.0.0alpha1", None, "alpha", 0, None), "1.0.0a2"), - (("1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1a0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0a0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0a0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0a1"), - (("1.0.0a2", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0a2", None, "beta", 1, None), "1.0.0b1"), - (("1.0.0beta1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc1", None, "rc", 0, None), "1.0.0rc2"), -] - -# additional pre-release tests run through various release scenarios -prerelease_cases = [ - # - (("3.3.3", "PATCH", "alpha", 0, None), "3.3.4a0"), - (("3.3.4a0", "PATCH", "alpha", 0, None), "3.3.4a1"), - (("3.3.4a1", "MINOR", "alpha", 0, None), "3.4.0a0"), - (("3.4.0a0", "PATCH", "alpha", 0, None), "3.4.0a1"), - (("3.4.0a1", "MINOR", "alpha", 0, None), "3.4.0a2"), - (("3.4.0a2", "MAJOR", "alpha", 0, None), "4.0.0a0"), - (("4.0.0a0", "PATCH", "alpha", 0, None), "4.0.0a1"), - (("4.0.0a1", "MINOR", "alpha", 0, None), "4.0.0a2"), - (("4.0.0a2", "MAJOR", "alpha", 0, None), "4.0.0a3"), - # - (("1.0.0", "PATCH", "alpha", 0, None), "1.0.1a0"), - (("1.0.1a0", "PATCH", "alpha", 0, None), "1.0.1a1"), - (("1.0.1a1", "MINOR", "alpha", 0, None), "1.1.0a0"), - (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"), - (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"), - (("1.1.0a2", "MAJOR", "alpha", 0, None), "2.0.0a0"), - # - (("1.0.0", "MINOR", "alpha", 0, None), "1.1.0a0"), - (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"), - (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"), - (("1.1.0a2", "PATCH", "alpha", 0, None), "1.1.0a3"), - (("1.1.0a3", "MAJOR", "alpha", 0, None), "2.0.0a0"), - # - (("1.0.0", "MAJOR", "alpha", 0, None), "2.0.0a0"), - (("2.0.0a0", "MINOR", "alpha", 0, None), "2.0.0a1"), - (("2.0.0a1", "PATCH", "alpha", 0, None), "2.0.0a2"), - (("2.0.0a2", "MAJOR", "alpha", 0, None), "2.0.0a3"), - (("2.0.0a3", "MINOR", "alpha", 0, None), "2.0.0a4"), - (("2.0.0a4", "PATCH", "alpha", 0, None), "2.0.0a5"), - (("2.0.0a5", "MAJOR", "alpha", 0, None), "2.0.0a6"), - # - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.0.0b1"), - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.0b1"), - # - (("1.0.1a0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1a0", "MINOR", None, 0, None), "1.1.0"), - (("1.0.1a0", "MAJOR", None, 0, None), "2.0.0"), - # - (("1.1.0a0", "PATCH", None, 0, None), "1.1.0"), - (("1.1.0a0", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0a0", "MAJOR", None, 0, None), "2.0.0"), - # - (("2.0.0a0", "MINOR", None, 0, None), "2.0.0"), - (("2.0.0a0", "MAJOR", None, 0, None), "2.0.0"), - (("2.0.0a0", "PATCH", None, 0, None), "2.0.0"), - # - (("3.0.0a1", None, None, 0, None), "3.0.0"), - (("3.0.0b1", None, None, 0, None), "3.0.0"), - (("3.0.0rc1", None, None, 0, None), "3.0.0"), - # - (("3.1.4", None, "alpha", 0, None), "3.1.4a0"), - (("3.1.4", None, "beta", 0, None), "3.1.4b0"), - (("3.1.4", None, "rc", 0, None), "3.1.4rc0"), - # - (("3.1.4", None, "alpha", 0, None), "3.1.4a0"), - (("3.1.4a0", "PATCH", "alpha", 0, None), "3.1.4a1"), # UNEXPECTED! - (("3.1.4a0", "MINOR", "alpha", 0, None), "3.2.0a0"), - (("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"), -] - -exact_cases = [ - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.0", "MINOR", None, 0, None), "1.1.0"), - # with exact_increment=False: "1.0.0b0" - (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1b0"), - # with exact_increment=False: "1.0.0rc0" - (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1rc0"), - # with exact_increment=False: "1.0.0rc1-dev1" - (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1rc0.dev1"), - # with exact_increment=False: "1.0.0b0" - (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "MINOR", "alpha", 0, None), "1.1.0a0"), - # with exact_increment=False: "1.0.0rc0" - (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0rc0"), - # with exact_increment=False: "1.0.0rc1" - (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0rc0"), - # with exact_increment=False: "1.0.0rc1-dev1" - (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0rc0.dev1"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"), - # same with exact_increment=False - (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1a0"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input,expected", - itertools.chain( - tdd_cases, - weird_cases, - simple_flow, - linear_prerelease_cases, - prerelease_cases, - ), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0beta1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc2", + ), + # weird cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0a2.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0a3.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0a3.dev0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0rc1.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a3.dev0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases + ( + VersionSchemeTestArgs( + current_version="0.1.1b1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1b2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1rc1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1rc1", + ), + # prerelease cases + ( + VersionSchemeTestArgs( + current_version="3.3.3", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.3.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.3.4a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.3.4a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.3.4a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a3", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a2", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a3", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a3", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a1", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a3", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a3", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a4", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a4", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a5", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a5", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a6", + ), + # + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0b1", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.0.0a1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="3.0.0b1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="3.0.0rc1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4b0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4rc0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.2.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a0", + ), + ], ) -def test_bump_pep440_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_pep440_version(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input, expected", exact_cases) -def test_bump_pep440_version_force(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + # with exact_increment=False: "1.0.0b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1b0", + ), + # with exact_increment=False: "1.0.0rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1rc0", + ), + # with exact_increment=False: "1.0.0rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.1rc0.dev1", + ), + # with exact_increment=False: "1.0.0b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + # with exact_increment=False: "1.0.0rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0rc0", + ), + # with exact_increment=False: "1.0.0rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0rc0", + ), + # with exact_increment=False: "1.0.0rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.1.0rc0.dev1", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.1.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.1", + ), + # same with exact_increment=False + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.0.0a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.1.0a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.1a0", + ), + ], +) +def test_bump_pep440_version_force(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, exact_increment=True, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_pep440_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_pep440_version_local(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -263,76 +1361,3 @@ def test_pep440_scheme_property(): def test_pep440_implement_version_protocol(): assert isinstance(Pep440("0.0.1"), VersionProtocol) - - -def test_pep440_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [Pep440(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1.dev1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0.dev1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a1", - "0.3.1a1", - "0.3.1a1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0a0", - "1.0.0a0", - "1.0.0a1", - "1.0.0a1", - "1.0.0a1", - "1.0.0a1", - "1.0.0a2.dev0", - "1.0.0a2.dev0", - "1.0.0a2.dev1", - "1.0.0a2", - "1.0.0a3.dev0", - "1.0.0a3.dev0", - "1.0.0a3.dev1", - "1.0.0b0", - "1.0.0b0", - "1.0.0b0", - "1.0.0b1", - "1.0.0b1", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc1.dev1", - "1.0.0rc1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/test_version_scheme_semver.py b/tests/test_version_scheme_semver.py index 8785717a34..8a163d4f6b 100644 --- a/tests/test_version_scheme_semver.py +++ b/tests/test_version_scheme_semver.py @@ -1,189 +1,882 @@ -import itertools -import random +from __future__ import annotations import pytest from commitizen.version_schemes import SemVer, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-a0"), - (("0.3.1a0", None, "alpha", 0, None), "0.3.1-a1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-a1"), - (("0.3.1a0", None, "alpha", 1, None), "0.3.1-a1"), - (("0.3.1a0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-a0"), - (("1.0.0a0", None, "alpha", 0, None), "1.0.0-a1"), - (("1.0.0a1", None, "alpha", 0, None), "1.0.0-a2"), - (("1.0.0a1", None, "alpha", 0, 1), "1.0.0-a2-dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 1), "1.0.0-a3-dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 0), "1.0.0-a3-dev0"), - (("1.0.0a1", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0b0", None, "beta", 0, None), "1.0.0-b1"), - (("1.0.0b1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc0", None, "rc", 0, None), "1.0.0-rc1"), - (("1.0.0rc0", None, "rc", 0, 1), "1.0.0-rc1-dev1"), - (("1.0.0rc0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0a3.dev0", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1b1", None, "alpha", 0, None), "0.1.1-b2"), - (("0.1.1rc0", None, "alpha", 0, None), "0.1.1-rc1"), - (("0.1.1rc0", None, "beta", 0, None), "0.1.1-rc1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1a0", None, "alpha", 0, None), "1.0.0-a1"), - (("1a0", None, "alpha", 1, None), "1.0.0-a1"), - (("1", None, "beta", 0, None), "1.0.0-b0"), - (("1", None, "beta", 1, None), "1.0.0-b1"), - (("1beta", None, "beta", 0, None), "1.0.0-b1"), - (("1.0.0alpha1", None, "alpha", 0, None), "1.0.0-a2"), - (("1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-a0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-a0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-a0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-a1"), - (("1.0.0a2", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0a2", None, "beta", 1, None), "1.0.0-b1"), - (("1.0.0beta1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc1", None, "rc", 0, None), "1.0.0-rc2"), - (("1.0.0-a0", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"), -] - -exact_cases = [ - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.0", "MINOR", None, 0, None), "1.1.0"), - # with exact_increment=False: "1.0.0-b0" - (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1-b0"), - # with exact_increment=False: "1.0.0-b1" - (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1-b0"), - # with exact_increment=False: "1.0.0-rc0" - (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1-rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1-rc0"), - # with exact_increment=False: "1.0.0-rc1-dev1" - (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1-rc0-dev1"), - # with exact_increment=False: "1.0.0-b0" - (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0-b0"), - # with exact_increment=False: "1.0.0-b1" - (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0-b0"), - # with exact_increment=False: "1.0.0-rc0" - (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0-rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0-rc0"), - # with exact_increment=False: "1.0.0-rc1-dev1" - (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0-rc0-dev1"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"), - # same with exact_increment=False - (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0-a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0-a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1-a0"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input, expected", - itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0beta1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-a0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + # weird cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-a2-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-a3-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0-a3-dev0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-rc1-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a3.dev0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases (never bump backwards on pre-releases) + ( + VersionSchemeTestArgs( + current_version="0.1.1b1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-b2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc1", + ), + ], ) -def test_bump_semver_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_semver_version( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input, expected", exact_cases) -def test_bump_semver_version_force(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + # with exact_increment=False: "1.0.0-b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-b0", + ), + # with exact_increment=False: "1.0.0-b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-b0", + ), + # with exact_increment=False: "1.0.0-rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-rc0", + ), + # with exact_increment=False: "1.0.0-rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.1-rc0-dev1", + ), + # with exact_increment=False: "1.0.0-b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-b0", + ), + # with exact_increment=False: "1.0.0-b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-b0", + ), + # with exact_increment=False: "1.0.0-rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-rc0", + ), + # with exact_increment=False: "1.0.0-rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.1.0-rc0-dev1", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.1.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.1", + ), + # same with exact_increment=False + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.0.0-a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.1.0-a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.1-a0", + ), + ], +) +def test_bump_semver_version_force( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, exact_increment=True, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_semver_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_semver_version_local( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -194,76 +887,3 @@ def test_semver_scheme_property(): def test_semver_implement_version_protocol(): assert isinstance(SemVer("0.0.1"), VersionProtocol) - - -def test_semver_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [SemVer(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1-dev1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0-dev1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a1", - "0.3.1-a1", - "0.3.1-a1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0-a0", - "1.0.0-a0", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a2-dev0", - "1.0.0-a2-dev0", - "1.0.0-a2-dev1", - "1.0.0-a2", - "1.0.0-a3-dev0", - "1.0.0-a3-dev0", - "1.0.0-a3-dev1", - "1.0.0-b0", - "1.0.0-b0", - "1.0.0-b0", - "1.0.0-b1", - "1.0.0-b1", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc1-dev1", - "1.0.0-rc1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/test_version_scheme_semver2.py b/tests/test_version_scheme_semver2.py index d18a058a7c..4a35e6470a 100644 --- a/tests/test_version_scheme_semver2.py +++ b/tests/test_version_scheme_semver2.py @@ -1,131 +1,664 @@ -import itertools -import random +from __future__ import annotations import pytest from commitizen.version_schemes import SemVer2, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev.1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev.1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-alpha.0"), - (("0.3.1-alpha.0", None, "alpha", 0, None), "0.3.1-alpha.1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-alpha.1"), - (("0.3.1-alpha.0", None, "alpha", 1, None), "0.3.1-alpha.1"), - (("0.3.1-alpha.0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"), - (("1.0.0-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), - (("1.0.0-alpha.1", None, "alpha", 0, 1), "1.0.0-alpha.2.dev.1"), - (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 1), "1.0.0-alpha.3.dev.1"), - (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 0), "1.0.0-alpha.3.dev.0"), - (("1.0.0-alpha.1", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0-beta.0", None, "beta", 0, None), "1.0.0-beta.1"), - (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.0", None, "rc", 0, None), "1.0.0-rc.1"), - (("1.0.0-rc.0", None, "rc", 0, 1), "1.0.0-rc.1.dev.1"), - (("1.0.0-rc.0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0-alpha.3.dev.0", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1-beta.1", None, "alpha", 0, None), "0.1.1-beta.2"), - (("0.1.1-rc.0", None, "alpha", 0, None), "0.1.1-rc.1"), - (("0.1.1-rc.0", None, "beta", 0, None), "0.1.1-rc.1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"), - (("1-alpha.0", None, "alpha", 1, None), "1.0.0-alpha.1"), - (("1", None, "beta", 0, None), "1.0.0-beta.0"), - (("1", None, "beta", 1, None), "1.0.0-beta.1"), - (("1-beta", None, "beta", 0, None), "1.0.0-beta.1"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), - (("1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-alpha.0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-alpha.0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-alpha.1"), - (("1.0.0-alpha.2", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0-alpha.2", None, "beta", 1, None), "1.0.0-beta.1"), - (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.1", None, "rc", 0, None), "1.0.0-rc.2"), - (("1.0.0-alpha.0", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input, expected", - itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + # weird_cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1-beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple_flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1-dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0-dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-alpha.2.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2.dev.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-alpha.3.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2.dev.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0-alpha.3.dev.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-rc.1.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.3.dev.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases (never bump backwards on pre-releases) + ( + VersionSchemeTestArgs( + current_version="0.1.1-beta.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-beta.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1-rc.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1-rc.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc.1", + ), + ], ) -def test_bump_semver_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_semver_version( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer2(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer2(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_semver_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_semver_version_local( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer2(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + SemVer2(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -136,76 +669,3 @@ def test_semver_scheme_property(): def test_semver_implement_version_protocol(): assert isinstance(SemVer2("0.0.1"), VersionProtocol) - - -def test_semver_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [SemVer2(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1-dev.1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0-dev.1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.1", - "0.3.1-alpha.1", - "0.3.1-alpha.1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0-alpha.0", - "1.0.0-alpha.0", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.2.dev.0", - "1.0.0-alpha.2.dev.0", - "1.0.0-alpha.2.dev.1", - "1.0.0-alpha.2", - "1.0.0-alpha.3.dev.0", - "1.0.0-alpha.3.dev.0", - "1.0.0-alpha.3.dev.1", - "1.0.0-beta.0", - "1.0.0-beta.0", - "1.0.0-beta.0", - "1.0.0-beta.1", - "1.0.0-beta.1", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.1.dev.1", - "1.0.0-rc.1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/utils.py b/tests/utils.py index 5e26b2d70a..43e0cf79a0 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,11 +4,13 @@ import time import uuid from pathlib import Path +from typing import NamedTuple import pytest from deprecated import deprecated from commitizen import cmd, exceptions, git +from commitizen.version_schemes import Increment, Prerelease skip_below_py_3_10 = pytest.mark.skipif( sys.version_info < (3, 10), @@ -21,6 +23,14 @@ ) +class VersionSchemeTestArgs(NamedTuple): + current_version: str + increment: Increment | None + prerelease: Prerelease | None + prerelease_offset: int + devrelease: int | None + + class FakeCommand: def __init__(self, out=None, err=None, return_code=0): self.out = out From cbb309d8573ffcb659284b584f0dad328731b5a8 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 20 Sep 2025 01:29:52 +0800 Subject: [PATCH 214/275] refactor: remove self.encoding for better maintainability --- commitizen/changelog_formats/base.py | 5 +++-- commitizen/commands/bump.py | 3 +-- commitizen/commands/changelog.py | 9 ++++++--- commitizen/commands/check.py | 5 +++-- commitizen/commands/commit.py | 13 +++++++++---- commitizen/commands/init.py | 9 ++++++--- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index 0a76ad68f9..64a795207a 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -24,7 +24,6 @@ def __init__(self, config: BaseConfig) -> None: # Constructor needs to be redefined because `Protocol` prevent instantiation by default # See: https://bugs.python.org/issue44807 self.config = config - self.encoding = self.config.settings["encoding"] self.tag_rules = TagRules( scheme=get_version_scheme(self.config.settings), tag_format=self.config.settings["tag_format"], @@ -36,7 +35,9 @@ def get_metadata(self, filepath: str) -> Metadata: if not os.path.isfile(filepath): return Metadata() - with open(filepath, encoding=self.encoding) as changelog_file: + with open( + filepath, encoding=self.config.settings["encoding"] + ) as changelog_file: return self.get_metadata_from_file(changelog_file) def get_metadata_from_file(self, file: IO[Any]) -> Metadata: diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index fc9649c6c5..3dc678920e 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -67,7 +67,6 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: raise NotAGitProjectError() self.config: BaseConfig = config - self.encoding = config.settings["encoding"] self.arguments = arguments self.bump_settings = cast( BumpArgs, @@ -335,7 +334,7 @@ def __call__(self) -> None: str(new_version), self.bump_settings["version_files"], check_consistency=self.check_consistency, - encoding=self.encoding, + encoding=self.config.settings["encoding"], ) ) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index c739419b8d..27b8ccb258 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -67,7 +67,6 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: else changelog_file_name ) - self.encoding = self.config.settings["encoding"] self.cz = factory.committer_factory(self.config) self.start_rev = arguments.get("start_rev") or self.config.settings.get( @@ -157,7 +156,9 @@ def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> def _write_changelog( self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata ) -> None: - with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file: + with smart_open( + self.file_name, "w", encoding=self.config.settings["encoding"] + ) as changelog_file: partial_changelog: str | None = None if self.incremental: new_lines = changelog.incremental_build( @@ -259,7 +260,9 @@ def __call__(self) -> None: lines = [] if self.incremental and os.path.isfile(self.file_name): - with open(self.file_name, encoding=self.encoding) as changelog_file: + with open( + self.file_name, encoding=self.config.settings["encoding"] + ) as changelog_file: lines = changelog_file.readlines() self._write_changelog(changelog_out, lines, changelog_meta) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index b79e6609e0..d45e388f9d 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -71,7 +71,6 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N self.commit_msg = sys.stdin.read() self.config: BaseConfig = config - self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) def __call__(self) -> None: @@ -106,7 +105,9 @@ def _get_commit_message(self) -> str | None: # Get commit message from command line (--message) return self.commit_msg - with open(self.commit_msg_file, encoding=self.encoding) as commit_file: + with open( + self.commit_msg_file, encoding=self.config.settings["encoding"] + ) as commit_file: # Get commit message from file (--commit-msg-file) return commit_file.read() diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 0a6349303c..8ab8d2cac9 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -48,7 +48,6 @@ def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None: raise NotAGitProjectError() self.config: BaseConfig = config - self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) self.arguments = arguments self.backup_file_path = get_backup_file_path() @@ -59,7 +58,9 @@ def _read_backup_message(self) -> str | None: return None # Read commit message from backup - with open(self.backup_file_path, encoding=self.encoding) as f: + with open( + self.backup_file_path, encoding=self.config.settings["encoding"] + ) as f: return f.read().strip() def _prompt_commit_questions(self) -> str: @@ -146,7 +147,9 @@ def __call__(self) -> None: out.info(f"\n{commit_message}\n") if write_message_to_file: - with smart_open(write_message_to_file, "w", encoding=self.encoding) as file: + with smart_open( + write_message_to_file, "w", encoding=self.config.settings["encoding"] + ) as file: file.write(commit_message) if dry_run: @@ -160,7 +163,9 @@ def __call__(self) -> None: out.error(c.err) # Create commit backup - with smart_open(self.backup_file_path, "w", encoding=self.encoding) as f: + with smart_open( + self.backup_file_path, "w", encoding=self.config.settings["encoding"] + ) as f: f.write(commit_message) raise CommitError() diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 92e7d06d7d..35e9c20415 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -121,7 +121,6 @@ class Init: def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - self.encoding = config.settings["encoding"] self.cz = factory.committer_factory(self.config) self.project_info = ProjectInfo() @@ -163,7 +162,9 @@ def __call__(self) -> None: if hook_types: config_data = self._get_config_data() with smart_open( - self._PRE_COMMIT_CONFIG_PATH, "w", encoding=self.encoding + self._PRE_COMMIT_CONFIG_PATH, + "w", + encoding=self.config.settings["encoding"], ) as config_file: yaml.safe_dump(config_data, stream=config_file) @@ -355,7 +356,9 @@ def _get_config_data(self) -> dict[str, Any]: # .pre-commit-config.yaml does not exist return {"repos": [CZ_HOOK_CONFIG]} - with open(self._PRE_COMMIT_CONFIG_PATH, encoding=self.encoding) as config_file: + with open( + self._PRE_COMMIT_CONFIG_PATH, encoding=self.config.settings["encoding"] + ) as config_file: config_data: dict[str, Any] = yaml.safe_load(config_file) or {} if not isinstance(repos := config_data.get("repos"), list): From 9f36d5ebfa72616b3caf765efde16787509ed085 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 14 Sep 2025 23:38:03 +0800 Subject: [PATCH 215/275] refactor(BaseConfig): update docstring, extract factory method and remove unnecessary variable assignment --- commitizen/commands/init.py | 71 ++++++++++++++++++----------- commitizen/config/__init__.py | 58 ++++++++++------------- commitizen/config/base_config.py | 8 +++- commitizen/config/factory.py | 22 +++++++++ commitizen/config/json_config.py | 9 ++-- commitizen/config/toml_config.py | 9 ++-- commitizen/config/yaml_config.py | 11 +++-- tests/commands/conftest.py | 3 +- tests/commands/test_init_command.py | 3 +- tests/test_conf.py | 21 +++++---- tests/test_cz_customize.py | 27 ++++++----- tests/test_cz_search_filter.py | 2 +- 12 files changed, 146 insertions(+), 98 deletions(-) create mode 100644 commitizen/config/factory.py diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 35e9c20415..637caa2841 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -2,6 +2,7 @@ import os import shutil +from pathlib import Path from typing import Any, NamedTuple import questionary @@ -9,7 +10,10 @@ from commitizen import cmd, factory, out from commitizen.__version__ import __version__ -from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig +from commitizen.config import ( + BaseConfig, +) +from commitizen.config.factory import create_config from commitizen.cz import registry from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS from commitizen.exceptions import InitFailedError, NoAnswersError @@ -188,45 +192,33 @@ def __call__(self) -> None: ) out.write("commitizen pre-commit hook is now installed in your '.git'\n") - # Initialize configuration - if "toml" in config_path: - self.config = TomlConfig(data="", path=config_path) - elif "json" in config_path: - self.config = JsonConfig(data="{}", path=config_path) - elif "yaml" in config_path: - self.config = YAMLConfig(data="", path=config_path) - - # Create and initialize config - self.config.init_empty_config_content() - - self.config.set_key("name", cz_name) - self.config.set_key("tag_format", tag_format) - self.config.set_key("version_scheme", version_scheme) - if version_provider == "commitizen": - self.config.set_key("version", version.public) - else: - self.config.set_key("version_provider", version_provider) - if update_changelog_on_bump: - self.config.set_key("update_changelog_on_bump", update_changelog_on_bump) - if major_version_zero: - self.config.set_key("major_version_zero", major_version_zero) + _write_config_to_file( + path=config_path, + cz_name=cz_name, + version_provider=version_provider, + version_scheme=version_scheme, + version=version, + tag_format=tag_format, + update_changelog_on_bump=update_changelog_on_bump, + major_version_zero=major_version_zero, + ) out.write("\nYou can bump the version running:\n") out.info("\tcz bump\n") out.success("Configuration complete 🚀") - def _ask_config_path(self) -> str: + def _ask_config_path(self) -> Path: default_path = ( "pyproject.toml" if self.project_info.has_pyproject else ".cz.toml" ) - name: str = questionary.select( + filename: str = questionary.select( "Please choose a supported config file: ", choices=CONFIG_FILES, default=default_path, style=self.cz.style, ).unsafe_ask() - return name + return Path(filename) def _ask_name(self) -> str: name: str = questionary.select( @@ -372,3 +364,30 @@ def _get_config_data(self) -> dict[str, Any]: else: repos.append(CZ_HOOK_CONFIG) return config_data + + +def _write_config_to_file( + *, + path: Path, + cz_name: str, + version_provider: str, + version_scheme: str, + version: Version, + tag_format: str, + update_changelog_on_bump: bool, + major_version_zero: bool, +) -> None: + out_config = create_config(path=path) + out_config.init_empty_config_content() + + out_config.set_key("name", cz_name) + out_config.set_key("tag_format", tag_format) + out_config.set_key("version_scheme", version_scheme) + if version_provider == "commitizen": + out_config.set_key("version", version.public) + else: + out_config.set_key("version_provider", version_provider) + if update_changelog_on_bump: + out_config.set_key("update_changelog_on_bump", update_changelog_on_bump) + if major_version_zero: + out_config.set_key("major_version_zero", major_version_zero) diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index 9dfd591c40..6cf8b840d8 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -1,58 +1,46 @@ from __future__ import annotations +from collections.abc import Generator from pathlib import Path from commitizen import defaults, git +from commitizen.config.factory import create_config from commitizen.exceptions import ConfigFileIsEmpty, ConfigFileNotFound from .base_config import BaseConfig -from .json_config import JsonConfig -from .toml_config import TomlConfig -from .yaml_config import YAMLConfig -def read_cfg(filepath: str | None = None) -> BaseConfig: - conf = BaseConfig() - +def _resolve_config_paths(filepath: str | None = None) -> Generator[Path, None, None]: if filepath is not None: - if not Path(filepath).exists(): + out_path = Path(filepath) + if not out_path.exists(): raise ConfigFileNotFound() - cfg_paths = (path for path in (Path(filepath),)) - else: - git_project_root = git.find_git_project_root() - cfg_search_paths = [Path(".")] - if git_project_root: - cfg_search_paths.append(git_project_root) + yield out_path + return - cfg_paths = ( - path / Path(filename) - for path in cfg_search_paths - for filename in defaults.CONFIG_FILES - ) + git_project_root = git.find_git_project_root() + cfg_search_paths = [Path(".")] + if git_project_root: + cfg_search_paths.append(git_project_root) - for filename in cfg_paths: - if not filename.exists(): - continue + for path in cfg_search_paths: + for filename in defaults.CONFIG_FILES: + out_path = path / Path(filename) + if out_path.exists(): + yield out_path - _conf: TomlConfig | JsonConfig | YAMLConfig +def read_cfg(filepath: str | None = None) -> BaseConfig: + for filename in _resolve_config_paths(filepath): with open(filename, "rb") as f: data: bytes = f.read() - if "toml" in filename.suffix: - _conf = TomlConfig(data=data, path=filename) - elif "json" in filename.suffix: - _conf = JsonConfig(data=data, path=filename) - elif "yaml" in filename.suffix: - _conf = YAMLConfig(data=data, path=filename) + conf = create_config(data=data, path=filename) + if not conf.is_empty_config: + return conf - if filepath is not None and _conf.is_empty_config: + if filepath is not None: raise ConfigFileIsEmpty() - elif _conf.is_empty_config: - continue - else: - conf = _conf - break - return conf + return BaseConfig() diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 4b8f5f05fc..f7a6f3b8e8 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -17,8 +17,8 @@ class BaseConfig: def __init__(self) -> None: + self.is_empty_config = False self._settings: Settings = DEFAULT_SETTINGS.copy() - self.encoding = self.settings["encoding"] self._path: Path | None = None @property @@ -30,7 +30,7 @@ def path(self) -> Path: return self._path # type: ignore[return-value] @path.setter - def path(self, path: str | Path) -> None: + def path(self, path: Path) -> None: self._path = Path(path) def set_key(self, key: str, value: Any) -> Self: @@ -48,4 +48,8 @@ def _parse_setting(self, data: bytes | str) -> None: raise NotImplementedError() def init_empty_config_content(self) -> None: + """Create a config file with the empty config content. + + The implementation is different for each config file type. + """ raise NotImplementedError() diff --git a/commitizen/config/factory.py b/commitizen/config/factory.py new file mode 100644 index 0000000000..b77aea5f32 --- /dev/null +++ b/commitizen/config/factory.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from pathlib import Path + +from commitizen.config.base_config import BaseConfig +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig + + +def create_config(*, data: bytes | str | None = None, path: Path) -> BaseConfig: + if "toml" in path.suffix: + return TomlConfig(data=data or "", path=path) + if "json" in path.suffix: + return JsonConfig(data=data or "{}", path=path) + if "yaml" in path.suffix: + return YAMLConfig(data=data or "", path=path) + + # Should be unreachable. See the constant CONFIG_FILES. + raise ValueError( + f"Unsupported config file: {path.name} due to unknown file extension" + ) diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index be1f1c36b0..4655f48d5e 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -20,14 +20,15 @@ class JsonConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str) -> None: + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False self.path = path self._parse_setting(data) def init_empty_config_content(self) -> None: - with smart_open(self.path, "a", encoding=self.encoding) as json_file: + with smart_open( + self.path, "a", encoding=self._settings["encoding"] + ) as json_file: json.dump({"commitizen": {}}, json_file) def set_key(self, key: str, value: Any) -> Self: @@ -40,7 +41,7 @@ def set_key(self, key: str, value: Any) -> Self: parser = json.load(f) parser["commitizen"][key] = value - with smart_open(self.path, "w", encoding=self.encoding) as f: + with smart_open(self.path, "w", encoding=self._settings["encoding"]) as f: json.dump(parser, f, indent=2) return self diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 2164d3f992..8fa3d97828 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -21,9 +21,8 @@ class TomlConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str) -> None: + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False self.path = path self._parse_setting(data) @@ -38,7 +37,9 @@ def init_empty_config_content(self) -> None: if parser.get("tool") is None: parser["tool"] = table() parser["tool"]["commitizen"] = table() # type: ignore[index] - output_toml_file.write(parser.as_string().encode(self.encoding)) + output_toml_file.write( + parser.as_string().encode(self._settings["encoding"]) + ) def set_key(self, key: str, value: Any) -> Self: """Set or update a key in the conf. @@ -51,7 +52,7 @@ def set_key(self, key: str, value: Any) -> Self: parser["tool"]["commitizen"][key] = value # type: ignore[index] with open(self.path, "wb") as f: - f.write(parser.as_string().encode(self.encoding)) + f.write(parser.as_string().encode(self._settings["encoding"])) return self def _parse_setting(self, data: bytes | str) -> None: diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index f2a79e6937..d2924ef023 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -21,14 +21,15 @@ class YAMLConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str) -> None: + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False self.path = path self._parse_setting(data) def init_empty_config_content(self) -> None: - with smart_open(self.path, "a", encoding=self.encoding) as json_file: + with smart_open( + self.path, "a", encoding=self._settings["encoding"] + ) as json_file: yaml.dump({"commitizen": {}}, json_file, explicit_start=True) def _parse_setting(self, data: bytes | str) -> None: @@ -61,7 +62,9 @@ def set_key(self, key: str, value: Any) -> Self: parser = yaml.load(yaml_file, Loader=yaml.FullLoader) parser["commitizen"][key] = value - with smart_open(self.path, "w", encoding=self.encoding) as yaml_file: + with smart_open( + self.path, "w", encoding=self._settings["encoding"] + ) as yaml_file: yaml.dump(parser, yaml_file, explicit_start=True) return self diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index 91931849b2..4f9b5de3c6 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -3,7 +3,8 @@ import pytest from commitizen import defaults -from commitizen.config import BaseConfig, JsonConfig +from commitizen.config import BaseConfig +from commitizen.config.json_config import JsonConfig @pytest.fixture() diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 8c632a2b67..4d8492ac3b 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -3,6 +3,7 @@ import json import os import sys +from pathlib import Path from typing import Any import pytest @@ -87,7 +88,7 @@ def test_init_without_setup_pre_commit_hook( def test_init_when_config_already_exists(config: BaseConfig, capsys): # Set config path - path = os.sep.join(["tests", "pyproject.toml"]) + path = Path(os.sep.join(["tests", "pyproject.toml"])) config.path = path commands.Init(config)() diff --git a/tests/test_conf.py b/tests/test_conf.py index cc38bf4b05..0953401ea7 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -9,6 +9,9 @@ import yaml from commitizen import config, defaults, git +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig from commitizen.exceptions import ConfigFileIsEmpty, InvalidConfigurationError PYPROJECT = """ @@ -211,7 +214,7 @@ def test_load_cz_json_not_from_config_argument(_, tmpdir): _not_root_path.write(JSON_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.json") - json_cfg_by_class = config.JsonConfig(data=JSON_STR, path=_not_root_path) + json_cfg_by_class = JsonConfig(data=JSON_STR, path=_not_root_path) assert cfg.settings == json_cfg_by_class.settings def test_load_cz_yaml_not_from_config_argument(_, tmpdir): @@ -220,7 +223,7 @@ def test_load_cz_yaml_not_from_config_argument(_, tmpdir): _not_root_path.write(YAML_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.yaml") - yaml_cfg_by_class = config.YAMLConfig(data=YAML_STR, path=_not_root_path) + yaml_cfg_by_class = YAMLConfig(data=YAML_STR, path=_not_root_path) assert cfg.settings == yaml_cfg_by_class._settings def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): @@ -244,7 +247,7 @@ def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): class TestTomlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - toml_config = config.TomlConfig(data="", path=path) + toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: @@ -257,7 +260,7 @@ def test_init_empty_config_content_with_existing_content( path = tmpdir.mkdir("commitizen").join(config_file) path.write(existing_content) - toml_config = config.TomlConfig(data="", path=path) + toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: @@ -270,7 +273,7 @@ def test_init_with_invalid_config_content( path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.TomlConfig(data=existing_content, path=path) + TomlConfig(data=existing_content, path=path) @pytest.mark.parametrize( @@ -284,7 +287,7 @@ def test_init_with_invalid_config_content( class TestJsonConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - json_config = config.JsonConfig(data="{}", path=path) + json_config = JsonConfig(data="{}", path=path) json_config.init_empty_config_content() with open(path, encoding="utf-8") as json_file: @@ -297,7 +300,7 @@ def test_init_with_invalid_config_content( path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.JsonConfig(data=existing_content, path=path) + JsonConfig(data=existing_content, path=path) @pytest.mark.parametrize( @@ -311,7 +314,7 @@ def test_init_with_invalid_config_content( class TestYamlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - yaml_config = config.YAMLConfig(data="{}", path=path) + yaml_config = YAMLConfig(data="{}", path=path) yaml_config.init_empty_config_content() with open(path) as yaml_file: @@ -322,4 +325,4 @@ def test_init_with_invalid_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.YAMLConfig(data=existing_content, path=path) + YAMLConfig(data=existing_content, path=path) diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 933b1aa065..76341828d4 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -1,6 +1,11 @@ +from pathlib import Path + import pytest -from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig +from commitizen.config import BaseConfig +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig from commitizen.cz.customize import CustomizeCommitsCz from commitizen.exceptions import MissingCzCustomizeConfigError @@ -319,8 +324,8 @@ @pytest.fixture( params=[ - TomlConfig(data=TOML_STR, path="not_exist.toml"), - JsonConfig(data=JSON_STR, path="not_exist.json"), + TomlConfig(data=TOML_STR, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR, path=Path("not_exist.json")), ] ) def config(request): @@ -334,9 +339,9 @@ def config(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_STR_INFO_PATH, path="not_exist.toml"), - JsonConfig(data=JSON_STR_INFO_PATH, path="not_exist.json"), - YAMLConfig(data=YAML_STR_INFO_PATH, path="not_exist.yaml"), + TomlConfig(data=TOML_STR_INFO_PATH, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR_INFO_PATH, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR_INFO_PATH, path=Path("not_exist.yaml")), ] ) def config_info(request): @@ -345,9 +350,9 @@ def config_info(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_STR_WITHOUT_INFO, path="not_exist.toml"), - JsonConfig(data=JSON_STR_WITHOUT_PATH, path="not_exist.json"), - YAMLConfig(data=YAML_STR_WITHOUT_PATH, path="not_exist.yaml"), + TomlConfig(data=TOML_STR_WITHOUT_INFO, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR_WITHOUT_PATH, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR_WITHOUT_PATH, path=Path("not_exist.yaml")), ] ) def config_without_info(request): @@ -356,8 +361,8 @@ def config_without_info(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_WITH_UNICODE, path="not_exist.toml"), - JsonConfig(data=JSON_WITH_UNICODE, path="not_exist.json"), + TomlConfig(data=TOML_WITH_UNICODE, path=Path("not_exist.toml")), + JsonConfig(data=JSON_WITH_UNICODE, path=Path("not_exist.json")), ] ) def config_with_unicode(request): diff --git a/tests/test_cz_search_filter.py b/tests/test_cz_search_filter.py index 0e70e3104a..cbceb8b887 100644 --- a/tests/test_cz_search_filter.py +++ b/tests/test_cz_search_filter.py @@ -1,6 +1,6 @@ import pytest -from commitizen.config import TomlConfig +from commitizen.config.toml_config import TomlConfig from commitizen.cz.customize import CustomizeCommitsCz TOML_WITH_SEARCH_FILTER = r""" From e758e9929b5d103756d9bdbad5c8c91b1dbc3ea9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 14:05:23 +0800 Subject: [PATCH 216/275] refactor(Init): make project_info a module and remove self.project_info --- commitizen/commands/init.py | 91 ++--------------------------- commitizen/project_info.py | 47 +++++++++++++++ tests/commands/test_init_command.py | 4 +- tests/test_project_info.py | 90 ++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 87 deletions(-) create mode 100644 commitizen/project_info.py create mode 100644 tests/test_project_info.py diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 637caa2841..4a8a69d9e9 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -1,14 +1,12 @@ from __future__ import annotations -import os -import shutil from pathlib import Path from typing import Any, NamedTuple import questionary import yaml -from commitizen import cmd, factory, out +from commitizen import cmd, factory, out, project_info from commitizen.__version__ import __version__ from commitizen.config import ( BaseConfig, @@ -69,64 +67,12 @@ def title(self) -> str: ) -class ProjectInfo: - """Discover information about the current folder.""" - - @property - def has_pyproject(self) -> bool: - return os.path.isfile("pyproject.toml") - - @property - def has_uv_lock(self) -> bool: - return os.path.isfile("uv.lock") - - @property - def has_setup(self) -> bool: - return os.path.isfile("setup.py") - - @property - def has_pre_commit_config(self) -> bool: - return os.path.isfile(".pre-commit-config.yaml") - - @property - def is_python_uv(self) -> bool: - return self.has_pyproject and self.has_uv_lock - - @property - def is_python_poetry(self) -> bool: - if not self.has_pyproject: - return False - with open("pyproject.toml") as f: - return "[tool.poetry]" in f.read() - - @property - def is_python(self) -> bool: - return self.has_pyproject or self.has_setup - - @property - def is_rust_cargo(self) -> bool: - return os.path.isfile("Cargo.toml") - - @property - def is_npm_package(self) -> bool: - return os.path.isfile("package.json") - - @property - def is_php_composer(self) -> bool: - return os.path.isfile("composer.json") - - @property - def is_pre_commit_installed(self) -> bool: - return bool(shutil.which("pre-commit")) - - class Init: _PRE_COMMIT_CONFIG_PATH = ".pre-commit-config.yaml" def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config self.cz = factory.committer_factory(self.config) - self.project_info = ProjectInfo() def __call__(self) -> None: if self.config.path: @@ -172,7 +118,7 @@ def __call__(self) -> None: ) as config_file: yaml.safe_dump(config_data, stream=config_file) - if not self.project_info.is_pre_commit_installed: + if not project_info.is_pre_commit_installed(): raise InitFailedError( "Failed to install pre-commit hook.\n" "pre-commit is not installed in current environment." @@ -208,14 +154,10 @@ def __call__(self) -> None: out.success("Configuration complete 🚀") def _ask_config_path(self) -> Path: - default_path = ( - "pyproject.toml" if self.project_info.has_pyproject else ".cz.toml" - ) - filename: str = questionary.select( "Please choose a supported config file: ", choices=CONFIG_FILES, - default=default_path, + default=project_info.get_default_config_filename(), style=self.cz.style, ).unsafe_ask() return Path(filename) @@ -280,37 +222,17 @@ def _ask_version_provider(self) -> str: "Choose the source of the version:", choices=_VERSION_PROVIDER_CHOICES, style=self.cz.style, - default=self._default_version_provider, + default=project_info.get_default_version_provider(), ).unsafe_ask() return version_provider - @property - def _default_version_provider(self) -> str: - if self.project_info.is_python: - if self.project_info.is_python_poetry: - return "poetry" - if self.project_info.is_python_uv: - return "uv" - return "pep621" - - if self.project_info.is_rust_cargo: - return "cargo" - if self.project_info.is_npm_package: - return "npm" - if self.project_info.is_php_composer: - return "composer" - - return "commitizen" - def _ask_version_scheme(self) -> str: """Ask for setting: version_scheme""" - default_scheme = "pep440" if self.project_info.is_python else "semver" - scheme: str = questionary.select( "Choose version scheme: ", choices=KNOWN_SCHEMES, style=self.cz.style, - default=default_scheme, + default=project_info.get_default_version_scheme(), ).unsafe_ask() return scheme @@ -344,8 +266,7 @@ def _get_config_data(self) -> dict[str, Any]: ], } - if not self.project_info.has_pre_commit_config: - # .pre-commit-config.yaml does not exist + if not Path(".pre-commit-config.yaml").is_file(): return {"repos": [CZ_HOOK_CONFIG]} with open( diff --git a/commitizen/project_info.py b/commitizen/project_info.py new file mode 100644 index 0000000000..a754388008 --- /dev/null +++ b/commitizen/project_info.py @@ -0,0 +1,47 @@ +"""Resolves project information about the current working directory.""" + +import shutil +from pathlib import Path +from typing import Literal + + +def is_pre_commit_installed() -> bool: + return bool(shutil.which("pre-commit")) + + +def get_default_version_provider() -> Literal[ + "commitizen", "cargo", "composer", "npm", "pep621", "poetry", "uv" +]: + pyproject_path = Path("pyproject.toml") + if pyproject_path.is_file(): + if "[tool.poetry]" in pyproject_path.read_text(): + return "poetry" + if Path("uv.lock").is_file(): + return "uv" + return "pep621" + + if Path("setup.py").is_file(): + return "pep621" + + if Path("Cargo.toml").is_file(): + return "cargo" + + if Path("package.json").is_file(): + return "npm" + + if Path("composer.json").is_file(): + return "composer" + + return "commitizen" + + +def get_default_config_filename() -> Literal["pyproject.toml", ".cz.toml"]: + return "pyproject.toml" if Path("pyproject.toml").is_file() else ".cz.toml" + + +def get_default_version_scheme() -> Literal["pep440", "semver"]: + return ( + "pep440" + if Path("pyproject.toml").is_file() or Path("setup.py").is_file() + else "semver" + ) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 4d8492ac3b..cfecfebeb1 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -122,7 +122,7 @@ def test_init_without_choosing_tag(config: BaseConfig, mocker: MockFixture, tmpd def pre_commit_installed(mocker: MockFixture): # Assume the `pre-commit` is installed mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", + "commitizen.project_info.is_pre_commit_installed", return_value=True, ) # And installation success (i.e. no exception raised) @@ -232,7 +232,7 @@ def test_pre_commit_not_installed( ): # Assume `pre-commit` is not installed mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", + "commitizen.project_info.is_pre_commit_installed", return_value=False, ) with tmpdir.as_cwd(): diff --git a/tests/test_project_info.py b/tests/test_project_info.py new file mode 100644 index 0000000000..d30a743e58 --- /dev/null +++ b/tests/test_project_info.py @@ -0,0 +1,90 @@ +"""Tests for project_info module.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from commitizen import project_info + + +def _create_project_files(files: dict[str, str | None]) -> None: + for file_path, content in files.items(): + path = Path(file_path) + if content is None: + path.touch() + else: + path.write_text(content) + + +@pytest.mark.parametrize( + "which_return, expected", + [ + ("/usr/local/bin/pre-commit", True), + (None, False), + ("", False), + ], +) +def test_is_pre_commit_installed(mocker, which_return, expected): + mocker.patch("shutil.which", return_value=which_return) + assert project_info.is_pre_commit_installed() is expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ( + {"pyproject.toml": '[tool.poetry]\nname = "test"\nversion = "0.1.0"'}, + "poetry", + ), + ({"pyproject.toml": "", "uv.lock": ""}, "uv"), + ( + {"pyproject.toml": '[tool.commitizen]\nversion = "0.1.0"'}, + "pep621", + ), + ({"setup.py": ""}, "pep621"), + ({"Cargo.toml": ""}, "cargo"), + ({"package.json": ""}, "npm"), + ({"composer.json": ""}, "composer"), + ({}, "commitizen"), + ( + { + "pyproject.toml": "", + "Cargo.toml": "", + "package.json": "", + "composer.json": "", + }, + "pep621", + ), + ], +) +def test_get_default_version_provider(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_version_provider() == expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ({"pyproject.toml": ""}, "pyproject.toml"), + ({}, ".cz.toml"), + ], +) +def test_get_default_config_filename(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_config_filename() == expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ({"pyproject.toml": ""}, "pep440"), + ({"setup.py": ""}, "pep440"), + ({"package.json": ""}, "semver"), + ({}, "semver"), + ], +) +def test_get_default_version_scheme(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_version_scheme() == expected From 8ab25ab530b1090267dd2ee1846e13995a922729 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 29 Sep 2025 11:15:54 +0800 Subject: [PATCH 217/275] refactor(hooks): refactor to improve readability --- hooks/prepare-commit-msg.py | 60 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/hooks/prepare-commit-msg.py b/hooks/prepare-commit-msg.py index a100f13afa..017ecc28ea 100755 --- a/hooks/prepare-commit-msg.py +++ b/hooks/prepare-commit-msg.py @@ -2,7 +2,7 @@ import shutil import subprocess import sys -from subprocess import CalledProcessError +from pathlib import Path try: from commitizen.cz.utils import get_backup_file_path @@ -23,33 +23,34 @@ def prepare_commit_msg(commit_msg_file: str) -> int: ], capture_output=True, ).returncode - if exit_code != 0: - backup_file_path = get_backup_file_path() - if backup_file_path.is_file(): - # confirm if commit message from backup file should be reused - answer = input("retry with previous message? [y/N]: ") - if answer.lower() == "y": - shutil.copyfile(backup_file_path, commit_msg_file) - return 0 - - # use commitizen to generate the commit message - try: - subprocess.run( - [ - "cz", - "commit", - "--dry-run", - "--write-message-to-file", - commit_msg_file, - ], - stdin=sys.stdin, - stdout=sys.stdout, - ).check_returncode() - except CalledProcessError as error: - return error.returncode - - # write message to backup file - shutil.copyfile(commit_msg_file, backup_file_path) + if exit_code == 0: + return 0 + + backup_file = Path(get_backup_file_path()) + if backup_file.is_file(): + # confirm if commit message from backup file should be reused + answer = input("retry with previous message? [y/N]: ") + if answer.lower() == "y": + shutil.copyfile(backup_file, commit_msg_file) + return 0 + + # use commitizen to generate the commit message + exit_code = subprocess.run( + [ + "cz", + "commit", + "--dry-run", + "--write-message-to-file", + commit_msg_file, + ], + stdin=sys.stdin, + stdout=sys.stdout, + ).returncode + if exit_code: + return exit_code + + # write message to backup file + shutil.copyfile(commit_msg_file, backup_file) return 0 @@ -57,4 +58,5 @@ def prepare_commit_msg(commit_msg_file: str) -> int: # make hook interactive by attaching /dev/tty to stdin with open("/dev/tty") as tty: sys.stdin = tty - exit(prepare_commit_msg(sys.argv[1])) + exit_code = prepare_commit_msg(sys.argv[1]) + exit(exit_code) From 33213d8df86a25dcf0fcea491569a5dfb810a3a2 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 29 Sep 2025 02:25:08 +0800 Subject: [PATCH 218/275] refactor(Commit): refactor _prompt_commit_questions and fix some type hint --- commitizen/commands/commit.py | 38 ++++++++++++++++++++--------------- commitizen/cz/base.py | 2 +- tests/conftest.py | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 8ab8d2cac9..5a37d4d7c9 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -63,33 +63,37 @@ def _read_backup_message(self) -> str | None: ) as f: return f.read().strip() - def _prompt_commit_questions(self) -> str: + def _get_message_by_prompt_commit_questions(self) -> str: # Prompt user for the commit message - cz = self.cz - questions = cz.questions() + questions = self.cz.questions() for question in (q for q in questions if q["type"] == "list"): question["use_shortcuts"] = self.config.settings["use_shortcuts"] try: - answers = questionary.prompt(questions, style=cz.style) + answers = questionary.prompt(questions, style=self.cz.style) except ValueError as err: root_err = err.__context__ if isinstance(root_err, CzException): - raise CustomError(root_err.__str__()) + raise CustomError(str(root_err)) raise err if not answers: raise NoAnswersError() - message = cz.message(answers) - message_len = len(message.partition("\n")[0].strip()) - message_length_limit = self.arguments.get("message_length_limit", 0) - if 0 < message_length_limit < message_len: + message = self.cz.message(answers) + self._validate_subject_length(message) + return message + + def _validate_subject_length(self, message: str) -> None: + # By the contract, message_length_limit is set to 0 for no limit + subject = message.partition("\n")[0].strip() + limit = self.arguments.get("message_length_limit", 0) + if limit == 0: + return + if len(subject) > limit: raise CommitMessageLengthExceededError( - f"Length of commit message exceeds limit ({message_len}/{message_length_limit})" + f"Length of commit message exceeds limit ({len(subject)}/{limit}), subject: '{subject}'" ) - return message - def manual_edit(self, message: str) -> str: editor = git.get_core_editor() if editor is None: @@ -114,11 +118,13 @@ def _get_message(self) -> str: raise NoCommitBackupError() return commit_message - if self.config.settings.get("retry_after_failure") and not self.arguments.get( - "no_retry" + if ( + self.config.settings.get("retry_after_failure") + and not self.arguments.get("no_retry") + and (backup_message := self._read_backup_message()) ): - return self._read_backup_message() or self._prompt_commit_questions() - return self._prompt_commit_questions() + return backup_message + return self._get_message_by_prompt_commit_questions() def __call__(self) -> None: extra_args = self.arguments.get("extra_cli_args", "") diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index ecb1a21962..8466b58bba 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -68,7 +68,7 @@ def __init__(self, config: BaseConfig) -> None: self.config.settings.update({"style": BaseCommitizen.default_style_config}) @abstractmethod - def questions(self) -> Iterable[CzQuestion]: + def questions(self) -> list[CzQuestion]: """Questions regarding the commit message.""" @abstractmethod diff --git a/tests/conftest.py b/tests/conftest.py index 61b64ae8d2..04a448d99a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -177,7 +177,7 @@ class SemverCommitizen(BaseCommitizen): "patch": "Bugs", } - def questions(self) -> list: + def questions(self) -> list[CzQuestion]: return [ { "type": "list", From fa8d227aefb7f28beac984fb31e3c6819503f086 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 8 Nov 2025 16:36:39 +0800 Subject: [PATCH 219/275] fixup! refactor(Commit): refactor _prompt_commit_questions and fix some type hint --- commitizen/commands/commit.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 5a37d4d7c9..d16b4d8d88 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -80,18 +80,16 @@ def _get_message_by_prompt_commit_questions(self) -> str: raise NoAnswersError() message = self.cz.message(answers) - self._validate_subject_length(message) + if limit := self.arguments.get("message_length_limit", 0): + self._validate_subject_length(message=message, length_limit=limit) return message - def _validate_subject_length(self, message: str) -> None: + def _validate_subject_length(self, *, message: str, length_limit: int) -> None: # By the contract, message_length_limit is set to 0 for no limit subject = message.partition("\n")[0].strip() - limit = self.arguments.get("message_length_limit", 0) - if limit == 0: - return - if len(subject) > limit: + if len(subject) > length_limit: raise CommitMessageLengthExceededError( - f"Length of commit message exceeds limit ({len(subject)}/{limit}), subject: '{subject}'" + f"Length of commit message exceeds limit ({len(subject)}/{length_limit}), subject: '{subject}'" ) def manual_edit(self, message: str) -> str: From 596446e9ad8a53188b04df556b92be630d967520 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sat, 13 Sep 2025 21:38:24 +0800 Subject: [PATCH 220/275] refactor(TomlConfig): minor cleanups for DX --- commitizen/config/toml_config.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 8fa3d97828..0aadad8c1f 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -2,9 +2,9 @@ import os from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING -from tomlkit import exceptions, parse, table +from tomlkit import TOMLDocument, exceptions, parse, table from commitizen.exceptions import InvalidConfigurationError @@ -27,32 +27,33 @@ def __init__(self, *, data: bytes | str, path: Path) -> None: self._parse_setting(data) def init_empty_config_content(self) -> None: + config_doc = TOMLDocument() if os.path.isfile(self.path): with open(self.path, "rb") as input_toml_file: - parser = parse(input_toml_file.read()) - else: - parser = parse("") + config_doc = parse(input_toml_file.read()) + + if config_doc.get("tool") is None: + config_doc["tool"] = table() + config_doc["tool"]["commitizen"] = table() # type: ignore[index] with open(self.path, "wb") as output_toml_file: - if parser.get("tool") is None: - parser["tool"] = table() - parser["tool"]["commitizen"] = table() # type: ignore[index] output_toml_file.write( - parser.as_string().encode(self._settings["encoding"]) + config_doc.as_string().encode(self._settings["encoding"]) ) - def set_key(self, key: str, value: Any) -> Self: + def set_key(self, key: str, value: object) -> Self: """Set or update a key in the conf. For now only strings are supported. We use to update the version number. """ with open(self.path, "rb") as f: - parser = parse(f.read()) + config_doc = parse(f.read()) - parser["tool"]["commitizen"][key] = value # type: ignore[index] + config_doc["tool"]["commitizen"][key] = value # type: ignore[index] with open(self.path, "wb") as f: - f.write(parser.as_string().encode(self._settings["encoding"])) + f.write(config_doc.as_string().encode(self._settings["encoding"])) + return self def _parse_setting(self, data: bytes | str) -> None: From d77da0930c18c7636f9f387ea6cf427a57e8bfee Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Sun, 14 Sep 2025 22:15:36 +0800 Subject: [PATCH 221/275] style(BaseConfig): update set_key comments and type annotation, remove duplicated docstring --- commitizen/config/base_config.py | 9 ++++----- commitizen/config/json_config.py | 15 +++++---------- commitizen/config/toml_config.py | 5 ----- commitizen/config/yaml_config.py | 15 +++++---------- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index f7a6f3b8e8..98270915d8 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from commitizen.defaults import DEFAULT_SETTINGS, Settings @@ -33,11 +33,10 @@ def path(self) -> Path: def path(self, path: Path) -> None: self._path = Path(path) - def set_key(self, key: str, value: Any) -> Self: - """Set or update a key in the conf. + def set_key(self, key: str, value: object) -> Self: + """Set or update a key in the config file. - For now only strings are supported. - We use to update the version number. + Currently, only strings are supported for the parameter key. """ raise NotImplementedError() diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 4655f48d5e..4e7097aa1a 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -2,7 +2,7 @@ import json from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from commitizen.exceptions import InvalidConfigurationError from commitizen.git import smart_open @@ -31,18 +31,13 @@ def init_empty_config_content(self) -> None: ) as json_file: json.dump({"commitizen": {}}, json_file) - def set_key(self, key: str, value: Any) -> Self: - """Set or update a key in the conf. - - For now only strings are supported. - We use to update the version number. - """ + def set_key(self, key: str, value: object) -> Self: with open(self.path, "rb") as f: - parser = json.load(f) + config_doc = json.load(f) - parser["commitizen"][key] = value + config_doc["commitizen"][key] = value with smart_open(self.path, "w", encoding=self._settings["encoding"]) as f: - json.dump(parser, f, indent=2) + json.dump(config_doc, f, indent=2) return self def _parse_setting(self, data: bytes | str) -> None: diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 0aadad8c1f..4ea1dca7d4 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -42,11 +42,6 @@ def init_empty_config_content(self) -> None: ) def set_key(self, key: str, value: object) -> Self: - """Set or update a key in the conf. - - For now only strings are supported. - We use to update the version number. - """ with open(self.path, "rb") as f: config_doc = parse(f.read()) diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index d2924ef023..c048ab272a 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING import yaml @@ -52,19 +52,14 @@ def _parse_setting(self, data: bytes | str) -> None: except (KeyError, TypeError): self.is_empty_config = True - def set_key(self, key: str, value: Any) -> Self: - """Set or update a key in the conf. - - For now only strings are supported. - We use to update the version number. - """ + def set_key(self, key: str, value: object) -> Self: with open(self.path, "rb") as yaml_file: - parser = yaml.load(yaml_file, Loader=yaml.FullLoader) + config_doc = yaml.load(yaml_file, Loader=yaml.FullLoader) - parser["commitizen"][key] = value + config_doc["commitizen"][key] = value with smart_open( self.path, "w", encoding=self._settings["encoding"] ) as yaml_file: - yaml.dump(parser, yaml_file, explicit_start=True) + yaml.dump(config_doc, yaml_file, explicit_start=True) return self From 79a420cdaf6254fd3e379c5607c44cb4777f0833 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 15 Sep 2025 20:58:05 +0800 Subject: [PATCH 222/275] refactor(RestructuredTest): rename variable, fix typo and remove unnecessary string copy --- .../changelog_formats/restructuredtext.py | 118 +++++++++--------- .../test_changelog_format_restructuredtext.py | 14 ++- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/commitizen/changelog_formats/restructuredtext.py b/commitizen/changelog_formats/restructuredtext.py index b7e4e105a1..e8ce411e80 100644 --- a/commitizen/changelog_formats/restructuredtext.py +++ b/commitizen/changelog_formats/restructuredtext.py @@ -1,92 +1,88 @@ from __future__ import annotations -import sys from itertools import zip_longest -from typing import IO, TYPE_CHECKING, Any, Union +from typing import IO from commitizen.changelog import Metadata from .base import BaseFormat -if TYPE_CHECKING: - # TypeAlias is Python 3.10+ but backported in typing-extensions - if sys.version_info >= (3, 10): - from typing import TypeAlias - else: - from typing_extensions import TypeAlias - - -# Can't use `|` operator and native type because of https://bugs.python.org/issue42233 only fixed in 3.10 -TitleKind: TypeAlias = Union[str, tuple[str, str]] - class RestructuredText(BaseFormat): extension = "rst" - def get_metadata_from_file(self, file: IO[Any]) -> Metadata: + def get_metadata_from_file(self, file: IO[str]) -> Metadata: """ RestructuredText section titles are not one-line-based, they spread on 2 or 3 lines and levels are not predefined - but determined byt their occurrence order. + but determined by their occurrence order. It requires its own algorithm. For a more generic approach, you need to rely on `docutils`. """ - meta = Metadata() - unreleased_title_kind: TitleKind | None = None - in_overlined_title = False - lines = file.readlines() + out_metadata = Metadata() + unreleased_title_kind: str | tuple[str, str] | None = None + is_overlined_title = False + lines = [line.strip().lower() for line in file.readlines()] + for index, (first, second, third) in enumerate( zip_longest(lines, lines[1:], lines[2:], fillvalue="") ): - first = first.strip().lower() - second = second.strip().lower() - third = third.strip().lower() title: str | None = None - kind: TitleKind | None = None - if self.is_overlined_title(first, second, third): + kind: str | tuple[str, str] | None = None + if _is_overlined_title(first, second, third): title = second kind = (first[0], third[0]) - in_overlined_title = True - elif not in_overlined_title and self.is_underlined_title(first, second): + is_overlined_title = True + elif not is_overlined_title and _is_underlined_title(first, second): title = first kind = second[0] else: - in_overlined_title = False - - if title: - if "unreleased" in title: - unreleased_title_kind = kind - meta.unreleased_start = index - continue - elif unreleased_title_kind and unreleased_title_kind == kind: - meta.unreleased_end = index - # Try to find the latest release done - if version := self.tag_rules.search_version(title): - meta.latest_version = version[0] - meta.latest_version_tag = version[1] - meta.latest_version_position = index - break - if meta.unreleased_start is not None and meta.unreleased_end is None: - meta.unreleased_end = ( - meta.latest_version_position if meta.latest_version else index + 1 + is_overlined_title = False + + if not title: + continue + + if "unreleased" in title: + unreleased_title_kind = kind + out_metadata.unreleased_start = index + continue + + if unreleased_title_kind and unreleased_title_kind == kind: + out_metadata.unreleased_end = index + # Try to find the latest release done + if version := self.tag_rules.search_version(title): + out_metadata.latest_version = version[0] + out_metadata.latest_version_tag = version[1] + out_metadata.latest_version_position = index + break + + if ( + out_metadata.unreleased_start is not None + and out_metadata.unreleased_end is None + ): + out_metadata.unreleased_end = ( + out_metadata.latest_version_position + if out_metadata.latest_version + else len(lines) ) - return meta - - def is_overlined_title(self, first: str, second: str, third: str) -> bool: - return ( - len(first) >= len(second) - and len(first) == len(third) - and all(char == first[0] for char in first[1:]) - and first[0] == third[0] - and self.is_underlined_title(second, third) - ) - - def is_underlined_title(self, first: str, second: str) -> bool: - return ( - len(second) >= len(first) - and not second.isalnum() - and all(char == second[0] for char in second[1:]) - ) + return out_metadata + + +def _is_overlined_title(first: str, second: str, third: str) -> bool: + return ( + len(first) == len(third) >= len(second) + and first[0] == third[0] + and all(char == first[0] for char in first) + and _is_underlined_title(second, third) + ) + + +def _is_underlined_title(first: str, second: str) -> bool: + return ( + len(second) >= len(first) + and not second.isalnum() + and all(char == second[0] for char in second) + ) diff --git a/tests/test_changelog_format_restructuredtext.py b/tests/test_changelog_format_restructuredtext.py index 14bc15ec09..ca79620ad3 100644 --- a/tests/test_changelog_format_restructuredtext.py +++ b/tests/test_changelog_format_restructuredtext.py @@ -7,7 +7,11 @@ import pytest from commitizen.changelog import Metadata -from commitizen.changelog_formats.restructuredtext import RestructuredText +from commitizen.changelog_formats.restructuredtext import ( + RestructuredText, + _is_overlined_title, + _is_underlined_title, +) from commitizen.config.base_config import BaseConfig if TYPE_CHECKING: @@ -325,9 +329,9 @@ def test_get_metadata( [(text, True) for text in UNDERLINED_TITLES] + [(text, False) for text in NOT_UNDERLINED_TITLES], ) -def test_is_underlined_title(format: RestructuredText, text: str, expected: bool): +def test_is_underlined_title(text: str, expected: bool): _, first, second = dedent(text).splitlines() - assert format.is_underlined_title(first, second) is expected + assert _is_underlined_title(first, second) is expected @pytest.mark.parametrize( @@ -335,10 +339,10 @@ def test_is_underlined_title(format: RestructuredText, text: str, expected: bool [(text, True) for text in OVERLINED_TITLES] + [(text, False) for text in NOT_OVERLINED_TITLES], ) -def test_is_overlined_title(format: RestructuredText, text: str, expected: bool): +def test_is_overlined_title(text: str, expected: bool): _, first, second, third = dedent(text).splitlines() - assert format.is_overlined_title(first, second, third) is expected + assert _is_overlined_title(first, second, third) is expected @pytest.mark.parametrize( From d895b47b319b71e5ed071b48ed31561a4f469c9e Mon Sep 17 00:00:00 2001 From: Sebastien Fusilier Date: Mon, 1 Sep 2025 14:11:38 +0000 Subject: [PATCH 223/275] feat(conventional_commits): allow exclamation in title on BC Add a configuration (disable by default) to allow exclamation in commit title to ease identify BC simple git log parsing. Signed-off-by: Sebastien Fusilier --- .../conventional_commits.py | 14 +++--- commitizen/defaults.py | 2 + docs/config.md | 10 ++++ docs/tutorials/writing_commits.md | 5 ++ tests/test_conf.py | 2 + tests/test_cz_conventional_commits.py | 49 +++++++++++++++++++ 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index c827cebb78..acb0dfe094 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -154,17 +154,15 @@ def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[o footer = answers["footer"] is_breaking_change = answers["is_breaking_change"] - if scope: - # example: "fix(users): email pattern corrected" - first_line = f"{prefix}({scope}): {subject}" - else: - # example: "fix: email pattern corrected" - first_line = f"{prefix}: {subject}" - + scope = f"({scope})" if scope else "" + body = f"\n\n{body}" if body else "" + title = f"{prefix}{scope}" if is_breaking_change: + if self.config.settings.get("breaking_change_exclamation_in_title", False): + title = f"{title}!" footer = f"BREAKING CHANGE: {footer}" - return "\n\n".join(s for s in (first_line, body, footer) if s) + return f"{title}: {subject}{body}{footer}" def example(self) -> str: return ( diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 4840a69b6a..68e580f52b 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -61,6 +61,7 @@ class Settings(TypedDict, total=False): version_scheme: str | None version_type: str | None version: str | None + breaking_change_exclamation_in_title: bool CONFIG_FILES: list[str] = [ @@ -109,6 +110,7 @@ class Settings(TypedDict, total=False): "always_signoff": False, "template": None, # default provided by plugin "extras": {}, + "breaking_change_exclamation_in_title": False, } MAJOR = "MAJOR" diff --git a/docs/config.md b/docs/config.md index 05f45a1a80..c00a6f72a8 100644 --- a/docs/config.md +++ b/docs/config.md @@ -96,6 +96,15 @@ Use annotated tags instead of lightweight tags. [See difference][annotated-tags- Create custom commit message. Useful to skip CI. [Read more][bump_message] +### `breaking_change_exclamation_in_title` + +Type: `bool` + +Default: `False` + +When true, breaking changes will be also indicated by an exclamation mark in the commit title (e.g., `feat!: breaking change`). +When false, breaking changes will be only indicated by `BREAKING CHANGE:` in the footer. [Read more][writing_commits] + ### `retry_after_failure` - Type: `bool` @@ -401,3 +410,4 @@ setup( [template-customization]: customization.md#customizing-the-changelog-template [annotated-tags-vs-lightweight]: https://stackoverflow.com/a/11514139/2047185 [encoding]: tutorials/writing_commits.md#writing-commits +[writing_commits]: tutorials/writing_commits.md#conventional-commits diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md index 7d9139929c..a64480874a 100644 --- a/docs/tutorials/writing_commits.md +++ b/docs/tutorials/writing_commits.md @@ -13,6 +13,10 @@ add to your commit body the following `BREAKING CHANGE`. Using these three keywords will allow the proper identification of the semantic version. Of course, there are other keywords, but I'll leave it to the reader to explore them. +Note: You can also indicate breaking changes by adding an exclamation mark in the commit title +(e.g., `feat!: breaking change`) by setting the `breaking_change_exclamation_in_title` +configuration option to `true`. [Read more][breaking-change-config] + ## Writing commits Now to the important part: when writing commits, it's important to think about: @@ -44,3 +48,4 @@ Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the [conventional_commits]: https://www.conventionalcommits.org [cz_emoji]: https://commitizen-tools.github.io/commitizen/third-party-commitizen/#cz-emoji [configuration]: ../config.md#encoding +[breaking-change-config]: ../config.md#breaking_change_exclamation_in_title diff --git a/tests/test_conf.py b/tests/test_conf.py index 0953401ea7..47633c7e01 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -105,6 +105,7 @@ "always_signoff": False, "template": None, "extras": {}, + "breaking_change_exclamation_in_title": False, } _new_settings: dict[str, Any] = { @@ -143,6 +144,7 @@ "always_signoff": False, "template": None, "extras": {}, + "breaking_change_exclamation_in_title": False, } diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index c96e036707..1742b0f3b7 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -105,6 +105,55 @@ def test_breaking_change_in_footer(config): ) +@pytest.mark.parametrize( + "scope,breaking_change_exclamation_in_title,expected_message", + [ + # Test with scope and breaking_change_exclamation_in_title enabled + ( + "users", + True, + "feat(users)!: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users", + ), + # Test without scope and breaking_change_exclamation_in_title enabled + ( + "", + True, + "feat!: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users", + ), + # Test with scope and breaking_change_exclamation_in_title disabled + ( + "users", + False, + "feat(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users", + ), + # Test without scope and breaking_change_exclamation_in_title disabled + ( + "", + False, + "feat: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users", + ), + ], +) +def test_breaking_change_message_formats( + config, scope, breaking_change_exclamation_in_title, expected_message +): + # Set the breaking_change_exclamation_in_title setting + config.settings["breaking_change_exclamation_in_title"] = ( + breaking_change_exclamation_in_title + ) + conventional_commits = ConventionalCommitsCz(config) + answers = { + "prefix": "feat", + "scope": scope, + "subject": "email pattern corrected", + "is_breaking_change": True, + "body": "complete content", + "footer": "migrate by renaming user to users", + } + message = conventional_commits.message(answers) + assert message == expected_message + + def test_example(config): """just testing a string is returned. not the content""" conventional_commits = ConventionalCommitsCz(config) From 327677abc0833ee5dc9aa748c90730b106d1b7a7 Mon Sep 17 00:00:00 2001 From: Sebastien Fusilier Date: Tue, 9 Sep 2025 13:43:06 +0000 Subject: [PATCH 224/275] fix(test): set terminal width for cli tests Force consistent terminal width for tests to avoid wrapping differences between single and multi-worker pytest modes Signed-off-by: Sebastien Fusilier --- .../conventional_commits.py | 10 ++++--- tests/commands/test_version_command.py | 27 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index acb0dfe094..f0b254eb10 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -154,15 +154,17 @@ def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[o footer = answers["footer"] is_breaking_change = answers["is_breaking_change"] - scope = f"({scope})" if scope else "" - body = f"\n\n{body}" if body else "" - title = f"{prefix}{scope}" + formatted_scope = f"({scope})" if scope else "" + title = f"{prefix}{formatted_scope}" if is_breaking_change: if self.config.settings.get("breaking_change_exclamation_in_title", False): title = f"{title}!" footer = f"BREAKING CHANGE: {footer}" - return f"{title}: {subject}{body}{footer}" + formatted_body = f"\n\n{body}" if body else "" + formatted_footter = f"\n\n{footer}" if footer else "" + + return f"{title}: {subject}{formatted_body}{formatted_footter}" def example(self) -> str: return ( diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index cd2e7f77e0..7b5b13a7e1 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -1,3 +1,4 @@ +import os import platform import sys @@ -112,13 +113,25 @@ def test_version_use_version_provider( def test_version_command_shows_description_when_use_help_option( mocker: MockerFixture, capsys, file_regression ): - testargs = ["cz", "version", "--help"] - mocker.patch.object(sys, "argv", testargs) - with pytest.raises(SystemExit): - cli.main() - - out, _ = capsys.readouterr() - file_regression.check(out, extension=".txt") + # Force consistent terminal width for tests to avoid wrapping differences + # between single and multi-worker pytest modes + original_columns = os.environ.get("COLUMNS") + os.environ["COLUMNS"] = "80" + + try: + testargs = ["cz", "version", "--help"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(SystemExit): + cli.main() + + out, _ = capsys.readouterr() + file_regression.check(out, extension=".txt") + finally: + # Restore original COLUMNS + if original_columns is not None: + os.environ["COLUMNS"] = original_columns + else: + os.environ.pop("COLUMNS", None) @pytest.mark.parametrize( From 0e9497050b9e04a5c045ea872ec4239aeb7dfb0b Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 10 Sep 2025 23:23:33 +0800 Subject: [PATCH 225/275] refactor(bump): cleanup related to update_version_file --- commitizen/bump.py | 81 ++++----- tests/test_bump_update_version_in_files.py | 182 ++++++++++++++++++++- 2 files changed, 207 insertions(+), 56 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 6d6b6dc069..6672c5f509 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -3,13 +3,13 @@ import os import re from collections import OrderedDict -from collections.abc import Iterable +from collections.abc import Generator, Iterable from glob import iglob from logging import getLogger from string import Template from typing import cast -from commitizen.defaults import BUMP_MESSAGE, ENCODING, MAJOR, MINOR, PATCH +from commitizen.defaults import BUMP_MESSAGE, MAJOR, MINOR, PATCH from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open from commitizen.version_schemes import Increment, Version @@ -64,8 +64,8 @@ def update_version_in_files( new_version: str, files: Iterable[str], *, - check_consistency: bool = False, - encoding: str = ENCODING, + check_consistency: bool, + encoding: str, ) -> list[str]: """Change old version to the new one in every file given. @@ -75,16 +75,22 @@ def update_version_in_files( Returns the list of updated files. """ - # TODO: separate check step and write step - updated = [] - for path, regex in _files_and_regexes(files, current_version): - current_version_found, version_file = _bump_with_regex( - path, - current_version, - new_version, - regex, - encoding=encoding, - ) + updated_files = [] + + for path, pattern in _resolve_files_and_regexes(files, current_version): + current_version_found = False + bumped_lines = [] + + with open(path, encoding=encoding) as version_file: + for line in version_file: + bumped_line = ( + line.replace(current_version, new_version) + if pattern.search(line) + else line + ) + + current_version_found = current_version_found or bumped_line != line + bumped_lines.append(bumped_line) if check_consistency and not current_version_found: raise CurrentVersionNotFoundError( @@ -93,53 +99,32 @@ def update_version_in_files( "version_files are possibly inconsistent." ) + bumped_version_file_content = "".join(bumped_lines) + # Write the file out again with smart_open(path, "w", encoding=encoding) as file: - file.write(version_file) - updated.append(path) - return updated + file.write(bumped_version_file_content) + updated_files.append(path) + + return updated_files -def _files_and_regexes(patterns: Iterable[str], version: str) -> list[tuple[str, str]]: +def _resolve_files_and_regexes( + patterns: Iterable[str], version: str +) -> Generator[tuple[str, re.Pattern], None, None]: """ Resolve all distinct files with their regexp from a list of glob patterns with optional regexp """ - out: set[tuple[str, str]] = set() + filepath_set: set[tuple[str, str]] = set() for pattern in patterns: drive, tail = os.path.splitdrive(pattern) path, _, regex = tail.partition(":") filepath = drive + path - if not regex: - regex = re.escape(version) + regex = regex or re.escape(version) - for file in iglob(filepath): - out.add((file, regex)) + filepath_set.update((path, regex) for path in iglob(filepath)) - return sorted(out) - - -def _bump_with_regex( - version_filepath: str, - current_version: str, - new_version: str, - regex: str, - encoding: str = ENCODING, -) -> tuple[bool, str]: - current_version_found = False - lines = [] - pattern = re.compile(regex) - with open(version_filepath, encoding=encoding) as f: - for line in f: - if not pattern.search(line): - lines.append(line) - continue - - bumped_line = line.replace(current_version, new_version) - if bumped_line != line: - current_version_found = True - lines.append(bumped_line) - - return current_version_found, "".join(lines) + return ((path, re.compile(regex)) for path, regex in sorted(filepath_set)) def create_commit_message( diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index c14e4ad1cb..5a74c1c456 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -103,7 +103,11 @@ def test_update_version_in_files(version_files, file_regression): old_version = "1.2.3" new_version = "2.0.0" bump.update_version_in_files( - old_version, new_version, version_files, encoding="utf-8" + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", ) file_contents = "" @@ -119,7 +123,9 @@ def test_partial_update_of_file(version_repeated_file, file_regression): regex = "version" location = f"{version_repeated_file}:{regex}" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(version_repeated_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".json") @@ -129,7 +135,9 @@ def test_random_location(random_location_version_file, file_regression): new_version = "2.0.0" location = f"{random_location_version_file}:version.+Commitizen" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(random_location_version_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".lock") @@ -141,7 +149,9 @@ def test_duplicates_are_change_with_no_regex( new_version = "2.0.0" location = f"{random_location_version_file}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(random_location_version_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".lock") @@ -153,7 +163,9 @@ def test_version_bump_increase_string_length( new_version = "1.2.10" location = f"{multiple_versions_increase_string}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_increase_string, encoding="utf-8") as f: file_regression.check(f.read(), extension=".txt") @@ -165,7 +177,9 @@ def test_version_bump_reduce_string_length( new_version = "2.0.0" location = f"{multiple_versions_reduce_string}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_reduce_string, encoding="utf-8") as f: file_regression.check(f.read(), extension=".txt") @@ -204,7 +218,9 @@ def test_multiple_versions_to_bump( new_version = "1.2.10" location = f"{multiple_versions_to_update_poetry_lock}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_to_update_poetry_lock, encoding="utf-8") as f: file_regression.check(f.read(), extension=".toml") @@ -220,8 +236,158 @@ def test_update_version_in_globbed_files(commitizen_config_file, file_regression version_files = [commitizen_config_file.dirpath("*.toml")] bump.update_version_in_files( - old_version, new_version, version_files, encoding="utf-8" + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", ) for file in commitizen_config_file, other: file_regression.check(file.read_text("utf-8"), extension=".toml") + + +def test_update_version_in_files_with_check_consistency_true(version_files): + """Test update_version_in_files with check_consistency=True (success case).""" + old_version = "1.2.3" + new_version = "2.0.0" + + # This should succeed because all files contain the current version + updated_files = bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=True, + encoding="utf-8", + ) + + # Verify that all files were updated + assert len(updated_files) == len(version_files) + for file_path in updated_files: + assert file_path in version_files + + +def test_update_version_in_files_with_check_consistency_true_failure( + commitizen_config_file, inconsistent_python_version_file +): + """Test update_version_in_files with check_consistency=True (failure case).""" + old_version = "1.2.3" + new_version = "2.0.0" + version_files = [commitizen_config_file, inconsistent_python_version_file] + + # This should fail because inconsistent_python_version_file doesn't contain the current version + with pytest.raises(CurrentVersionNotFoundError) as excinfo: + bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=True, + encoding="utf-8", + ) + + expected_msg = ( + f"Current version {old_version} is not found in {inconsistent_python_version_file}.\n" + "The version defined in commitizen configuration and the ones in " + "version_files are possibly inconsistent." + ) + assert expected_msg in str(excinfo.value) + + +@pytest.mark.parametrize( + "encoding,filename", + [ + ("latin-1", "test_latin1.txt"), + ("utf-16", "test_utf16.txt"), + ], + ids=["latin-1", "utf-16"], +) +def test_update_version_in_files_with_different_encodings(tmp_path, encoding, filename): + """Test update_version_in_files with different encodings.""" + # Create a test file with the specified encoding + test_file = tmp_path / filename + content = f'version = "1.2.3"\n# This is a test file with {encoding} encoding\n' + test_file.write_text(content, encoding=encoding) + + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + [str(test_file)], + check_consistency=True, + encoding=encoding, + ) + + # Verify the file was updated + assert len(updated_files) == 1 + assert str(test_file) in updated_files + + # Verify the content was updated correctly + updated_content = test_file.read_text(encoding=encoding) + assert f'version = "{new_version}"' in updated_content + assert f'version = "{old_version}"' not in updated_content + + +def test_update_version_in_files_return_value(version_files): + """Test that update_version_in_files returns the correct list of updated files.""" + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", + ) + + # Verify return value is a list + assert isinstance(updated_files, list) + + # Verify all files in the input are in the returned list + assert len(updated_files) == len(version_files) + for file_path in version_files: + assert file_path in updated_files + + # Verify the returned paths are strings + for file_path in updated_files: + assert isinstance(file_path, str) + + +def test_update_version_in_files_return_value_partial_update(tmp_path): + """Test return value when only some files are updated.""" + # Create two test files + file1 = tmp_path / "file1.txt" + file2 = tmp_path / "file2.txt" + + # File1 contains the version to update + file1.write_text('version = "1.2.3"\n') + + # File2 doesn't contain the version + file2.write_text("some other content\n") + + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + [str(file1), str(file2)], + check_consistency=False, + encoding="utf-8", + ) + + # Verify return value + assert isinstance(updated_files, list) + assert len(updated_files) == 2 # Both files should be in the list + assert str(file1) in updated_files + assert str(file2) in updated_files + + # Verify file1 was actually updated + content1 = file1.read_text(encoding="utf-8") + assert f'version = "{new_version}"' in content1 + + # Verify file2 was not changed + content2 = file2.read_text(encoding="utf-8") + assert content2 == "some other content\n" From a31df00f77ed28ba8cb65cde5f93830cbdcf6e98 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 8 Nov 2025 17:08:06 +0800 Subject: [PATCH 226/275] test: simplify assertion --- tests/test_bump_update_version_in_files.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index 5a74c1c456..a1df2f34b4 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -346,13 +346,10 @@ def test_update_version_in_files_return_value(version_files): assert isinstance(updated_files, list) # Verify all files in the input are in the returned list - assert len(updated_files) == len(version_files) - for file_path in version_files: - assert file_path in updated_files + assert set(version_files) == set(updated_files) # Verify the returned paths are strings - for file_path in updated_files: - assert isinstance(file_path, str) + assert all(isinstance(file_path, str) for file_path in updated_files) def test_update_version_in_files_return_value_partial_update(tmp_path): From 31b1309ae298361eeae13f9324ca572821bc0ea7 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 8 Nov 2025 17:59:37 +0800 Subject: [PATCH 227/275] test: replace tmpdir with tmppath --- tests/test_bump_update_version_in_files.py | 79 ++++++++++++---------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index a1df2f34b4..9d53a3e81d 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -2,6 +2,7 @@ from shutil import copyfile import pytest +from _pytest.fixtures import FixtureRequest from commitizen import bump from commitizen.exceptions import CurrentVersionNotFoundError @@ -21,40 +22,40 @@ def _copy_sample_file_to_tmpdir( @pytest.fixture(scope="function") -def commitizen_config_file(tmpdir): +def commitizen_config_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "sample_pyproject.toml", "pyproject.toml" + tmp_path, "sample_pyproject.toml", "pyproject.toml" ) @pytest.fixture(scope="function") -def python_version_file(tmpdir, request): - return _copy_sample_file_to_tmpdir(tmpdir, "sample_version.py", "__version__.py") +def python_version_file(tmp_path: Path, request: FixtureRequest) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, "sample_version.py", "__version__.py") @pytest.fixture(scope="function") -def inconsistent_python_version_file(tmpdir): +def inconsistent_python_version_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "inconsistent_version.py", "__version__.py" + tmp_path, "inconsistent_version.py", "__version__.py" ) @pytest.fixture(scope="function") -def random_location_version_file(tmpdir): - return _copy_sample_file_to_tmpdir(tmpdir, "sample_cargo.lock", "Cargo.lock") +def random_location_version_file(tmp_path: Path) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, "sample_cargo.lock", "Cargo.lock") @pytest.fixture(scope="function") -def version_repeated_file(tmpdir): +def version_repeated_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "repeated_version_number.json", "package.json" + tmp_path, "repeated_version_number.json", "package.json" ) @pytest.fixture(scope="function") -def docker_compose_file(tmpdir): +def docker_compose_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "sample_docker_compose.yaml", "docker-compose.yaml" + tmp_path, "sample_docker_compose.yaml", "docker-compose.yaml" ) @@ -66,36 +67,38 @@ def docker_compose_file(tmpdir): ), ids=("with_eol", "without_eol"), ) -def multiple_versions_to_update_poetry_lock(tmpdir, request): - return _copy_sample_file_to_tmpdir(tmpdir, request.param, "pyproject.toml") +def multiple_versions_to_update_poetry_lock( + tmp_path: Path, request: FixtureRequest +) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, request.param, "pyproject.toml") @pytest.fixture(scope="function") -def multiple_versions_increase_string(tmpdir): - tmp_file = tmpdir.join("anyfile") - tmp_file.write(MULTIPLE_VERSIONS_INCREASE_STRING) +def multiple_versions_increase_string(tmp_path: Path) -> str: + tmp_file = tmp_path / "anyfile" + tmp_file.write_text(MULTIPLE_VERSIONS_INCREASE_STRING) return str(tmp_file) @pytest.fixture(scope="function") -def multiple_versions_reduce_string(tmpdir): - tmp_file = tmpdir.join("anyfile") - tmp_file.write(MULTIPLE_VERSIONS_REDUCE_STRING) +def multiple_versions_reduce_string(tmp_path: Path) -> str: + tmp_file = tmp_path / "anyfile" + tmp_file.write_text(MULTIPLE_VERSIONS_REDUCE_STRING) return str(tmp_file) @pytest.fixture(scope="function") def version_files( - commitizen_config_file, - python_version_file, - version_repeated_file, - docker_compose_file, -): + commitizen_config_file: Path, + python_version_file: Path, + version_repeated_file: Path, + docker_compose_file: Path, +) -> tuple[str, ...]: return ( - commitizen_config_file, - python_version_file, - version_repeated_file, - docker_compose_file, + str(commitizen_config_file), + str(python_version_file), + str(version_repeated_file), + str(docker_compose_file), ) @@ -228,12 +231,14 @@ def test_multiple_versions_to_bump( def test_update_version_in_globbed_files(commitizen_config_file, file_regression): old_version = "1.2.3" new_version = "2.0.0" - other = commitizen_config_file.dirpath("other.toml") - print(commitizen_config_file, other) + other = commitizen_config_file.parent / "other.toml" + copyfile(commitizen_config_file, other) # Prepend full path as test assume absolute paths or cwd-relative - version_files = [commitizen_config_file.dirpath("*.toml")] + version_files = [ + str(file_path) for file_path in commitizen_config_file.parent.glob("*.toml") + ] bump.update_version_in_files( old_version, @@ -247,13 +252,15 @@ def test_update_version_in_globbed_files(commitizen_config_file, file_regression file_regression.check(file.read_text("utf-8"), extension=".toml") -def test_update_version_in_files_with_check_consistency_true(version_files): +def test_update_version_in_files_with_check_consistency_true( + version_files: tuple[str, ...], +): """Test update_version_in_files with check_consistency=True (success case).""" old_version = "1.2.3" new_version = "2.0.0" # This should succeed because all files contain the current version - updated_files = bump.update_version_in_files( + updated_files: list[str] = bump.update_version_in_files( old_version, new_version, version_files, @@ -262,9 +269,7 @@ def test_update_version_in_files_with_check_consistency_true(version_files): ) # Verify that all files were updated - assert len(updated_files) == len(version_files) - for file_path in updated_files: - assert file_path in version_files + assert set(updated_files) == set(version_files) def test_update_version_in_files_with_check_consistency_true_failure( From e6bcb1a81acd9d424c60c8002949409514a197f3 Mon Sep 17 00:00:00 2001 From: catfish Date: Mon, 11 Aug 2025 23:54:31 +0800 Subject: [PATCH 228/275] feat: add config option for line length warning --- commitizen/cli.py | 2 - commitizen/commands/check.py | 21 +++++++--- commitizen/commands/commit.py | 7 +++- commitizen/defaults.py | 2 + poetry.lock | 2 +- pyproject.toml | 2 +- tests/commands/test_check_command.py | 59 ++++++++++++++++++++++++++- tests/commands/test_commit_command.py | 59 +++++++++++++++++++++++++++ tests/test_conf.py | 2 + 9 files changed, 143 insertions(+), 13 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index e9689d75f3..c11e9078dc 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -160,7 +160,6 @@ def __call__( { "name": ["-l", "--message-length-limit"], "type": int, - "default": 0, "help": "length limit of the commit message; 0 for no limit", }, { @@ -499,7 +498,6 @@ def __call__( { "name": ["-l", "--message-length-limit"], "type": int, - "default": 0, "help": "length limit of the commit message; 0 for no limit", }, ], diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index d45e388f9d..a6101f7df7 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -7,6 +7,7 @@ from commitizen import factory, git, out from commitizen.config import BaseConfig from commitizen.exceptions import ( + CommitMessageLengthExceededError, InvalidCommandArgumentError, InvalidCommitMessageError, NoCommitsFoundError, @@ -18,7 +19,7 @@ class CheckArgs(TypedDict, total=False): commit_msg: str rev_range: str allow_abort: bool - message_length_limit: int + message_length_limit: int | None allowed_prefixes: list[str] message: str use_default_range: bool @@ -41,8 +42,11 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N self.allow_abort = bool( arguments.get("allow_abort", config.settings["allow_abort"]) ) + self.use_default_range = bool(arguments.get("use_default_range")) - self.max_msg_length = arguments.get("message_length_limit", 0) + self.max_msg_length = arguments.get( + "message_length_limit", config.settings.get("message_length_limit", None) + ) # we need to distinguish between None and [], which is a valid value allowed_prefixes = arguments.get("allowed_prefixes") @@ -88,7 +92,7 @@ def __call__(self) -> None: invalid_msgs_content = "\n".join( f'commit "{commit.rev}": "{commit.message}"' for commit in commits - if not self._validate_commit_message(commit.message, pattern) + if not self._validate_commit_message(commit.message, pattern, commit.rev) ) if invalid_msgs_content: # TODO: capitalize the first letter of the error message for consistency in v5 @@ -153,7 +157,7 @@ def _filter_comments(msg: str) -> str: return "\n".join(lines) def _validate_commit_message( - self, commit_msg: str, pattern: re.Pattern[str] + self, commit_msg: str, pattern: re.Pattern[str], commit_hash: str ) -> bool: if not commit_msg: return self.allow_abort @@ -161,9 +165,14 @@ def _validate_commit_message( if any(map(commit_msg.startswith, self.allowed_prefixes)): return True - if self.max_msg_length: + if self.max_msg_length is not None: msg_len = len(commit_msg.partition("\n")[0].strip()) if msg_len > self.max_msg_length: - return False + raise CommitMessageLengthExceededError( + f"commit validation: failed!\n" + f"commit message length exceeds the limit.\n" + f'commit "{commit_hash}": "{commit_msg}"\n' + f"message length limit: {self.max_msg_length} (actual: {msg_len})" + ) return bool(pattern.match(commit_msg)) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index d16b4d8d88..7144bced87 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -33,7 +33,7 @@ class CommitArgs(TypedDict, total=False): dry_run: bool edit: bool extra_cli_args: str - message_length_limit: int + message_length_limit: int | None no_retry: bool signoff: bool write_message_to_file: Path | None @@ -80,8 +80,11 @@ def _get_message_by_prompt_commit_questions(self) -> str: raise NoAnswersError() message = self.cz.message(answers) - if limit := self.arguments.get("message_length_limit", 0): + if limit := self.arguments.get( + "message_length_limit", self.config.settings.get("message_length_limit", 0) + ): self._validate_subject_length(message=message, length_limit=limit) + return message def _validate_subject_length(self, *, message: str, length_limit: int) -> None: diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 68e580f52b..9b3b76a686 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -46,6 +46,7 @@ class Settings(TypedDict, total=False): ignored_tag_formats: Sequence[str] legacy_tag_formats: Sequence[str] major_version_zero: bool + message_length_limit: int | None name: str post_bump_hooks: list[str] | None pre_bump_hooks: list[str] | None @@ -111,6 +112,7 @@ class Settings(TypedDict, total=False): "template": None, # default provided by plugin "extras": {}, "breaking_change_exclamation_in_title": False, + "message_length_limit": None, # None for no limit } MAJOR = "MAJOR" diff --git a/poetry.lock b/poetry.lock index 1a731e4f06..49df9dcd52 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1955,4 +1955,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "bdc8773ed978a4265a2a099265db7e116d2f65c467c4980d984e546716cea244" +content-hash = "cd5648d8aad7b58913b1c0e4cd4f04c98d5bcfa7e4ef8e7bb994a59492d7d4a2" diff --git a/pyproject.toml b/pyproject.toml index f16ad596bb..4ff252ebf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "colorama (>=0.4.1,<1.0)", "termcolor (>=1.1.0,<4.0.0)", "packaging>=19", - "tomlkit (>=0.5.3,<1.0.0)", + "tomlkit (>=0.8.0,<1.0.0)", "jinja2>=2.10.3", "pyyaml>=3.08", "argcomplete >=1.12.1,<3.7", diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index 365a556dda..d2a82a903a 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -8,6 +8,7 @@ from commitizen import cli, commands, git from commitizen.exceptions import ( + CommitMessageLengthExceededError, InvalidCommandArgumentError, InvalidCommitMessageError, NoCommitsFoundError, @@ -449,7 +450,7 @@ def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFi arguments={"message": message, "message_length_limit": len(message) - 1}, ) - with pytest.raises(InvalidCommitMessageError): + with pytest.raises(CommitMessageLengthExceededError): check_cmd() error_mock.assert_called_once() @@ -460,3 +461,59 @@ def test_check_command_with_amend_prefix_default(config, mocker: MockFixture): check_cmd() success_mock.assert_called_once() + + +def test_check_command_with_config_message_length_limit(config, mocker: MockFixture): + success_mock = mocker.patch("commitizen.out.success") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) + 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message}, + ) + + check_cmd() + success_mock.assert_called_once() + + +def test_check_command_with_config_message_length_limit_exceeded( + config, mocker: MockFixture +): + error_mock = mocker.patch("commitizen.out.error") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) - 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message}, + ) + + with pytest.raises(CommitMessageLengthExceededError): + check_cmd() + error_mock.assert_called_once() + + +def test_check_command_cli_overrides_config_message_length_limit( + config, mocker: MockFixture +): + success_mock = mocker.patch("commitizen.out.success") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) - 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message, "message_length_limit": len(message) + 1}, + ) + + check_cmd() + success_mock.assert_called_once() + + success_mock.reset_mock() + check_cmd = commands.Check( + config=config, + arguments={"message": message, "message_length_limit": None}, + ) diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 8d0181f3e0..3e408576fe 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -554,3 +554,62 @@ def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out): commit_mock.assert_called_once() error_mock.assert_called_once_with(out) + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_with_config_message_length_limit(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prefix = "feat" + subject = "random subject" + message_length = len(prefix) + len(": ") + len(subject) + prompt_mock.return_value = { + "prefix": prefix, + "subject": subject, + "scope": "", + "is_breaking_change": False, + "body": "random body", + "footer": "random footer", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + success_mock = mocker.patch("commitizen.out.success") + + config.settings["message_length_limit"] = message_length + commands.Commit(config, {})() + success_mock.assert_called_once() + + config.settings["message_length_limit"] = message_length - 1 + with pytest.raises(CommitMessageLengthExceededError): + commands.Commit(config, {})() + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_cli_overrides_config_message_length_limit( + config, mocker: MockFixture +): + prompt_mock = mocker.patch("questionary.prompt") + prefix = "feat" + subject = "random subject" + message_length = len(prefix) + len(": ") + len(subject) + prompt_mock.return_value = { + "prefix": prefix, + "subject": subject, + "scope": "", + "is_breaking_change": False, + "body": "random body", + "footer": "random footer", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + success_mock = mocker.patch("commitizen.out.success") + + config.settings["message_length_limit"] = message_length - 1 + + commands.Commit(config, {"message_length_limit": message_length})() + success_mock.assert_called_once() + + success_mock.reset_mock() + commands.Commit(config, {"message_length_limit": None})() + success_mock.assert_called_once() diff --git a/tests/test_conf.py b/tests/test_conf.py index 47633c7e01..bbbed41e08 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -106,6 +106,7 @@ "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, + "message_length_limit": None, } _new_settings: dict[str, Any] = { @@ -145,6 +146,7 @@ "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, + "message_length_limit": None, } From f31db0d8764842c19684fd9a813110798b91741e Mon Sep 17 00:00:00 2001 From: catfish Date: Tue, 12 Aug 2025 13:06:27 +0800 Subject: [PATCH 229/275] docs(config): add message length limit configuration option --- docs/config.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/config.md b/docs/config.md index c00a6f72a8..94aa2076b4 100644 --- a/docs/config.md +++ b/docs/config.md @@ -119,6 +119,14 @@ Automatically retry failed commit when running `cz commit`. [Read more][retry_af Disallow empty commit messages. Useful in CI. [Read more][allow_abort] +### `message_length_limit` + +Type: `int` + +Default: `0` + +Maximum length of the commit message. Setting it to `0` disables the length limit. It can be overridden by the `-l/--message-length-limit` command line argument. + ### `allowed_prefixes` - Type: `list` From a85ab5e72426272872dc58590aeffb2bd38b01cd Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Tue, 13 Aug 2024 17:35:37 +0200 Subject: [PATCH 230/275] style: unify YAML quotes style Signed-off-by: Adrian DC --- docs/customization.md | 14 +++++++------- tests/test_cz_customize.py | 16 +++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index df77171077..99ffd39ba7 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -110,13 +110,13 @@ And the correspondent example for a yaml file: commitizen: name: cz_customize customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' example: 'feature: this feature enable customize through config file' - schema: ": " - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" - commit_parser: "^(?Pfeature|bug fix):\\s(?P.*)?" - changelog_pattern: "^(feature|bug fix)?(!)?" + schema: ': ' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?Pfeature|bug fix):\\s(?P.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' change_type_map: feature: Feat bug fix: Fix @@ -125,7 +125,7 @@ commitizen: new: MINOR fix: PATCH hotfix: PATCH - change_type_order: ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] + change_type_order: ['BREAKING CHANGE', 'feat', 'fix', 'refactor', 'perf'] info_path: cz_customize_info.txt info: This is customized info questions: diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 76341828d4..dd354d65ea 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -110,17 +110,22 @@ - commitizen/__version__.py - pyproject.toml customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' example: 'feature: this feature enables customization through a config file' - schema: ": " - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" + schema: ': ' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?Pfeature|bug fix):\\s(?P.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' + change_type_map: + feature: Feat + bug fix: Fix bump_map: break: MAJOR new: MINOR fix: PATCH hotfix: PATCH - change_type_order: ["perf", "BREAKING CHANGE", "feat", "fix", "refactor"] + change_type_order: ['perf', 'BREAKING CHANGE', 'feat', 'fix', 'refactor'] info: This is a customized cz. questions: - type: list @@ -326,6 +331,7 @@ params=[ TomlConfig(data=TOML_STR, path=Path("not_exist.toml")), JsonConfig(data=JSON_STR, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR, path=Path("not_exist.yaml")), ] ) def config(request): From 7018c78d03b0fe2691f01464a4a533fea60b5c56 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sun, 9 Nov 2025 09:59:15 +0800 Subject: [PATCH 231/275] fix(commands/version): add missing return --- commitizen/commands/version.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 04fa664f25..7ccadb5135 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -48,6 +48,8 @@ def __call__(self) -> None: version_scheme = get_version_scheme(self.config.settings) except VersionSchemeUnknown: out.error("Unknown version scheme.") + return + _version = version_scheme(version) if self.parameter.get("major"): From 9f3ec868c0429155a46b4819b625d47a8522a3d5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 10 Nov 2025 14:08:24 +0000 Subject: [PATCH 232/275] =?UTF-8?q?bump:=20version=204.9.1=20=E2=86=92=204?= =?UTF-8?q?.10.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4af318e4eb..bf207ea60b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.9.1 # automatically updated by Commitizen + rev: v4.10.0 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index a72dc0fda0..f8bca478b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## v4.10.0 (2025-11-10) + +### Feat + +- add config option for line length warning +- **conventional_commits**: allow exclamation in title on BC +- **version**: add the ability to just print major or minor version +- allow `amend!` prefix as created by `git --fixup=reword:` + +### Fix + +- **commands/version**: add missing return +- **test**: set terminal width for cli tests +- **Init**: raise InitFailedError on keyboard interrupt on pre-commit hook question, simplify logic, remove unreachable code path + +### Refactor + +- **bump**: cleanup related to update_version_file +- **RestructuredTest**: rename variable, fix typo and remove unnecessary string copy +- **TomlConfig**: minor cleanups for DX +- **Commit**: refactor _prompt_commit_questions and fix some type hint +- **hooks**: refactor to improve readability +- **Init**: make project_info a module and remove self.project_info +- **BaseConfig**: update docstring, extract factory method and remove unnecessary variable assignment +- remove self.encoding for better maintainability +- **utils**: make get_backup_file_path to return a path for semantic correctness +- remove unnecessary class member tag_format +- **Bump**: remove use of getattr +- **ConventionalCommitsCz**: rewrite message method to make the pattern more clear +- **cmd**: unnest try except +- **BaseCommitizen**: remove NotImplementedError and make them abstract method +- **BaseCommitizen**: construct Style object directly to get rid of potential type error + ## v4.9.1 (2025-09-10) ### Fix diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 0139b2ebd5..897e6be2ff 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.9.1" +__version__ = "4.10.0" diff --git a/pyproject.toml b/pyproject.toml index 4ff252ebf5..8211b969d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.9.1" +version = "4.10.0" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -90,7 +90,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.9.1" +version = "4.10.0" tag_format = "v$version" version_files = [ "pyproject.toml:version", From f4e8fcb863b21a3796218dc7fd210cacb85a3bf4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 12 Sep 2025 18:13:26 +0800 Subject: [PATCH 233/275] test(changelog): cover incremental_build --- tests/test_incremental_build.py | 606 ++++++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 tests/test_incremental_build.py diff --git a/tests/test_incremental_build.py b/tests/test_incremental_build.py new file mode 100644 index 0000000000..13f123a651 --- /dev/null +++ b/tests/test_incremental_build.py @@ -0,0 +1,606 @@ +"""Tests for the incremental_build function in commitizen.changelog module.""" + +from commitizen.changelog import Metadata, incremental_build + + +class TestIncrementalBuild: + """Test cases for the incremental_build function.""" + + def test_basic_replacement_of_unreleased_section(self): + """Test basic functionality of replacing unreleased section with new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=6, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_is_none(self): + """Test replacement when latest_version_position is None (append to end).""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_after_unreleased_end(self): + """Test replacement when latest_version_position is after unreleased_end.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_before_unreleased_end(self): + """Test replacement when latest_version_position is before unreleased_end.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=10, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "- New feature", + ] + + assert result == expected + + def test_no_unreleased_section_append_to_end(self): + """Test appending new content when no unreleased section exists.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "\n", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_no_unreleased_section_with_latest_version_position(self): + """Test inserting new content at latest_version_position when no unreleased section.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_empty_lines_list(self): + """Test behavior with empty lines list.""" + lines = [] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_single_line_unreleased_section(self): + """Test replacement of single line unreleased section.""" + lines = [ + "# Changelog", + "## Unreleased", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=1, + unreleased_end=1, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + ] + + assert result == expected + + def test_unreleased_section_at_end_of_file(self): + """Test replacement when unreleased section is at the end of file.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=10, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "- New feature", + ] + + assert result == expected + + def test_blank_line_handling_when_appending(self): + """Test that blank line is added when appending to non-empty content.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "\n", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_no_blank_line_when_content_ends_with_blank_line(self): + """Test that no extra blank line is added when content already ends with blank line.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_empty_new_content(self): + """Test behavior with empty new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "", + "\n", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_multiline_new_content(self): + """Test behavior with multiline new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Feature 1\n- Feature 2\n\n### Fixed\n- Bug fix" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- Feature 1\n- Feature 2\n\n### Fixed\n- Bug fix", + "\n", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_metadata_with_none_values(self): + """Test behavior when metadata has None values for unreleased positions.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_skip_behavior_during_unreleased_section(self): + """Test that lines are properly skipped during unreleased section processing.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- Old feature 1", + "- Old feature 2", + "", + "### Fixed", + "- Old bug fix", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=9, + latest_version="1.0.0", + latest_version_position=11, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_latest_version_position_at_unreleased_end(self): + """Test behavior when latest_version_position equals unreleased_end.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=5, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_latest_version_position_before_unreleased_start(self): + """Test behavior when latest_version_position is before unreleased_start.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=9, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "### Added", + "- New feature", + ] + + assert result == expected From 90e5ee7139174def05a3ccdf2dc6fb93a3ba4c41 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 17 Nov 2025 22:42:56 +0800 Subject: [PATCH 234/275] docs(Bump): wording consistency --- docs/commands/bump.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 47e64dfc6e..510fb4619c 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -9,7 +9,7 @@ - **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump - **Manual Version Control**: Supports manual version specification when needed - **Pre-release Support**: Handles alpha, beta, and release candidate versions -- **Multiple Version Schemes**: Supports both [PEP 0440][pep440] and [semantic versioning][semver] formats +- **Multiple Version Schemes**: Supports both [PEP 440][pep440] and [semantic versioning][semver] formats ### Version Increment Rules @@ -23,7 +23,7 @@ The version follows the `MAJOR.MINOR.PATCH` format, with increments determined b ### Version Schemes -By default, Commitizen uses [PEP 0440][pep440] for version formatting. You can switch to semantic versioning using either: +By default, Commitizen uses [PEP 440][pep440] for version formatting. You can switch to semantic versioning using either: 1. Command line: ```sh From f32bec0e86def0d6beb60dd0bb04b2a3ecb5f662 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 17 Nov 2025 23:27:50 +0800 Subject: [PATCH 235/275] docs(bump): add example to version file --- docs/commands/bump.md | 47 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 510fb4619c..cee6c6db26 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -470,36 +470,65 @@ Supported variables: --- -### `version_files` \* +### `version_files` It is used to identify the files or glob patterns which should be updated with the new version. -It is also possible to provide a pattern for each file, separated by colons (`:`). Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping, regarding if the file is present or not in `version_files`. -\* Renamed from `files` to `version_files`. +You may specify the `version_files` in your `pyproject.toml`, `.cz.toml` or `cz.toml` configuration file. -Some examples +It is also possible to provide a pattern for each file, separated by a colon (e.g. `file:pattern`). See the below example for more details. -`pyproject.toml`, `.cz.toml` or `cz.toml` +#### Example Configuration ```toml title="pyproject.toml" [tool.commitizen] version_files = [ "src/__version__.py", "packages/*/pyproject.toml:version", - "setup.py:version", + "setup.json:version", ] ``` -In the example above, we can see the reference `"setup.py:version"`. -This means that it will find a file `setup.py` and will only make a change -in a line containing the `version` substring. +In the example configuration above, we can see the reference `"setup.json:version"`. + +This means that it will find a file `setup.json` and will only change the lines that contain the substring `"version"`. + +For example, if we have a file `setup.json` with the following content: + + + +```json title="setup.json" +{ + "name": "magictool", + "version": "1.2.3", + "dependencies": { + "lodash": "1.2.3" + } +} +``` + +After running `cz bump 2.0.0`, the file will be updated to: + +```diff title="setup.json" +{ + "name": "magictool", +- "version": "1.2.3", ++ "version": "2.0.0", + "dependencies": { + "lodash": "1.2.3" + } +} +``` !!! note Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. +!!! note + (Historical note) This option was renamed from `files` to `version_files`. + --- ### `bump_message` From cbbf6150ea32c7758a0aa97f965e19c8cb754268 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 19 Nov 2025 01:02:32 +0800 Subject: [PATCH 236/275] docs(bump): general documentation update --- docs/commands/bump.md | 477 +++++++++++++++++++++++------------------- docs/config.md | 2 +- 2 files changed, 268 insertions(+), 211 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index cee6c6db26..eb807f841c 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -2,9 +2,18 @@ ## About -`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. It analyzes your commits to determine the appropriate version increment according to semantic versioning principles. +`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. -### Key Features +It analyzes your commits to determine the appropriate version increment according to semantic versioning principles. + +!!! note + In the following documentation, the term "configuration file" refers to `pyproject.toml`, `.cz.toml` or other configuration files. + + We will use `pyproject.toml` as the configuration file throughout the documentation. + + See [Configuration file](../config.md#configuration-file) for more details. + +## Key Features - **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump - **Manual Version Control**: Supports manual version specification when needed @@ -21,7 +30,7 @@ The version follows the `MAJOR.MINOR.PATCH` format, with increments determined b | `MINOR` | New features | `feat` | | `PATCH` | Fixes and improvements | `fix`, `perf`, `refactor`| -### Version Schemes +### Version Schemes (`--version-scheme`) By default, Commitizen uses [PEP 440][pep440] for version formatting. You can switch to semantic versioning using either: @@ -36,6 +45,26 @@ cz bump --version-scheme semver version_scheme = "semver" ``` +Available options are: + +- `pep440`: [PEP 440][pep440] (**default** and recommended for Python projects) +- `semver`: [Semantic Versioning][semver] (recommended for non-Python projects) + +You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`. + +!!! note + [pep440][pep440] and [semver][semver] are quite similar, although their difference lies in + how the prereleases look. For example, `0.3.1a0` in pep440 is equivalent to `0.3.1-a0` in semver. + + The following table illustrates the difference between the two schemes: + + | Version Type | pep440 | semver | + |--------------|----------------|-----------------| + | Non-prerelease | `0.1.0` | `0.1.0` | + | Prerelease | `0.3.1a0` | `0.3.1-a0` | + | Devrelease | `0.1.1.dev1` | `0.1.1-dev1` | + | Dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | + ### PEP440 Version Examples Commitizen supports the [PEP 440][pep440] version format, which includes several version types. Here are examples of each: @@ -76,13 +105,13 @@ Commitizen supports the [PEP 440][pep440] version format, which includes several > **Note**: `post` releases (e.g., `1.0.0.post1`) are not currently supported. -## Usage +## Command line options ![cz bump --help](../images/cli_help/cz_bump___help.svg) ### `--files-only` -Bumps the version in the files defined in `version_files` without creating a commit and tag on the git repository, +Bumps the version in the files defined in [`version_files`](#version_files) without creating a commit and tag on the git repository, ```bash cz bump --files-only @@ -90,7 +119,7 @@ cz bump --files-only ### `--changelog` -Generate a **changelog** along with the new version and tag when bumping. +Generate a **changelog** along with the new version and tag when bumping. See [changelog](./changelog.md) for more details. ```bash cz bump --changelog @@ -122,16 +151,21 @@ by their precedence and showcase how a release might flow through a development ### `--increment-mode` -By default, `--increment-mode` is set to `linear`, which ensures that bumping pre-releases _maintains linearity_: -bumping of a pre-release with lower precedence than the current pre-release phase maintains the current phase of -higher precedence. For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will -continue to bump the "beta" phase. +#### `--increment-mode=linear` (default) + +Ensures that bumping pre-releases **maintains linearity**. -Setting `--increment-mode` to `exact` instructs `cz bump` to instead apply the -exact changes that have been specified with `--increment` or determined from the commit log. For example, -`--prerelease beta` will always result in a `b` tag, and `--increment PATCH` will always increase the patch component. +Bumping a pre-release with lower precedence than the current pre-release phase maintains the current phase of higher precedence. +For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will continue to bump the *beta* phase. -Below are some examples that illustrate the difference in behavior: +#### `--increment-mode=exact` + +Applies the exact changes that have been specified with `--increment` or determined from the commit log. +For example, `--prerelease beta` will always result in a `b` tag, and `--increment PATCH` will always increase the patch component. + +#### Examples + +The following table illustrates the difference in behavior between the two modes: | Increment | Pre-release | Start Version | `--increment-mode=linear` | `--increment-mode=exact` | |-----------|-------------|---------------|---------------------------|--------------------------| @@ -144,14 +178,13 @@ Below are some examples that illustrate the difference in behavior: ### `--check-consistency` -Check whether the versions defined in `version_files` and the version in Commitizen -configuration are consistent before bumping version. +Check whether the versions defined in [`version_files`](#version_files) and the version in Commitizen configuration are consistent before bumping version. ```bash cz bump --check-consistency ``` -For example, if we have `pyproject.toml` +For example, if we have the following configuration file `pyproject.toml`: ```toml title="pyproject.toml" [tool.commitizen] @@ -162,67 +195,94 @@ version_files = [ ] ``` -`src/__version__.py` - +and the following version files `src/__version__.py` and `setup.py`: ```python title="src/__version__.py" __version__ = "1.21.0" ``` -and `setup.py` - ```python title="setup.py" from setuptools import setup setup(..., version="1.0.5", ...) ``` -If `--check-consistency` is used, Commitizen will check whether the current version in `pyproject.toml` -exists in all version_files and find out it does not exist in `setup.py` and fails. -However, it will still update `pyproject.toml` and `src/__version__.py`. +When you run `cz bump --check-consistency`, Commitizen will verify that the current version in `pyproject.toml` (`1.21.0`) exists in all files listed in [`version_files`](#version_files). +In this example, it will detect that `setup.py` contains `1.0.5` instead of `1.21.0`, causing the bump to fail. + +!!! warning "Partial updates on failure" + If the consistency check fails, Commitizen may have already updated some files (like `pyproject.toml` and `src/__version__.py`) before detecting the inconsistency. + In this case, you'll need to restore the files to their previous state. + + To resolve this issue: -To fix it, you'll first `git checkout .` to reset to the status before trying to bump and update -the version in `setup.py` to `1.21.0` + 1. Restore the modified files to their previous state: + ```bash + git checkout . + ``` + + 2. Manually update the version in `setup.py` to match the version in `pyproject.toml`: + ```python title="setup.py" + from setuptools import setup + + setup(..., version="1.21.0", ...) + ``` + + 3. Run the bump command again: + ```bash + cz bump --check-consistency + ``` ### `--local-version` Bump the local portion of the version. -```bash -cz bump --local-version -``` - -For example, if we have `pyproject.toml` +For example, if we have the following configuration file `pyproject.toml`: ```toml title="pyproject.toml" [tool.commitizen] version = "5.3.5+0.1.0" ``` -If `--local-version` is used, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`. +When you run `cz bump --local-version`, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`. ### `--annotated-tag` -If `--annotated-tag` is used, Commitizen will create annotated tags. It is also available via configuration, in `pyproject.toml` or `.cz.toml`. +Create annotated tags. + +It is also available via configuration files. + +For example, in `pyproject.toml`: + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag = true +``` ### `--annotated-tag-message` -If `--annotated-tag-message` is used, Commitizen will create annotated tags with the given message. +Create annotated tags with the given message. + +It is also available via configuration files. + +For example, in `pyproject.toml`: + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag_message = "Annotated tag message" +``` ### `--changelog-to-stdout` -If `--changelog-to-stdout` is used, the incremental changelog generated by the bump -will be sent to the stdout, and any other message generated by the bump will be -sent to stderr. +Send the incremental changelog generated by `cz bump` to `stdout`. +Any other messages generated by `cz bump` will be sent to `stderr`. -If `--changelog` is not used with this command, it is still smart enough to -understand that the user wants to create a changelog. It is recommended to be -explicit and use `--changelog` (or the setting `update_changelog_on_bump`). +When this flag is used, `--changelog` is implied. +However, it is recommended to set `--changelog` (or the setting `update_changelog_on_bump`) explicitly when the option `--changelog-to-stdout` is used. -This command is useful to "transport" the newly created changelog. -It can be sent to an auditing system, or to create a GitHub Release. +#### Useful scenarios -Example: +This is useful to pipe the newly created changelog to another tool. For example, it can be sent to an auditing system, or to create a GitHub Release, etc. ```bash cz bump --changelog --changelog-to-stdout > body.md @@ -230,55 +290,64 @@ cz bump --changelog --changelog-to-stdout > body.md ### `--git-output-to-stderr` -If `--git-output-to-stderr` is used, git commands output is redirected to stderr. +If `--git-output-to-stderr` is used, git commands output is redirected to `stderr`. + +Useful when used with `--changelog-to-stdout` and piping the output to a file, -This command is useful when used with `--changelog-to-stdout` and piping the output to a file, -and you don't want the `git commit` output polluting the stdout. +For example, `git commit` output may pollute `stdout`, so it is recommended to use this flag when piping the output to a file. ### `--retry` -If you use tools like [pre-commit](https://pre-commit.com/), add this flag. -It will retry the commit if it fails the 1st time. +If you use tools like [pre-commit](https://pre-commit.com/), you can add this flag. +It will retry the commit if it fails the first time. Useful to combine with code formatters, like [Prettier](https://prettier.io/). ### `--major-version-zero` -A project in its initial development should have a major version zero, and even breaking changes -should not bump that major version from zero. This command ensures that behavior. +Breaking changes does not bump the major version number. -If `--major-version-zero` is used for projects that have a version number greater than zero it fails. -If used together with a manual version the command also fails. +Say you have a project with the version `0.1.x` and you commit a breaking change like this: -We recommend setting `major_version_zero = true` in your configuration file while a project -is in its initial development. Remove that configuration using a breaking-change commit to bump -your project's major version to `v1.0.0` once your project has reached maturity. +```text +fix(magic)!: fully deprecate whatever +``` -### `--version-scheme` +and you run -Choose the version format, options: `pep440`, `semver`. +```bash +cz bump --major-version-zero +``` -Default: `pep440` +Then the version of your project will be bumped to `0.2.0` instead of `1.0.0`. -Recommended for python: `pep440` +!!! note + A project in its initial development should have a major version zero, + and even breaking changes should not bump that major version from zero. This command ensures that behavior. -Recommended for other: `semver` + We recommend setting `major_version_zero = true` in your configuration file while a project + is in its initial development. Remove that configuration using a breaking-change commit to bump + your project's major version to `v1.0.0` once your project has reached maturity. -You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`. +!!! warning + This option is only compatible with projects that have major version number zero, `0.x.x` for example. -[pep440][pep440] and [semver][semver] are quite similar, their difference lies in -how the prereleases look. + It fails when used with projects that have a version number greater than zero like `1.x.x`. -| schemes | pep440 | semver | -| -------------- | -------------- | --------------- | -| non-prerelease | `0.1.0` | `0.1.0` | -| prerelease | `0.3.1a0` | `0.3.1-a0` | -| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | -| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | + If used together with a manual version, the command also fails. -Can I transition from one to the other? + ```bash + # This fails + cz bump 0.1.0 --major-version-zero + ``` -Yes, you shouldn't have any issues. +### `--gpg-sign` + +Create gpg signed tags. + +```bash +cz bump --gpg-sign +``` ### `--template` @@ -304,15 +373,23 @@ cz bump --build-metadata yourmetadata ``` Will create a version like `1.1.2+yourmetadata`. + This can be useful for multiple things + - Git hash in version - Labeling the version with additional metadata. -Note that Commitizen ignores everything after `+` when it bumps the version. It is therefore safe to write different build-metadata between versions. +!!! note + Commitizen ignores everything after `+` when it bumps the version. + + It is therefore safe to write different build-metadata between versions. + + +!!! warning + Normally, you should not use this functionality, but if you decide to do so, keep in mind that: -You should normally not use this functionality, but if you decide to do, keep in mind that -- Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. -- It might be problematic having the metadata in place when doing upgrades depending on what tool you use. + - Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. + - It might be problematic having the metadata in place when doing upgrades depending on what tool you use. ### `--get-next` @@ -323,33 +400,33 @@ and `manual version`. cz bump --get-next ``` -Will output the next version, e.g., `1.2.3`. This can be useful for determining the next version based on CI for non -production environments/builds. +Will only output the next version, e.g., `1.2.3`. This can be useful for determining the next version based on CI for non-production environments/builds. -This behavior differs from the `--dry-run` flag. The `--dry-run` flag provides a more detailed output and can also show -the changes as they would appear in the changelog file. +!!! note "Compare with `--dry-run`" + `--dry-run` provides a more detailed output including the changes as they would appear in the changelog file, while `--get-next` only outputs the next version. -The following output is the result of `cz bump --dry-run`: + The following is the output of `cz bump --dry-run`: -``` -bump: version 3.28.0 → 3.29.0 -tag to create: v3.29.0 -increment detected: MINOR -``` + ```text + bump: version 3.28.0 → 3.29.0 + tag to create: v3.29.0 + increment detected: MINOR + ``` -The following output is the result of `cz bump --get-next`: + The following is the output of `cz bump --get-next`: -``` -3.29.0 -``` + ```text + 3.29.0 + ``` -The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump. +!!! warning + The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump. -For information on how to suppress this exit, see [avoid raising errors](#avoid-raising-errors). + For information on how to suppress this exit, see [avoid raising errors](#avoid-raising-errors). ### `--allow-no-commit` -Allow the project version to be bumped even when there's no eligible version. This is most useful when used with `--increment {MAJOR,MINOR,PATCH}` or `[MANUL_VERSION]` +Allow the project version to be bumped even when there's no eligible version. This is most useful when used with `--increment {MAJOR,MINOR,PATCH}` or `[MANUAL_VERSION]` ```sh # bump a minor version even when there's only bug fixes, documentation changes or even no commits @@ -359,74 +436,15 @@ cz bump --incremental MINOR --allow-no-commit cz bump --allow-no-commit 2.0.0 ``` -## Avoid raising errors - -Some situations from Commitizen raise an exit code different from 0. -If the error code is different from 0, any CI or script running Commitizen might be interrupted. - -If you have a special use case, where you don't want to raise one of this error codes, you can -tell Commitizen to not raise them. - -### Recommended use case - -At the moment, we've identified that the most common error code to skip is - -| Error name | Exit code | -| ----------------- | --------- | -| NoneIncrementExit | 21 | - -There are some situations where you don't want to get an error code when some -commits do not match your rules, you just want those commits to be skipped. - -```sh -cz -nr 21 bump -``` - -### Easy way - -Check which error code was raised by Commitizen by running in the terminal - -```sh -echo $? -``` - -The output should be an integer like this - -```sh -3 -``` - -And then you can tell Commitizen to ignore it: - -```sh -cz --no-raise 3 -``` - -You can tell Commitizen to skip more than one if needed: - -```sh -cz --no-raise 3,4,5 -``` - -### Longer way - -Check the list of [exit_codes](../exit_codes.md) and understand which one you have -to skip and why. +### `--version-scheme` -Remember to document somewhere this, because you'll forget. +See [Version Schemes](#version-schemes-version-scheme). -For example if the system raises a `NoneIncrementExit` error, you look it up -on the list, and then you can use the exit code: - -```sh -cz -nr 21 bump -``` - -## Configuration +## Configuration file options ### `tag_format` -`tag_format` and `version_scheme` are combined to make Git tag names from versions. +`tag_format` and [`version_scheme`](#version-schemes-version-scheme) are combined to make Git tag names from versions. These are used in: @@ -438,7 +456,7 @@ These are used in: - If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix) - If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) -Commitizen supports 2 types of formats, a simple and a more complex. +Commitizen supports two types of formats, a simple and a more complex. ```bash cz bump --tag-format="v$version" @@ -448,7 +466,7 @@ cz bump --tag-format="v$version" cz bump --tag-format="v$minor.$major.$patch$prerelease.$devrelease" ``` -In your `pyproject.toml` or `.cz.toml` +In your configuration file: ```toml [tool.commitizen] @@ -461,27 +479,42 @@ Supported variables: | Variable | Description | |--------------------------------|---------------------------------------------| -| `$version`, `${version}` | full generated version | +| `$version`, `${version}` | fully generated version | | `$major`, `${major}` | MAJOR increment | | `$minor`, `${minor}` | MINOR increment | | `$patch`, `${patch}` | PATCH increment | | `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) | | `$devrelease`, ${devrelease}` | Development release | ---- - ### `version_files` -It is used to identify the files or glob patterns which should be updated with the new version. +Identify the files or glob patterns which should be updated with the new version. -Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping, +Commitizen will update its configuration file automatically when bumping, regarding if the file is present or not in `version_files`. -You may specify the `version_files` in your `pyproject.toml`, `.cz.toml` or `cz.toml` configuration file. +You may specify the `version_files` in your configuration file. + +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "src/__version__.py", +] +``` It is also possible to provide a pattern for each file, separated by a colon (e.g. `file:pattern`). See the below example for more details. -#### Example Configuration +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "packages/*/pyproject.toml:version", + "setup.json:version", +] +``` + +#### Example scenario + +We have a project with the following configuration file `pyproject.toml`: ```toml title="pyproject.toml" [tool.commitizen] @@ -492,11 +525,9 @@ version_files = [ ] ``` -In the example configuration above, we can see the reference `"setup.json:version"`. +For the reference `"setup.json:version"`, it means that it will look for a file `setup.json` and will only change the lines that contain the substring `"version"`. -This means that it will find a file `setup.json` and will only change the lines that contain the substring `"version"`. - -For example, if we have a file `setup.json` with the following content: +For example, if the content of `setup.json` is: @@ -510,7 +541,7 @@ For example, if we have a file `setup.json` with the following content: } ``` -After running `cz bump 2.0.0`, the file will be updated to: +After running `cz bump 2.0.0`, its content will be updated to: ```diff title="setup.json" { @@ -526,10 +557,8 @@ After running `cz bump 2.0.0`, the file will be updated to: !!! note Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. -!!! note - (Historical note) This option was renamed from `files` to `version_files`. - ---- +!!! note "Historical note" + This option was renamed from `files` to `version_files`. ### `bump_message` @@ -542,69 +571,52 @@ Defaults to: `bump: version $current_version → $new_version` | `$current_version` | the version existing before bumping | | `$new_version` | version generated after bumping | -Some examples - -`pyproject.toml`, `.cz.toml` or `cz.toml` +#### Example configuration ```toml title="pyproject.toml" [tool.commitizen] bump_message = "release $current_version → $new_version [skip-ci]" ``` ---- - ### `update_changelog_on_bump` -When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time. - -Defaults to: `false` +When set to `true`, `cz bump` is equivalent to `cz bump --changelog`. ```toml title="pyproject.toml" [tool.commitizen] update_changelog_on_bump = true ``` ---- - ### `annotated_tag` -When set to `true`, Commitizen will create annotated tags. +When set to `true`, `cz bump` is equivalent to `cz bump --annotated-tag`. ```toml title="pyproject.toml" [tool.commitizen] annotated_tag = true ``` ---- - ### `gpg_sign` -When set to `true`, Commitizen will create gpg signed tags. +When set to `true`, `cz bump` is equivalent to `cz bump --gpg-sign`. See [--gpg-sign](#-gpg-sign). ```toml title="pyproject.toml" [tool.commitizen] gpg_sign = true ``` ---- - ### `major_version_zero` -When set to `true`, Commitizen will keep the major version at zero. -Useful during the initial development stage of your project. - -Defaults to: `false` +When set to `true`, `cz bump` is equivalent to `cz bump --major-version-zero`. See [--major-version-zero](#-major-version-zero). ```toml title="pyproject.toml" [tool.commitizen] major_version_zero = true ``` ---- - ### `pre_bump_hooks` -A list of optional commands that will run right _after_ updating `version_files` +A list of optional commands that will run right _after_ updating [`version_files`](#version_files) and _before_ actual committing and tagging the release. Useful when you need to generate documentation based on the new version. During @@ -628,8 +640,6 @@ pre_bump_hooks = [ ] ``` ---- - ### `post_bump_hooks` A list of optional commands that will run right _after_ committing and tagging the release. @@ -668,22 +678,69 @@ prerelease_offset = 1 ### `version_scheme` -Choose version scheme +See [Version Schemes](#version-schemes-version-scheme). -| schemes | pep440 | semver | semver2 | -| -------------- | -------------- | --------------- | --------------------- | -| non-prerelease | `0.1.0` | `0.1.0` | `0.1.0` | -| prerelease | `0.3.1a0` | `0.3.1-a0` | `0.3.1-alpha.0` | -| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | `0.1.1-dev.1` | -| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | `1.0.0-alpha.3.dev.1` | +## Avoid raising errors -Options: `pep440`, `semver`, `semver2` +Some situations from Commitizen raise an exit code different from 0. +If the error code is different from 0, any CI or script running Commitizen might be interrupted. -Defaults to: `pep440` +If you have a special use case, where you don't want to raise one of this error codes, you can +tell Commitizen to not raise them. -```toml title="pyproject.toml" -[tool.commitizen] -version_scheme = "semver" +### Recommended use case + +At the moment, we've identified that the most common error code to skip is + +| Error name | Exit code | +| ----------------- | --------- | +| NoneIncrementExit | 21 | + +There are some situations where you don't want to get an error code when some +commits do not match your rules, you just want those commits to be skipped. + +```sh +cz -nr 21 bump +``` + +### Easy way + +Check which error code was raised by Commitizen by running in the terminal + +```sh +echo $? +``` + +The output should be an integer like this + +```sh +3 +``` + +And then you can tell Commitizen to ignore it: + +```sh +cz --no-raise 3 +``` + +You can tell Commitizen to skip more than one if needed: + +```sh +cz --no-raise 3,4,5 +``` + +### Longer way + +Check the list of [exit_codes](../exit_codes.md) and understand which one you have +to skip and why. + +Remember to document somewhere this, because you'll forget. + +For example if the system raises a `NoneIncrementExit` error, you look it up +on the list, and then you can use the exit code: + +```sh +cz -nr 21 bump ``` ## Custom bump diff --git a/docs/config.md b/docs/config.md index 94aa2076b4..a3e3179887 100644 --- a/docs/config.md +++ b/docs/config.md @@ -408,7 +408,7 @@ setup( [prerelease-offset]: commands/bump.md#prerelease_offset [retry_after_failure]: commands/commit.md#retry [allow_abort]: commands/check.md#allow-abort -[version-scheme]: commands/bump.md#-version-scheme +[version-scheme]: commands/bump.md#version-schemes-version-scheme [pre_bump_hooks]: commands/bump.md#pre_bump_hooks [post_bump_hooks]: commands/bump.md#post_bump_hooks [allowed_prefixes]: commands/check.md#allowed-prefixes From 6fb02926251ce55e20255f257d0eb0b457ede031 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 01:05:04 +0000 Subject: [PATCH 237/275] ci(deps): bump dawidd6/action-homebrew-bump-formula from 5 to 6 Bumps [dawidd6/action-homebrew-bump-formula](https://github.com/dawidd6/action-homebrew-bump-formula) from 5 to 6. - [Release notes](https://github.com/dawidd6/action-homebrew-bump-formula/releases) - [Commits](https://github.com/dawidd6/action-homebrew-bump-formula/compare/v5...v6) --- updated-dependencies: - dependency-name: dawidd6/action-homebrew-bump-formula dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/homebrewpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index 8c315b6322..b603acd181 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -24,7 +24,7 @@ jobs: run: | echo "project_version=$(cz version --project)" >> $GITHUB_ENV - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@v5 + uses: dawidd6/action-homebrew-bump-formula@v6 with: token: ${{secrets.PERSONAL_ACCESS_TOKEN}} formula: commitizen From d037977a3939e50b95e88dce9d8fc8a34aa6e0fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 01:05:10 +0000 Subject: [PATCH 238/275] ci(deps): bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/bumpversion.yml | 2 +- .github/workflows/docspublish.yml | 4 ++-- .github/workflows/homebrewpublish.yml | 2 +- .github/workflows/label_pr.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- .github/workflows/pythonpublish.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bumpversion.yml b/.github/workflows/bumpversion.yml index ba48d677e0..d74ed624a7 100644 --- a/.github/workflows/bumpversion.yml +++ b/.github/workflows/bumpversion.yml @@ -12,7 +12,7 @@ jobs: name: "Bump version and create changelog with commitizen" steps: - name: Check out - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index eb52dc46a8..0102856cbc 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -10,7 +10,7 @@ jobs: update-cli-screenshots: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest needs: update-cli-screenshots steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index b603acd181..ca4f66c872 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -12,7 +12,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index c1a3df2c70..8e2d674f2b 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -9,7 +9,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: sparse-checkout: | .github/labeler.yml diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 7a574c9e0a..2c332391db 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -10,7 +10,7 @@ jobs: platform: [ubuntu-22.04, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index e70690a1a3..de078815fa 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -9,7 +9,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 From 84629b5831f5ba508b03ae02abf9d53d4dacd773 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 21 Nov 2025 10:31:21 +0800 Subject: [PATCH 239/275] docs(external_links): fix outdated link and add links to another commitizen talk --- docs/external_links.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/external_links.md b/docs/external_links.md index a90bbd085b..1b5efb05df 100644 --- a/docs/external_links.md +++ b/docs/external_links.md @@ -3,14 +3,15 @@ ## Talks | Name | Speaker | Occasion | Language | Extra | -| ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | -| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | -| Automating release cycles | Santiago Fraire | PyAmsterdam June 24, 2020, Online | English | [slides](https://woile.github.io/commitizen-presentation/) | -| [Automatizando Releases con Commitizen y Github Actions][automatizando] | Santiago Fraire | PyConAr 2020, Remote | Español | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/) | +| ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| [Atomic Commits: An Easy & Proven Way to Manage & Automate Release Process](https://www.youtube.com/watch?v=IxzN9ClXhs8) | Wei Lee | COSCUP 2023 | Taiwanese Mandarin | [slides](https://speakerdeck.com/leew/atomic-commits-an-easy-and-proven-way-to-manage-and-automate-release-process) | +| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | +| Automating release cycles | Santiago Fraire | PyAmsterdam June 24, 2020, Online | English | [slides](https://woile.github.io/commitizen-presentation/) | +| [Automatizando Releases con Commitizen y Github Actions][automatizando] | Santiago Fraire | PyConAr 2020, Remote | Español | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/) | ## Articles -- [Python Table Manners - Commitizen: 規格化 commit message](https://lee-w.github.io/posts/tech/2020/03/python-table-manners-commitizen/) (Written in Traditional Mandarin) +- [Python Table Manners - Commitizen: 規格化 commit message](https://blog.wei-lee.me/posts/tech/2020/03/python-table-manners-commitizen/) (Written in Traditional Mandarin) - [Automating semantic release with commitizen](https://woile.dev/posts/automating-semver-releases-with-commitizen/) (English) - [How to Write Better Git Commit Messages – A Step-By-Step Guide](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/?utm_source=tldrnewsletter) (English) - [Continuous delivery made easy (in Python)](https://medium.com/dev-genius/continuous-delivery-made-easy-in-python-c085e9c82e69) From 89564c694139a73f317860a9eae693cbc0f67d1f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 19 Nov 2025 13:09:43 +0800 Subject: [PATCH 240/275] docs(bump): check consistency warning minor update --- docs/commands/bump.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index eb807f841c..d819b38be0 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -222,10 +222,11 @@ In this example, it will detect that `setup.py` contains `1.0.5` instead of `1.2 ``` 2. Manually update the version in `setup.py` to match the version in `pyproject.toml`: - ```python title="setup.py" + ```diff title="setup.py" from setuptools import setup - setup(..., version="1.21.0", ...) + - setup(..., version="1.0.5", ...) + + setup(..., version="1.21.0", ...) ``` 3. Run the bump command again: From c92280fb82697559b626c47e575c00f4ec7e831c Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 19 Nov 2025 14:57:47 +0800 Subject: [PATCH 241/275] docs(bump): add missing --allow-no-commit behaviors and other minor documentation changes --- docs/commands/bump.md | 78 +++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index d819b38be0..ebd8b2774e 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -111,7 +111,7 @@ Commitizen supports the [PEP 440][pep440] version format, which includes several ### `--files-only` -Bumps the version in the files defined in [`version_files`](#version_files) without creating a commit and tag on the git repository, +Bumps the version in the files defined in [`version_files`](#version_files) without creating a commit and tag on the git repository. ```bash cz bump --files-only @@ -128,7 +128,7 @@ cz bump --changelog ### `--prerelease` The bump is a pre-release bump, meaning that in addition to a possible version bump the new version receives a -pre-release segment compatible with the bump’s version scheme, where the segment consist of a _phase_ and a +pre-release segment compatible with the bump's version scheme, where the segment consists of a _phase_ and a non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or `rc` (release candidate). For more details, refer to the [Python Packaging User Guide](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases). @@ -281,19 +281,20 @@ Any other messages generated by `cz bump` will be sent to `stderr`. When this flag is used, `--changelog` is implied. However, it is recommended to set `--changelog` (or the setting `update_changelog_on_bump`) explicitly when the option `--changelog-to-stdout` is used. -#### Useful scenarios +!!! note "Useful scenarios" + Pipe the newly created changelog to another tool. -This is useful to pipe the newly created changelog to another tool. For example, it can be sent to an auditing system, or to create a GitHub Release, etc. + The output can be redirected to an auditing system, or used to create a GitHub Release, etc. -```bash -cz bump --changelog --changelog-to-stdout > body.md -``` + ```bash + cz bump --changelog --changelog-to-stdout > body.md + ``` ### `--git-output-to-stderr` -If `--git-output-to-stderr` is used, git commands output is redirected to `stderr`. +Redirects git commands output to `stderr`. -Useful when used with `--changelog-to-stdout` and piping the output to a file, +Useful when used with `--changelog-to-stdout` and piping the output to a file. For example, `git commit` output may pollute `stdout`, so it is recommended to use this flag when piping the output to a file. @@ -306,7 +307,7 @@ Useful to combine with code formatters, like [Prettier](https://prettier.io/). ### `--major-version-zero` -Breaking changes does not bump the major version number. +Breaking changes do not bump the major version number. Say you have a project with the version `0.1.x` and you commit a breaking change like this: @@ -344,7 +345,7 @@ Then the version of your project will be bumped to `0.2.0` instead of `1.0.0`. ### `--gpg-sign` -Create gpg signed tags. +Creates gpg signed tags. ```bash cz bump --gpg-sign @@ -367,18 +368,16 @@ See [the template customization section](../customization.md#customizing-the-cha ### `--build-metadata` -Provides a way to specify additional metadata in the version string. This parameter is not compatible with `--local-version` as it uses the same part of the version string. +Specifies additional metadata in the version string. ```bash +# creates a version like `1.1.2+yourmetadata`. cz bump --build-metadata yourmetadata ``` -Will create a version like `1.1.2+yourmetadata`. - -This can be useful for multiple things - -- Git hash in version -- Labeling the version with additional metadata. +!!! note "Example usage" + - Git hash in version + - Labeling the version with additional metadata. !!! note Commitizen ignores everything after `+` when it bumps the version. @@ -392,16 +391,19 @@ This can be useful for multiple things - Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. - It might be problematic having the metadata in place when doing upgrades depending on what tool you use. +!!! warning + This parameter is not compatible with `--local-version` as it uses the same part of the version string. + ### `--get-next` -Provides a way to determine the next version and write it to stdout. This parameter is not compatible with `--changelog` -and `manual version`. +Similar to `--dry-run` but only outputs the next version. ```bash +# outputs 1.0.1 if the current version is 1.0.0 and the increment is PATCH cz bump --get-next ``` -Will only output the next version, e.g., `1.2.3`. This can be useful for determining the next version based on CI for non-production environments/builds. +Useful for determining the next version based on CI for non-production environments/builds. !!! note "Compare with `--dry-run`" `--dry-run` provides a more detailed output including the changes as they would appear in the changelog file, while `--get-next` only outputs the next version. @@ -427,15 +429,27 @@ Will only output the next version, e.g., `1.2.3`. This can be useful for determi ### `--allow-no-commit` -Allow the project version to be bumped even when there's no eligible version. This is most useful when used with `--increment {MAJOR,MINOR,PATCH}` or `[MANUAL_VERSION]` +Allow the project version to be bumped even when there's no eligible version. -```sh -# bump a minor version even when there's only bug fixes, documentation changes or even no commits -cz bump --incremental MINOR --allow-no-commit +!!! note "Example usage" + ```sh + # bump a minor version even when there's only bug fixes, documentation changes or even no commits -# bump version to 2.0.0 even when there's no breaking changes changes or even no commits -cz bump --allow-no-commit 2.0.0 -``` + cz bump --increment MINOR --allow-no-commit + + # bump version to 2.0.0 even when there's no breaking changes or even no commits + cz bump --allow-no-commit 2.0.0 + ``` + +!!! note "Default increment" + The increment is overridden to `PATCH` if there is no increment detected or specified. + + In other words, `cz bump --allow-no-commit` allows you to bump the version to the next patch version even when there is no eligible commit. + + ```sh + # will bump to `1.0.1` if the current version is `1.0.0`. + cz bump --allow-no-commit + ``` ### `--version-scheme` @@ -480,19 +494,19 @@ Supported variables: | Variable | Description | |--------------------------------|---------------------------------------------| -| `$version`, `${version}` | fully generated version | +| `$version`, `${version}` | fully generated version | | `$major`, `${major}` | MAJOR increment | | `$minor`, `${minor}` | MINOR increment | | `$patch`, `${patch}` | PATCH increment | | `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) | -| `$devrelease`, ${devrelease}` | Development release | +| `$devrelease`, `${devrelease}` | Development release | ### `version_files` Identify the files or glob patterns which should be updated with the new version. Commitizen will update its configuration file automatically when bumping, -regarding if the file is present or not in `version_files`. +regardless of whether the file is present or not in `version_files`. You may specify the `version_files` in your configuration file. @@ -686,7 +700,7 @@ See [Version Schemes](#version-schemes-version-scheme). Some situations from Commitizen raise an exit code different from 0. If the error code is different from 0, any CI or script running Commitizen might be interrupted. -If you have a special use case, where you don't want to raise one of this error codes, you can +If you have a special use case, where you don't want to raise one of these error codes, you can tell Commitizen to not raise them. ### Recommended use case From b565e4b45e5a82810797a5ab27dd01c3d23a0261 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 21 Nov 2025 01:21:19 +0800 Subject: [PATCH 242/275] docs(third-party): resturcture commitizen plugin documentation --- docs/customization.md | 2 +- docs/third-party-commitizen.md | 200 ------------------ docs/third-party-plugins/about.md | 73 +++++++ .../commitizen-deno-provider.md | 28 +++ docs/third-party-plugins/commitizen-emoji.md | 17 ++ docs/third-party-plugins/conventional-jira.md | 13 ++ docs/third-party-plugins/cz-ai.md | 17 ++ .../cz-conventional-gitmoji.md | 19 ++ docs/third-party-plugins/cz-emoji.md | 17 ++ docs/third-party-plugins/cz-legacy.md | 19 ++ docs/third-party-plugins/cz-path.md | 54 +++++ .../github-jira-conventional.md | 17 ++ mkdocs.yml | 13 +- 13 files changed, 287 insertions(+), 202 deletions(-) delete mode 100644 docs/third-party-commitizen.md create mode 100644 docs/third-party-plugins/about.md create mode 100644 docs/third-party-plugins/commitizen-deno-provider.md create mode 100644 docs/third-party-plugins/commitizen-emoji.md create mode 100644 docs/third-party-plugins/conventional-jira.md create mode 100644 docs/third-party-plugins/cz-ai.md create mode 100644 docs/third-party-plugins/cz-conventional-gitmoji.md create mode 100644 docs/third-party-plugins/cz-emoji.md create mode 100644 docs/third-party-plugins/cz-legacy.md create mode 100644 docs/third-party-plugins/cz-path.md create mode 100644 docs/third-party-plugins/github-jira-conventional.md diff --git a/docs/customization.md b/docs/customization.md index 99ffd39ba7..50bb0c8fbb 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -207,7 +207,7 @@ cookiecutter gh:commitizen-tools/commitizen_cz_template See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. -Once you publish your rules, you can send us a PR to the [Third-party section](./third-party-commitizen.md). +Once you publish your rules, you can send us a PR to the [Third-party section](./third-party-plugins/about.md). ### Custom commit rules diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md deleted file mode 100644 index 0c35a31557..0000000000 --- a/docs/third-party-commitizen.md +++ /dev/null @@ -1,200 +0,0 @@ -## Third-Party Commitizen Templates - -In addition to the native templates, some alternative commit format templates -are available as PyPI packages (installable with `pip`). - -### [cz-ai](https://github.com/watadarkstar/cz_ai) - -A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes. - -#### Installation - -```sh -pip install cz-ai -``` - -#### Usage - -```sh -cz --name cz_ai commit -``` - -### [Conventional JIRA](https://pypi.org/project/conventional-JIRA/) - -Just like _conventional commit_ format, but the scope has been restricted to a -JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a -meaningful way. - -#### Installation - -```sh -pip install conventional-JIRA -``` - -### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) - -This plugin extends the Commitizen tools by: - -- requiring a JIRA issue ID in the commit message -- creating links to GitHub commits in the CHANGELOG.md -- creating links to JIRA issues in the CHANGELOG.md - -#### Installation - -```sh -pip install cz-github-jira-conventional -``` - -For installation instructions (configuration and pre-commit) please visit [https://github.com/apheris/cz-github-jira-conventional](https://github.com/apheris/cz-github-jira-conventional) - -### [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji) - -_conventional commit_ format, but with emojis - -#### Installation - -```sh -pip install cz-emoji -``` - -#### Usage - -```sh -cz --name cz_emoji commit -``` - -### [cz-conventional-gitmoji](https://github.com/ljnsn/cz-conventional-gitmoji) - -*conventional commit*s, but with [gitmojis](https://gitmoji.dev). - -Includes a pre-commit hook that automatically adds the correct gitmoji to the commit message based on the conventional type. - -#### Installation - -```sh -pip install cz-conventional-gitmoji -``` - -#### Usage - -```sh -cz --name cz_gitmoji commit -``` - -### [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained) - -Just like _conventional commit_ format, but with emojis and optionally time spent and related tasks. - -#### Installation - -```sh -pip install commitizen-emoji -``` - -#### Usage - -```sh -cz --name cz_commitizen_emoji commit -``` - -### [Conventional Legacy (cz_legacy)][1] - -An extension of the _conventional commit_ format to include user-specified -legacy change types in the `CHANGELOG` while preventing the legacy change types -from being used in new commit messages - -#### Installation - -```sh -pip install cz_legacy -``` - -#### Usage - -See the [README][1] for instructions on configuration - -[1]: https://pypi.org/project/cz_legacy - -## Third-Party Commitizen Providers - -Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`). - -### [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/) - -A provider for **Deno** projects. The provider updates the version in deno.json and jsr.json files. - -#### Installation - -```sh -pip install commitizen-deno-provider -``` - -#### Usage - -Add `deno-provider` to your configuration file. - -Example for `.cz.yaml`: - -```yaml ---- -commitizen: - major_version_zero: true - name: cz_conventional_commits - tag_format: $version - update_changelog_on_bump: true - version_provider: deno-provider - version_scheme: semver -``` - -### [cz-path](https://pypi.org/project/cz-path/) - -Provides prefix choices for commit messages based on staged files (Git only). -For example, if the staged files are `component/z/a.ts` and `component/z/b.ts`, -the path prefix option will be `component/z` and commit message might look like: -`component/z/: description of changes`. If only one file is staged, the extension -is removed in the prefix. - -#### Installation - -```sh -pip install cz-path -``` - -#### Usage - -Add `cz-path` to your configuration file. - -Example for `.cz.json`: - -```json -{ - "commitizen": { - "name": "cz_path", - "remove_path_prefixes": ["src", "module_name"] - } -} -``` - -The default value for `remove_path_prefixes` is `["src"]`. Adding `/` to the -prefixes is not required. - -#### Example session - -```plain - $ git add .vscode/ - $ cz -n cz_path c -? Prefix: (Use arrow keys) - » .vscode - .vscode/ - project - (empty) -? Prefix: .vscode -? Commit title: adjust settings - -.vscode: adjust settings - -[main 0000000] .vscode: adjust settings - 2 files changed, 1 insertion(+), 11 deletions(-) - -Commit successful! -``` diff --git a/docs/third-party-plugins/about.md b/docs/third-party-plugins/about.md new file mode 100644 index 0000000000..9711386fc8 --- /dev/null +++ b/docs/third-party-plugins/about.md @@ -0,0 +1,73 @@ +# What are third-party plugins? + +Third-party plugins are a way to extend Commitizen with additional customized features. + +These plugins are available as PyPI packages (installable with `pip`). + +!!! note + New plugins are welcome! Once you published your plugins, please send us a PR to update this page. + +!!! note "Historical notes" + This section was originally called "Third-Party Commitizen Templates", but has been renamed to "Third-Party Commitizen Plugins" to better reflect the content. + +## Plugin features + + + + +### Commit message convention + +Includes the rules to validate and generate commit messages. + +### Version scheme + +Includes the rules to generate version numbers. + +### Version provider + +Read and write version from data sources. + +### Changelog format + +Generate changelog in customized formats. + + + + +## How to help us update the list of plugins? + +Please document what features the plugin provides: + +- a convention +- a scheme +- a provider +- a `changelog_format` + +Of course, a plugin can provide multiple features. +You may have noticed that `commitizen` itself can be viewed as a plugin that provides all the above features. + +Please see [cz-path](./cz-path.md) for a detailed example. + +## New plugin documentation template + + # [Package name](https://github.com/author/package-name) + + + + + + ## Installation + + ```sh + pip install package-name + ``` + + ## Usage + + ```sh + cz --name package-name commit + ``` + + ## Example + + diff --git a/docs/third-party-plugins/commitizen-deno-provider.md b/docs/third-party-plugins/commitizen-deno-provider.md new file mode 100644 index 0000000000..77f7d2e177 --- /dev/null +++ b/docs/third-party-plugins/commitizen-deno-provider.md @@ -0,0 +1,28 @@ +# [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/) + +A provider for **Deno** projects. The provider updates the version in `deno.json` and `jsr.json` files. + + + +## Installation + +```sh +pip install commitizen-deno-provider +``` + +## Usage + +Add `deno-provider` to your configuration file. + +Example for `.cz.yaml`: + +```yaml +--- +commitizen: + major_version_zero: true + name: cz_conventional_commits + tag_format: $version + update_changelog_on_bump: true + version_provider: deno-provider + version_scheme: semver +``` diff --git a/docs/third-party-plugins/commitizen-emoji.md b/docs/third-party-plugins/commitizen-emoji.md new file mode 100644 index 0000000000..e8e2654516 --- /dev/null +++ b/docs/third-party-plugins/commitizen-emoji.md @@ -0,0 +1,17 @@ +# [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained) + +Just like *conventional commit* format, but with emojis and optionally time spent and related tasks. + + + +## Installation + +```sh +pip install commitizen-emoji +``` + +## Usage + +```sh +cz --name cz_commitizen_emoji commit +``` diff --git a/docs/third-party-plugins/conventional-jira.md b/docs/third-party-plugins/conventional-jira.md new file mode 100644 index 0000000000..99505a2743 --- /dev/null +++ b/docs/third-party-plugins/conventional-jira.md @@ -0,0 +1,13 @@ +# [Conventional JIRA](https://pypi.org/project/conventional-JIRA/) + +Just like _conventional commit_ format, but the scope has been restricted to a +JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a +meaningful way. + + + +## Installation + +```sh +pip install conventional-JIRA +``` diff --git a/docs/third-party-plugins/cz-ai.md b/docs/third-party-plugins/cz-ai.md new file mode 100644 index 0000000000..e9ced907bf --- /dev/null +++ b/docs/third-party-plugins/cz-ai.md @@ -0,0 +1,17 @@ +# [cz-ai](https://github.com/watadarkstar/cz_ai) + +A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes. + + + +## Installation + +```sh +pip install cz-ai +``` + +## Usage + +```sh +cz --name cz_ai commit +``` diff --git a/docs/third-party-plugins/cz-conventional-gitmoji.md b/docs/third-party-plugins/cz-conventional-gitmoji.md new file mode 100644 index 0000000000..f4157105e7 --- /dev/null +++ b/docs/third-party-plugins/cz-conventional-gitmoji.md @@ -0,0 +1,19 @@ +# [cz-conventional-gitmoji](https://github.com/ljnsn/cz-conventional-gitmoji) + +*conventional commit*s, but with [gitmojis](https://gitmoji.dev). + +Includes a pre-commit hook that automatically adds the correct gitmoji to the commit message based on the conventional type. + + + +## Installation + +```sh +pip install cz-conventional-gitmoji +``` + +## Usage + +```sh +cz --name cz_gitmoji commit +``` diff --git a/docs/third-party-plugins/cz-emoji.md b/docs/third-party-plugins/cz-emoji.md new file mode 100644 index 0000000000..c638f4bfa1 --- /dev/null +++ b/docs/third-party-plugins/cz-emoji.md @@ -0,0 +1,17 @@ +# [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji) + +_conventional commit_ format, but with emojis. + + + +## Installation + +```sh +pip install cz-emoji +``` + +## Usage + +```sh +cz --name cz_emoji commit +``` diff --git a/docs/third-party-plugins/cz-legacy.md b/docs/third-party-plugins/cz-legacy.md new file mode 100644 index 0000000000..4f0a2e1221 --- /dev/null +++ b/docs/third-party-plugins/cz-legacy.md @@ -0,0 +1,19 @@ +# [Conventional Legacy (cz_legacy)][cz_legacy] + +An extension of the *conventional commit* format to include user-specified +legacy change types in the `CHANGELOG` while preventing the legacy change types +from being used in new commit messages. + + + +## Installation + +```sh +pip install cz_legacy +``` + +## Usage + +See the [Conventional Legacy README][cz_legacy] for instructions on configuration. + +[cz_legacy]: https://pypi.org/project/cz_legacy diff --git a/docs/third-party-plugins/cz-path.md b/docs/third-party-plugins/cz-path.md new file mode 100644 index 0000000000..7a2ba59176 --- /dev/null +++ b/docs/third-party-plugins/cz-path.md @@ -0,0 +1,54 @@ +# [cz-path](https://pypi.org/project/cz-path/) + +Provides prefix choices for commit messages based on staged files (Git only). +For example, if the staged files are `component/z/a.ts` and `component/z/b.ts`, +the path prefix option will be `component/z` and commit message might look like: +`component/z/: description of changes`. If only one file is staged, the extension +is removed in the prefix. + +This plugins provides a commitizen convention for commit messages. + +## Installation + +```sh +pip install cz-path +``` + +## Usage + +Add `cz-path` to your configuration file. + +Example for `.cz.json`: + +```json +{ + "commitizen": { + "name": "cz_path", + "remove_path_prefixes": ["src", "module_name"] + } +} +``` + +The default value for `remove_path_prefixes` is `["src"]`. Adding `/` to the +prefixes is not required. + +## Example session + +```plain + $ git add .vscode/ + $ cz -n cz_path c +? Prefix: (Use arrow keys) + » .vscode + .vscode/ + project + (empty) +? Prefix: .vscode +? Commit title: adjust settings + +.vscode: adjust settings + +[main 0000000] .vscode: adjust settings + 2 files changed, 1 insertion(+), 11 deletions(-) + +Commit successful! +``` diff --git a/docs/third-party-plugins/github-jira-conventional.md b/docs/third-party-plugins/github-jira-conventional.md new file mode 100644 index 0000000000..6032b8e7ff --- /dev/null +++ b/docs/third-party-plugins/github-jira-conventional.md @@ -0,0 +1,17 @@ +# [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) + +This plugin extends the Commitizen tools by: + +- requiring a JIRA issue ID in the commit message +- creating links to GitHub commits in `CHANGELOG.md` +- creating links to JIRA issues in `CHANGELOG.md` + + + +## Installation + +```sh +pip install cz-github-jira-conventional +``` + +For installation instructions (configuration and pre-commit), please visit the [GitHub repository](https://github.com/apheris/cz-github-jira-conventional). diff --git a/mkdocs.yml b/mkdocs.yml index dec8d3fd5d..49d76c30ad 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,7 +56,18 @@ nav: - Monorepo support: "tutorials/monorepo_guidance.md" - FAQ: "faq.md" - Exit Codes: "exit_codes.md" - - Third-Party Commitizen Templates: "third-party-commitizen.md" + - Third-Party Commitizen Plugins: + - About: "third-party-plugins/about.md" + # Please sort the plugins alphabetically + - "third-party-plugins/commitizen-deno-provider.md" + - "third-party-plugins/commitizen-emoji.md" + - "third-party-plugins/conventional-jira.md" + - "third-party-plugins/cz-ai.md" + - "third-party-plugins/cz-conventional-gitmoji.md" + - "third-party-plugins/cz-emoji.md" + - "third-party-plugins/cz-legacy.md" + - "third-party-plugins/cz-path.md" + - "third-party-plugins/github-jira-conventional.md" - Contributing: "contributing.md" - Contributing TL;DR: "contributing_tldr.md" - Resources: "external_links.md" From 52cb251726c36084471deec242181b4196f3a5ac Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 21 Nov 2025 13:41:51 +0800 Subject: [PATCH 243/275] docs(faq): minor updates in faq and make features wont add a separate page --- docs/faq.md | 46 +++++++++++++++++++-------------------- docs/features_wont_add.md | 7 ++++++ mkdocs.yml | 1 + 3 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 docs/features_wont_add.md diff --git a/docs/faq.md b/docs/faq.md index ee9d97068d..22cd9e8d6c 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,14 +1,8 @@ -## Features we won't add +This page contains frequently asked questions about Commitizen. -For a comprehensive list of features that have been considered but won't be implemented, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22). +## Support for [`PEP621`](https://peps.python.org/pep-0621/) -- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955) -- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790) -- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597) - -## Support for PEP621 - -PEP621 establishes a `[project]` definition inside `pyproject.toml` +`PEP621` establishes a `[project]` definition inside `pyproject.toml` ```toml [project] @@ -16,7 +10,7 @@ name = "spam" version = "2.5.1" ``` -Commitizen provides a [`pep621` version provider](config.md#version-providers) to get and set version from this field. +Commitizen provides a `PEP621` [version provider](config.md#version-providers) to get and set version from this field. You just need to set the proper `version_provider` setting: ```toml @@ -28,12 +22,14 @@ version = "2.5.1" version_provider = "pep621" ``` -## Why are `revert` and `chore` valid types in the check pattern of cz conventional_commits but not types we can select? +## Why are `revert` and `chore` valid types in the check pattern of `cz_conventional_commits` but not types we can select? -`revert` and `chore` are added to the "pattern" in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). -However, you can create a customized `cz` with those extra types. (See [Customization](customization.md)). +`revert` and `chore` are added to `pattern` in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). +However, you can create a customized `cz` with those extra types. See [Customization](customization.md) for more details. -See more discussion in issue [#142](https://github.com/commitizen-tools/commitizen/issues/142) and [#36](https://github.com/commitizen-tools/commitizen/issues/36) +See more discussion in +- [issue #142](https://github.com/commitizen-tools/commitizen/issues/142) +- [issue #36](https://github.com/commitizen-tools/commitizen/issues/36) ## How to revert a bump? @@ -47,23 +43,25 @@ git reset --hard HEAD This will remove the last tag created, plus the commit containing the update to `.cz.toml` and the changelog generated for the version. -In case the commit was pushed to the server you can remove it by running +In case the commit was pushed to the server, you can remove it by running: ```sh git push --delete origin ``` -## Is this project affiliated with the Commitizen JS project? +## Is this project affiliated with the [Commitizen JS][cz-js] project? -It is not affiliated. +**It is not affiliated.** Both are used for similar purposes, parsing commits, generating changelog and version we presume. This one is written in python to make integration easier for python projects and the other serves the JS packages. -They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with this repo (python's commitizen): + -- create custom rules, version bumps and changelog generation, by default we use the popular conventional commits (I think cz-js allows this). -- single package, install one thing and it will work (cz-js is a monorepo, but you have to install different dependencies AFAIK) +They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with our Commitizen: + +- create custom rules, version bumps and changelog generation. By default, we use the popular conventional commits (I think cz-js allows this). +- single package, install one thing and it will work. cz-js is a monorepo, but you have to install different dependencies as far as I know. - pre-commit integration - works on any language project, as long as you create the `.cz.toml` or `cz.toml` file. @@ -73,8 +71,6 @@ If you are using conventional commits in your git history, then you could swap o Regarding the name, [cz-js][cz-js] came first, they used the word Commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen". -[cz-js]: https://github.com/commitizen/cz-cli - ## How to handle revert commits? ```sh @@ -82,7 +78,7 @@ git revert --no-commit git commit -m "revert: foo bar" ``` -## Why don't we use Pydantic? +## Why don't we use [Pydantic](https://docs.pydantic.dev/)? While Pydantic is a powerful and popular library for data validation, we intentionally avoid using it in this project to keep our dependency tree minimal and maintainable. @@ -98,7 +94,7 @@ In short, avoiding Pydantic helps us: ## I got `Exception [WinError 995] The I/O operation ...` error -This error was caused by a Python bug on Windows. It's been fixed by [this PR](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue. +This error was caused by a Python bug on Windows. It's been fixed by [cpython #22017](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue. More discussion can be found in issue [#318](https://github.com/commitizen-tools/commitizen/issues/318). @@ -162,3 +158,5 @@ ignored_tag_formats = [ "v${major}.${minor}", ] ``` + +[cz-js]: https://github.com/commitizen/cz-cli diff --git a/docs/features_wont_add.md b/docs/features_wont_add.md new file mode 100644 index 0000000000..9fbd6d5b5c --- /dev/null +++ b/docs/features_wont_add.md @@ -0,0 +1,7 @@ +This page contains features that have been proposed or considered but won't be implemented in Commitizen. + +For a comprehensive list, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22). + +- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955) +- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790) +- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597) diff --git a/mkdocs.yml b/mkdocs.yml index 49d76c30ad..8a55bb23fa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,6 +55,7 @@ nav: - Developmental releases: "tutorials/dev_releases.md" - Monorepo support: "tutorials/monorepo_guidance.md" - FAQ: "faq.md" + - Features we won't add: "features_wont_add.md" - Exit Codes: "exit_codes.md" - Third-Party Commitizen Plugins: - About: "third-party-plugins/about.md" From 771b2172a6c4860c61d9871a0a9d51abcc6be874 Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Wed, 3 Dec 2025 16:13:20 +0800 Subject: [PATCH 244/275] docs(pyproject): update maintainers list (#1681) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 8211b969d0..bebf011187 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ { name = "Wei Lee", email = "weilee.rx@gmail.com" }, { name = "Axel H.", email = "noirbizarre@gmail.com" }, + { name = "Tim Hsiung", email = "bear890707@gmail.com" }, ] license = { file = "LICENSE" } readme = "docs/README.md" From 7c64b9189c17291f072ee9f73974642793457502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 01:05:20 +0000 Subject: [PATCH 245/275] ci(deps): bump dawidd6/action-homebrew-bump-formula from 6 to 7 Bumps [dawidd6/action-homebrew-bump-formula](https://github.com/dawidd6/action-homebrew-bump-formula) from 6 to 7. - [Release notes](https://github.com/dawidd6/action-homebrew-bump-formula/releases) - [Commits](https://github.com/dawidd6/action-homebrew-bump-formula/compare/v6...v7) --- updated-dependencies: - dependency-name: dawidd6/action-homebrew-bump-formula dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/homebrewpublish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index ca4f66c872..3ca67da811 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -24,7 +24,7 @@ jobs: run: | echo "project_version=$(cz version --project)" >> $GITHUB_ENV - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@v6 + uses: dawidd6/action-homebrew-bump-formula@v7 with: token: ${{secrets.PERSONAL_ACCESS_TOKEN}} formula: commitizen From 295d7a925e4ac21ab782befbded6cdfe33bb4eb1 Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Thu, 4 Dec 2025 11:23:27 +0800 Subject: [PATCH 246/275] docs(contributing): fix broken link (#1684) --- docs/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.md b/docs/contributing.md index 16db930626..9ba95f5ec5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,7 +4,7 @@ First, thank you for taking the time to contribute! 🎉 Your contributions help When contributing to Commitizen, we encourage you to: -1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](faq.md#features-we-wont-add) to see if there's already a discussion about the change you wish to make. +1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](features_wont_add.md) to see if there's already a discussion about the change you wish to make. 2. If there's no discussion, [create an issue](https://github.com/commitizen-tools/commitizen/issues/new) to discuss your proposed changes. 3. Follow our [development workflow](#development-workflow) and guidelines below. From 4ade72164c2489c80b90d8318f6827d913256c2a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 3 Dec 2025 22:45:43 +0800 Subject: [PATCH 247/275] docs(monorepo_guidance): fix wording and grammar issues, update structure --- docs/tutorials/monorepo_guidance.md | 120 +++++++++++++++------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md index 6f15a87247..f863a9e531 100644 --- a/docs/tutorials/monorepo_guidance.md +++ b/docs/tutorials/monorepo_guidance.md @@ -1,81 +1,87 @@ # Configuring Commitizen in a monorepo -This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each -other, it also assumes that conventional commits with scopes are in use. Some suggested layouts: - -```shell-session -. -├── library-b -│   └── .cz.toml -└── library-z - └── .cz.toml -``` - -```shell-session -src -├── library-b -│   └── .cz.toml -└── library-z - └── .cz.toml -``` - -Sample `.cz.toml` for each component: - -```toml -# library-b/.cz.toml -[tool.commitizen] -name = "cz_customize" -version = "0.0.0" -tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator -ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags -update_changelog_on_bump = true -``` - -```toml -# library-z/.cz.toml -[tool.commitizen] -name = "cz_customize" -version = "0.0.0" -tag_format = "${version}-library-z" -ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags -update_changelog_on_bump = true -``` - -And finally, to bump each of these: - -```sh -cz --config library-b/.cz.toml bump --yes -cz --config library-z/.cz.toml bump --yes -``` +This tutorial assumes that your monorepo is structured with multiple components that can be released independently of each other. +It also assumes that you are using conventional commits with scopes. + +Here is a step-by-step example using two libraries, `library-b` and `library-z`: + +1. **Organize your monorepo** + + For example, you might have one of these layouts: + + ```shell-session + . + ├── library-b + │   └── .cz.toml + └── library-z + └── .cz.toml + ``` + + ```shell-session + src + ├── library-b + │   └── .cz.toml + └── library-z + └── .cz.toml + ``` + +2. **Add a Commitizen configuration for each component** + + ```toml + # library-b/.cz.toml + [tool.commitizen] + name = "cz_customize" + version = "0.0.0" + tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator + ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags + update_changelog_on_bump = true + ``` + + ```toml + # library-z/.cz.toml + [tool.commitizen] + name = "cz_customize" + version = "0.0.0" + tag_format = "${version}-library-z" + ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags + update_changelog_on_bump = true + ``` + +3. **Bump each component independently** + + ```sh + cz --config library-b/.cz.toml bump --yes + cz --config library-z/.cz.toml bump --yes + ``` ## Changelog per component -In order to filter the correct commits for each component, you'll have to come up with a strategy. +To filter the correct commits for each component, you'll need to define a strategy. For example: -- Trigger the pipeline based on the changed path, which can have some downsides, as you'll rely on the developer not including files from other files - - [GitHub actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` +- Trigger the pipeline based on the changed path. This can have some downsides, as you'll rely on the developer not including files from unrelated components. + - [GitHub Actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` - [Jenkins](https://www.jenkins.io/doc/book/pipeline/syntax/#built-in-conditions) uses `changeset` - [GitLab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes` -- Filter certain pattern of the commit message (recommended) +- Filter commits by a specific pattern in the commit message (recommended) ### Example with scope in conventional commits -For this example, to include the message in the changelog, we will require commits to use a specific scope. -This way, only relevant commits will be included in the appropriate change log for a given component, and any other commit will be ignored. +In this example, we want `library-b`'s changelog to only include commits that use the `library-b` scope. +To achieve this, we configure Commitizen to match only commit messages with that scope. -Example config and commit for `library-b`: +Here is an example configuration for `library-b`: ```toml [tool.commitizen.customize] -changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include +changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" # the type pattern can be a wildcard or any types you wish to include ``` -A commit message looking like this, would be included: +With this configuration, a commit message like the following would be included in `library-b`'s changelog: -``` +```text fix(library-b): Some awesome message ``` From e241812a15c078e91bb019af2b95753fd50ff3b0 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 5 Dec 2025 00:06:38 +0800 Subject: [PATCH 248/275] docs(check): rewrite command document --- docs/commands/check.md | 151 ++++++++++++++++++++++++++++++----------- docs/config.md | 4 +- 2 files changed, 112 insertions(+), 43 deletions(-) diff --git a/docs/commands/check.md b/docs/commands/check.md index 320bffb881..ef7f8f5f90 100644 --- a/docs/commands/check.md +++ b/docs/commands/check.md @@ -1,95 +1,164 @@ -# Check +This feature checks whether a string or a range of git commits follows the given committing rules. Comments in git messages will be ignored. -## About - -This feature checks whether the commit message follows the given committing rules. Comments in git messages will be ignored. - -If you want to set up an automatic check before every git commit, please refer to -[Automatically check message before commit](../tutorials/auto_check.md). +To set up an automatic check before every git commit, please refer to [Automatically check message before commit](../tutorials/auto_check.md). ## Usage ![cz check --help](../images/cli_help/cz_check___help.svg) -There are three mutually exclusive ways to use `cz check`: +More specifically, there are three mutually exclusive ways to use `cz check`: + +- Validate a range of git commit messages with `--rev-range` +- Validate a given string with `--message` or by piping the message to it +- Validate a commit message from a file with `--commit-msg-file` + +### Use `cz check` to validate a commit message before committing + +#### Option 1: use `--message` to check a given string: + +```bash +cz check --message +``` + +#### Option 2: pipe the message to `cz check`: +```bash +echo | cz check +``` + +#### Option 3: use `--commit-msg-file` to read the commit message from a file +```bash +cz check --commit-msg-file /path/to/file.txt +``` -- with `--rev-range` to check a range of pre-existing commits -- with `--message` or by piping the message to it to check a given string -- or with `--commit-msg-file` to read the commit message from a file +## Command Line Options -### Git Rev Range +### `--rev-range` -If you'd like to check a commit's message after it has already been created, then you can specify the range of commits to check with `--rev-range REV_RANGE`. +Test if a given range of commits in the git log passes `cz check`. ```bash -$ cz check --rev-range REV_RANGE +cz check --rev-range REV_RANGE ``` -For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range ..HEAD`. +For more information on `REV_RANGE`, check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). + +#### Use cases + +1. Validate the latest 3 commit messages: + ```bash + cz check --rev-range HEAD~3..HEAD + # or + cz check --rev-range HEAD~3.. + # or + cz check --rev-range HEAD~~~.. + ``` -You can also use `--use-default-range` to check all commits from the default branch up to HEAD. This is equivalent to using `--rev-range ..HEAD`. +1. Validate all git commit messages on some branch up to HEAD: + ```bash + cz check --rev-range ..HEAD + ``` -For more information on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). + For example, to check all git commit messages on `main` branch up to HEAD: + ```bash + cz check --rev-range main..HEAD + ``` -### Commit Message + or if your project still uses `master` branch: + ```bash + cz check --rev-range master..HEAD + ``` -There are two ways you can provide your plain message and check it. + !!! note "Default branch" + Usually the default branch is `main` or `master`. + You can check the default branch by running `cz check --use-default-range`. -#### Method 1: use `-m` or `--message` +1. Validate all git commit messages starting from when you first implemented commit message linting: + + **(Why this is useful?)** Let's say you decided to enforce commit message today. However, it is impractical to `git rebase` all your previous commits. `--rev-range` helps you skip commits before you first implemented commit message linting by using a specific commit hash. + + ```bash + cz check --rev-range ..HEAD + ``` + +### `--use-default-range` + +Equivalent to `--rev-range ..HEAD`. ```bash -$ cz check --message MESSAGE +cz check --use-default-range +# or +cz check -d ``` -In this option, `MESSAGE` is the commit message to be checked. +### `--message` -#### Method 2: use pipe to pipe it to `cz check` +Test if a given string passes `cz check`. ```bash -$ echo MESSAGE | cz check +cz check --message ``` -In this option, `MESSAGE` is piped to `cz check` and will be checked. +### `--commit-msg-file` -### Commit Message File +Test if a given file contains a commit message that passes `cz check`. ```bash -$ cz check --commit-msg-file COMMIT_MSG_FILE +cz check --commit-msg-file ``` -In this option, `COMMIT_MSG_FILE` is the path of the temporary file that contains the commit message. -This argument can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hooks. +This can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more detailed examples. -### Allow Abort +### `--allow-abort` + +Example: ```bash -cz check --message MESSAGE --allow-abort +cz check --message --allow-abort ``` Empty commit messages typically instruct Git to abort a commit, so you can pass `--allow-abort` to permit them. Since `git commit` accepts the `--allow-empty-message` flag (primarily for wrapper scripts), you may wish to disallow such commits in CI. `--allow-abort` may be used in conjunction with any of the other options. -### Allowed Prefixes +### `--allowed-prefixes` + +Skip validation for commit messages that start with the specified prefixes. -If the commit message starts with some specific prefixes, `cz check` returns `True` without checking the regex. -By default, the following prefixes are allowed: +If not set, commit messages starting with the following prefixes are ignored by `cz check`: - `Merge` - `Revert` - `Pull request` - `fixup!` - `squash!` +- `amend!` ```bash -cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' +cz check --message --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' ``` -### Commit message length limit +For example, + +```bash +# The following message passes the check because it starts with 'Merge' +cz check --message "Merge branch 'main' into feature/new-feature" --allowed-prefixes 'Merge' -The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages. -For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESSAGE` is more than 3 characters long. -By default, the limit is set to 0, which means no limit on the length. +# The following fails +cz check --message "Merge branch 'main' into feature/new-feature" --allowed-prefixes 'aaa' +``` + +### `--message-length-limit` + +Restrict the length of **the first line** of the commit message. + +```bash +# The following passes +cz check --message "docs(check): fix grammar issues" -l 80 + +# The following fails +cz check --message "docs:very long long long long message with many words" -l 3 +``` -**Note that the limit applies only to the first line of the message.** +By default, the limit is set to `0`, which means no limit on the length. -Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, while the body and the footer are not counted. +!!! note + Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, while the body and the footer are not counted. diff --git a/docs/config.md b/docs/config.md index a3e3179887..307cf54a9f 100644 --- a/docs/config.md +++ b/docs/config.md @@ -407,11 +407,11 @@ setup( [major-version-zero]: commands/bump.md#-major-version-zero [prerelease-offset]: commands/bump.md#prerelease_offset [retry_after_failure]: commands/commit.md#retry -[allow_abort]: commands/check.md#allow-abort +[allow_abort]: commands/check.md#-allow-abort [version-scheme]: commands/bump.md#version-schemes-version-scheme [pre_bump_hooks]: commands/bump.md#pre_bump_hooks [post_bump_hooks]: commands/bump.md#post_bump_hooks -[allowed_prefixes]: commands/check.md#allowed-prefixes +[allowed_prefixes]: commands/check.md#-allowed-prefixes [additional-features]: https://github.com/tmbo/questionary#additional-features [customization]: customization.md [shortcuts]: customization.md#shortcut-keys From b1e74a5411e1a47bb5d6eadd85ae64cc3c755fa6 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sun, 16 Nov 2025 10:16:56 +0800 Subject: [PATCH 249/275] build(pyproject.toml): update dev group spec from poetry style to PEP 735 style This requires poetry-core >= 2.2.0 https://python-poetry.org/blog/announcing-poetry-2.2.0/ --- docs/contributing.md | 2 +- docs/contributing_tldr.md | 2 +- poetry.lock | 4 +-- pyproject.toml | 64 +++++++++++++++++++-------------------- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 9ba95f5ec5..741a13f213 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -16,7 +16,7 @@ If you're a first-time contributor, please check out issues labeled [good first 1. **Python Environment** - Python `>=3.9` - - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0` + - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.2.0` 2. **Version Control & Security** - Git - Commitizen diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md index 9eecc3efa0..7b040c2951 100644 --- a/docs/contributing_tldr.md +++ b/docs/contributing_tldr.md @@ -3,7 +3,7 @@ Feel free to send a PR to update this file if you find anything useful. 🙇 ## Environment - Python `>=3.9` -- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0` +- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.2.0` ## Useful commands diff --git a/poetry.lock b/poetry.lock index 49df9dcd52..4a60b32da5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "argcomplete" @@ -1955,4 +1955,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "cd5648d8aad7b58913b1c0e4cd4f04c98d5bcfa7e4ef8e7bb994a59492d7d4a2" +content-hash = "8f4edbdaff3ae48098899feacba7ea3764bdf2d960b78b8f5e8c39b19cc8c2e1" diff --git a/pyproject.toml b/pyproject.toml index bebf011187..7890edee2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,8 +85,38 @@ pep440 = "commitizen.version_schemes:Pep440" semver = "commitizen.version_schemes:SemVer" semver2 = "commitizen.version_schemes:SemVer2" +[dependency-groups] +dev = ["ipython>=8.0,<9.0", "tox>4", "poethepoet>=0.34.0,<1.0.0"] + +test = [ + "pytest>=7.2,<9.0", + "pytest-cov>=4,<7", + "pytest-mock>=3.10,<4.0", + "pytest-regressions>=2.4.0,<3.0.0", + "pytest-freezer>=0.4.6,<1.0.0", + "pytest-xdist>=3.1.0,<4.0.0", +] + +linters = [ + "ruff>=0.11.5,<1.0.0", + "pre-commit>=3.2.0,<5.0", + "mypy>=1.16.0,<2.0", + "types-deprecated>=1.2.9.2,<2.0", + "types-python-dateutil>=2.8.19.13,<3.0", + "types-PyYAML>=5.4.3,<7.0.0", + "types-termcolor>=0.1.1,<1.0", + "types-colorama>=0.4.15.20240311,<1.0", +] + +documentation = ["mkdocs>=1.4.2,<2.0", "mkdocs-material>=9.1.6,<10.0"] + +script = [ + # for scripts/gen_cli_help_screenshots.py + "rich>=13.7.1,<14.0", +] + [build-system] -requires = ["poetry-core>=2.0"] +requires = ["poetry-core>=2.2"] build-backend = "poetry.core.masonry.api" @@ -107,38 +137,6 @@ packages = [{ include = "commitizen" }, { include = "commitizen/py.typed" }] [tool.poetry.requires-plugins] "poethepoet" = ">=0.32.2" -[tool.poetry.group.dev.dependencies] -ipython = "^8.0" -tox = ">4" -poethepoet = "^0.34.0" - -[tool.poetry.group.test.dependencies] -pytest = ">=7.2,<9.0" -pytest-cov = ">=4,<7" -pytest-mock = "^3.10" -pytest-regressions = "^2.4.0" -pytest-freezer = "^0.4.6" -pytest-xdist = "^3.1.0" - -[tool.poetry.group.linters.dependencies] -ruff = "^0.11.5" -pre-commit = ">=3.2.0,<5.0" -mypy = "^1.16.0" -types-deprecated = "^1.2.9.2" -types-python-dateutil = "^2.8.19.13" -types-PyYAML = ">=5.4.3,<7.0.0" -types-termcolor = "^0.1.1" -types-colorama = "^0.4.15.20240311" - -[tool.poetry.group.documentation.dependencies] -mkdocs = "^1.4.2" -mkdocs-material = "^9.1.6" - -[tool.poetry.group.script.dependencies] -# for scripts/gen_cli_help_screenshots.py -rich = "^13.7.1" - - [tool.coverage] [tool.coverage.report] show_missing = true From fbee9418c58d566c431b4b1f4851c8305cbbf231 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sun, 16 Nov 2025 10:21:13 +0800 Subject: [PATCH 250/275] build(dev-dependencies): relax upper bound of dev dependencies Unless something is breaking. Wee could use the latest version of our dev dependencies for enhanced stability and features --- poetry.lock | 1281 +++++++++++++++++++++++++++--------------------- pyproject.toml | 34 +- 2 files changed, 727 insertions(+), 588 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4a60b32da5..80051bf4de 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,14 +2,14 @@ [[package]] name = "argcomplete" -version = "3.6.2" +version = "3.6.3" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, - {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, + {file = "argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce"}, + {file = "argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c"}, ] [package.extras] @@ -17,19 +17,19 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] [[package]] name = "asttokens" -version = "3.0.0" +version = "3.0.1" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, - {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, + {file = "asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a"}, + {file = "asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7"}, ] [package.extras] -astroid = ["astroid (>=2,<4)"] -test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] +astroid = ["astroid (>=2,<5)"] +test = ["astroid (>=2,<5)", "pytest (<9.0)", "pytest-cov", "pytest-xdist"] [[package]] name = "babel" @@ -48,19 +48,19 @@ dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)" [[package]] name = "backrefs" -version = "5.9" +version = "6.1" description = "A wrapper around re and regex that adds additional back references." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f"}, - {file = "backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf"}, - {file = "backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa"}, - {file = "backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b"}, - {file = "backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9"}, - {file = "backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60"}, - {file = "backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59"}, + {file = "backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1"}, + {file = "backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7"}, + {file = "backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a"}, + {file = "backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05"}, + {file = "backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853"}, + {file = "backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0"}, + {file = "backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231"}, ] [package.extras] @@ -68,26 +68,26 @@ extras = ["regex"] [[package]] name = "cachetools" -version = "6.2.0" +version = "6.2.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6"}, - {file = "cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32"}, + {file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"}, + {file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"}, ] [[package]] name = "certifi" -version = "2025.8.3" +version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["documentation"] files = [ - {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, - {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] @@ -116,91 +116,125 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.3" +version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, - {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, - {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, - {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, - {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, - {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, - {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, - {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, - {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, - {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] [[package]] @@ -232,100 +266,116 @@ files = [ [[package]] name = "coverage" -version = "7.10.6" +version = "7.10.7" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356"}, - {file = "coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301"}, - {file = "coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460"}, - {file = "coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd"}, - {file = "coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb"}, - {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6"}, - {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945"}, - {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e"}, - {file = "coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1"}, - {file = "coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528"}, - {file = "coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f"}, - {file = "coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc"}, - {file = "coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a"}, - {file = "coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a"}, - {file = "coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62"}, - {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153"}, - {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5"}, - {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619"}, - {file = "coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba"}, - {file = "coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e"}, - {file = "coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c"}, - {file = "coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea"}, - {file = "coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634"}, - {file = "coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6"}, - {file = "coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9"}, - {file = "coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c"}, - {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a"}, - {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5"}, - {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972"}, - {file = "coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d"}, - {file = "coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629"}, - {file = "coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80"}, - {file = "coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6"}, - {file = "coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80"}, - {file = "coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003"}, - {file = "coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27"}, - {file = "coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4"}, - {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d"}, - {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc"}, - {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc"}, - {file = "coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e"}, - {file = "coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32"}, - {file = "coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2"}, - {file = "coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b"}, - {file = "coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393"}, - {file = "coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27"}, - {file = "coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df"}, - {file = "coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb"}, - {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282"}, - {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4"}, - {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21"}, - {file = "coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0"}, - {file = "coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5"}, - {file = "coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b"}, - {file = "coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e"}, - {file = "coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb"}, - {file = "coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034"}, - {file = "coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1"}, - {file = "coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a"}, - {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb"}, - {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d"}, - {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747"}, - {file = "coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5"}, - {file = "coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713"}, - {file = "coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32"}, - {file = "coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65"}, - {file = "coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6"}, - {file = "coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0"}, - {file = "coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e"}, - {file = "coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5"}, - {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7"}, - {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5"}, - {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0"}, - {file = "coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7"}, - {file = "coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930"}, - {file = "coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b"}, - {file = "coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352"}, - {file = "coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612"}, - {file = "coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b"}, - {file = "coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144"}, - {file = "coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b"}, - {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862"}, - {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2"}, - {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78"}, - {file = "coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c"}, - {file = "coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf"}, - {file = "coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3"}, - {file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"}, + {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"}, + {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"}, + {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"}, + {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"}, + {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"}, + {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"}, + {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"}, + {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"}, + {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"}, + {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"}, + {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"}, + {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"}, + {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"}, + {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"}, + {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"}, + {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"}, + {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"}, + {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"}, + {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"}, + {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"}, + {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"}, + {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"}, + {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"}, + {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"}, + {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"}, + {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, ] [package.dependencies] @@ -360,18 +410,18 @@ files = [ [[package]] name = "deprecated" -version = "1.2.18" +version = "1.3.1" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" groups = ["main"] files = [ - {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, - {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, + {file = "deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f"}, + {file = "deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223"}, ] [package.dependencies] -wrapt = ">=1.10,<2" +wrapt = ">=1.10,<3" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] @@ -409,14 +459,14 @@ test = ["pytest (>=6)"] [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [package.extras] @@ -424,14 +474,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "executing" -version = "2.2.0" +version = "2.2.1" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, - {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, + {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, + {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, ] [package.extras] @@ -484,14 +534,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.13" +version = "2.6.15" description = "File identification library for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b"}, - {file = "identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32"}, + {file = "identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757"}, + {file = "identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf"}, ] [package.extras] @@ -499,14 +549,14 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -627,14 +677,14 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" -version = "3.8.2" +version = "3.9" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, - {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, + {file = "markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280"}, + {file = "markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a"}, ] [package.dependencies] @@ -671,90 +721,121 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main", "documentation"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] name = "matplotlib-inline" -version = "0.1.7" +version = "0.2.1" description = "Inline Matplotlib backend for Jupyter" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, + {file = "matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76"}, + {file = "matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe"}, ] [package.dependencies] traitlets = "*" +[package.extras] +test = ["flake8", "nbdime", "nbval", "notebook", "pytest"] + [[package]] name = "mdurl" version = "0.1.2" @@ -831,33 +912,32 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.6.18" +version = "9.7.0" description = "Documentation that simply works" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "mkdocs_material-9.6.18-py3-none-any.whl", hash = "sha256:dbc1e146a0ecce951a4d84f97b816a54936cdc9e1edd1667fc6868878ac06701"}, - {file = "mkdocs_material-9.6.18.tar.gz", hash = "sha256:a2eb253bcc8b66f8c6eaf8379c10ed6e9644090c2e2e9d0971c7722dc7211c05"}, + {file = "mkdocs_material-9.7.0-py3-none-any.whl", hash = "sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887"}, + {file = "mkdocs_material-9.7.0.tar.gz", hash = "sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec"}, ] [package.dependencies] -babel = ">=2.10,<3.0" -backrefs = ">=5.7.post1,<6.0" -click = "<8.2.2" -colorama = ">=0.4,<1.0" -jinja2 = ">=3.1,<4.0" -markdown = ">=3.2,<4.0" -mkdocs = ">=1.6,<2.0" -mkdocs-material-extensions = ">=1.3,<2.0" -paginate = ">=0.5,<1.0" -pygments = ">=2.16,<3.0" -pymdown-extensions = ">=10.2,<11.0" -requests = ">=2.26,<3.0" +babel = ">=2.10" +backrefs = ">=5.7.post1" +colorama = ">=0.4" +jinja2 = ">=3.1" +markdown = ">=3.2" +mkdocs = ">=1.6" +mkdocs-material-extensions = ">=1.3" +paginate = ">=0.5" +pygments = ">=2.16" +pymdown-extensions = ">=10.2" +requests = ">=2.26" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<12.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] @@ -874,50 +954,50 @@ files = [ [[package]] name = "mypy" -version = "1.17.1" +version = "1.18.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"}, - {file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"}, - {file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"}, - {file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"}, - {file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"}, - {file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"}, - {file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"}, - {file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"}, - {file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"}, - {file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"}, - {file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"}, - {file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"}, - {file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"}, - {file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"}, - {file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"}, - {file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"}, - {file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"}, - {file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"}, - {file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"}, - {file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"}, - {file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"}, - {file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"}, - {file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"}, - {file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"}, - {file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"}, - {file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"}, - {file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"}, - {file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"}, - {file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"}, - {file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"}, - {file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"}, - {file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"}, - {file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"}, - {file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"}, - {file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"}, - {file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"}, - {file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"}, - {file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"}, + {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, + {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, + {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, + {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, + {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, + {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, + {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, + {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, + {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, + {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, + {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, + {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, + {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, + {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, + {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, + {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, + {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, + {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, + {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, + {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, + {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, + {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, + {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, + {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, + {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, + {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, + {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, + {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, + {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, + {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, + {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, + {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, + {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, + {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, + {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, + {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, + {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, + {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, ] [package.dependencies] @@ -1076,14 +1156,14 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.34.0" -description = "A task runner that works well with poetry." +version = "0.37.0" +description = "A task runner that works well with poetry and uv." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e"}, - {file = "poethepoet-0.34.0.tar.gz", hash = "sha256:86203acce555bbfe45cb6ccac61ba8b16a5784264484195874da457ddabf5850"}, + {file = "poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22"}, + {file = "poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d"}, ] [package.dependencies] @@ -1173,14 +1253,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.16.1" +version = "10.17.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d"}, - {file = "pymdown_extensions-10.16.1.tar.gz", hash = "sha256:aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91"}, + {file = "pymdown_extensions-10.17.1-py3-none-any.whl", hash = "sha256:1f160209c82eecbb5d8a0d8f89a4d9bd6bdcbde9a8537761844cfc57ad5cd8a6"}, + {file = "pymdown_extensions-10.17.1.tar.gz", hash = "sha256:60d05fe55e7fb5a1e4740fc575facad20dc6ee3a748e8d3d36ba44142e75ce03"}, ] [package.dependencies] @@ -1212,14 +1292,14 @@ testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "p [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, - {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, ] [package.dependencies] @@ -1236,23 +1316,23 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-cov" -version = "6.2.1" +version = "7.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, - {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, + {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, + {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] -coverage = {version = ">=7.5", extras = ["toml"]} +coverage = {version = ">=7.10.6", extras = ["toml"]} pluggy = ">=1.2" -pytest = ">=6.2.5" +pytest = ">=7" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] +testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-datadir" @@ -1291,14 +1371,14 @@ pytest = ">=3.6" [[package]] name = "pytest-mock" -version = "3.14.1" +version = "3.15.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, - {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, + {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"}, + {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"}, ] [package.dependencies] @@ -1309,14 +1389,14 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-regressions" -version = "2.8.2" +version = "2.8.3" description = "Easy to use fixtures to write regression tests." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_regressions-2.8.2-py3-none-any.whl", hash = "sha256:a0804c1ce66d8e4d9a3c7c68f42a3d436182edca8e86565c232caeaf9e080fc2"}, - {file = "pytest_regressions-2.8.2.tar.gz", hash = "sha256:1d8f4767be58b9994bfa7d60271099469ad32b8ca9f9d9ceca1c1d6827156b19"}, + {file = "pytest_regressions-2.8.3-py3-none-any.whl", hash = "sha256:72500dd95bde418c850f290a3108dacb56427067f364f7112cb5b16f6d6cc29c"}, + {file = "pytest_regressions-2.8.3.tar.gz", hash = "sha256:1ad90708bee02a3d36c78ef0b6f9692a9a30d312dd828680fd6d2a7235fcd221"}, ] [package.dependencies] @@ -1368,65 +1448,85 @@ six = ">=1.5" [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main", "dev", "documentation", "linters", "test"] files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] @@ -1483,50 +1583,50 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.9.4" +version = "14.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["script"] files = [ - {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, - {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.11.13" +version = "0.14.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["linters"] files = [ - {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"}, - {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"}, - {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"}, - {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"}, - {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"}, - {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"}, - {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"}, - {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"}, - {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"}, - {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"}, - {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"}, - {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"}, + {file = "ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594"}, + {file = "ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72"}, + {file = "ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367"}, + {file = "ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b"}, + {file = "ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621"}, + {file = "ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4"}, + {file = "ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1"}, ] [[package]] @@ -1578,44 +1678,54 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "tomli" -version = "2.2.1" +version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev", "linters", "test"] files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, ] markers = {dev = "python_version < \"3.11\"", linters = "python_version < \"3.11\"", test = "python_full_version <= \"3.11.0a6\""} @@ -1633,14 +1743,14 @@ files = [ [[package]] name = "tox" -version = "4.29.0" +version = "4.30.3" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tox-4.29.0-py3-none-any.whl", hash = "sha256:b914f134176cea74c5e01c29cb4befc8afa4cd38b356c3756eff85832d27b5c0"}, - {file = "tox-4.29.0.tar.gz", hash = "sha256:7b3a2bb43974285110eee35a859f2b3f2e87a24f6e1011d83f466b7c75835bd2"}, + {file = "tox-4.30.3-py3-none-any.whl", hash = "sha256:a9f17b4b2d0f74fe0d76207236925a119095011e5c2e661a133115a8061178c9"}, + {file = "tox-4.30.3.tar.gz", hash = "sha256:f3dd0735f1cd4e8fbea5a3661b77f517456b5f0031a6256432533900e34b90bf"}, ] [package.dependencies] @@ -1686,50 +1796,50 @@ files = [ [[package]] name = "types-deprecated" -version = "1.2.15.20250304" +version = "1.3.1.20251101" description = "Typing stubs for Deprecated" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_deprecated-1.2.15.20250304-py3-none-any.whl", hash = "sha256:86a65aa550ea8acf49f27e226b8953288cd851de887970fbbdf2239c116c3107"}, - {file = "types_deprecated-1.2.15.20250304.tar.gz", hash = "sha256:c329030553029de5cc6cb30f269c11f4e00e598c4241290179f63cda7d33f719"}, + {file = "types_deprecated-1.3.1.20251101-py3-none-any.whl", hash = "sha256:274edcc2a084d3fe31802d3c1379abd630716d3db34e40577e12ad84d6b73134"}, + {file = "types_deprecated-1.3.1.20251101.tar.gz", hash = "sha256:f002d266b73201f46ec6fc712c1f016067ec6cb44357559cdb50c86b010951a7"}, ] [[package]] name = "types-python-dateutil" -version = "2.9.0.20250822" +version = "2.9.0.20251115" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_python_dateutil-2.9.0.20250822-py3-none-any.whl", hash = "sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc"}, - {file = "types_python_dateutil-2.9.0.20250822.tar.gz", hash = "sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53"}, + {file = "types_python_dateutil-2.9.0.20251115-py3-none-any.whl", hash = "sha256:9cf9c1c582019753b8639a081deefd7e044b9fa36bd8217f565c6c4e36ee0624"}, + {file = "types_python_dateutil-2.9.0.20251115.tar.gz", hash = "sha256:8a47f2c3920f52a994056b8786309b43143faa5a64d4cbb2722d6addabdf1a58"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20250822" +version = "6.0.12.20250915" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098"}, - {file = "types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413"}, + {file = "types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6"}, + {file = "types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3"}, ] [[package]] name = "types-termcolor" -version = "0.1.1" +version = "1.1.6.2" description = "Typing stubs for termcolor" optional = false python-versions = "*" groups = ["linters"] files = [ - {file = "types-termcolor-0.1.1.tar.gz", hash = "sha256:4d9e09ce7f3267985f5280b22e25790c98cb64628b6466e1fb915dbb52ad7136"}, - {file = "types_termcolor-0.1.1-py2.py3-none-any.whl", hash = "sha256:3694c312e32f71fdc0f469c334ea21645f3130d90c93cd53bcb06b1233e174d5"}, + {file = "types-termcolor-1.1.6.2.tar.gz", hash = "sha256:d8f0f69cf5552cc59ce75aa5172937cec9b320c17453adefe4168b93d16daad6"}, + {file = "types_termcolor-1.1.6.2-py3-none-any.whl", hash = "sha256:44c4c762c54a90d99b5c1033ef008aaa5610056d31d5c66b9288a942682a64d7"}, ] [[package]] @@ -1738,12 +1848,12 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "dev", "linters", "script", "test"] +groups = ["main", "dev", "linters", "test"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" @@ -1765,14 +1875,14 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.34.0" +version = "20.35.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev", "linters"] files = [ - {file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"}, - {file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"}, + {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, + {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, ] [package.dependencies] @@ -1830,107 +1940,136 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" description = "Measures the displayed width of unicode strings in a terminal" optional = false -python-versions = "*" +python-versions = ">=3.6" groups = ["main", "dev"] files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, + {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, + {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, ] [[package]] name = "wrapt" -version = "1.17.3" +version = "2.0.1" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, - {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, - {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, - {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, - {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, - {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, - {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, - {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, - {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, - {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, - {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, - {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, - {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, - {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, - {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, - {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, - {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, - {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, - {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, - {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, - {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, - {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, - {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, - {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, - {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, - {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, - {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, - {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, - {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, - {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, - {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, - {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, - {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, - {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, - {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, - {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, - {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, - {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, - {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, - {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, - {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, - {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, - {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, - {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, - {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, - {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, - {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, - {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, - {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25"}, + {file = "wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4"}, + {file = "wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45"}, + {file = "wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd"}, + {file = "wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be"}, + {file = "wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b"}, + {file = "wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb"}, + {file = "wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9"}, + {file = "wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75"}, + {file = "wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b"}, + {file = "wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7"}, + {file = "wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3"}, + {file = "wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e"}, + {file = "wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed"}, + {file = "wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0"}, + {file = "wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c"}, + {file = "wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349"}, + {file = "wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:90897ea1cf0679763b62e79657958cd54eae5659f6360fc7d2ccc6f906342183"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:50844efc8cdf63b2d90cd3d62d4947a28311e6266ce5235a219d21b195b4ec2c"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49989061a9977a8cbd6d20f2efa813f24bf657c6990a42967019ce779a878dbf"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:09c7476ab884b74dce081ad9bfd07fe5822d8600abade571cb1f66d5fc915af6"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1a8a09a004ef100e614beec82862d11fc17d601092c3599afd22b1f36e4137e"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:89a82053b193837bf93c0f8a57ded6e4b6d88033a499dadff5067e912c2a41e9"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f26f8e2ca19564e2e1fdbb6a0e47f36e0efbab1acc31e15471fad88f828c75f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win32.whl", hash = "sha256:115cae4beed3542e37866469a8a1f2b9ec549b4463572b000611e9946b86e6f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c4012a2bd37059d04f8209916aa771dfb564cccb86079072bdcd48a308b6a5c5"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:68424221a2dc00d634b54f92441914929c5ffb1c30b3b837343978343a3512a3"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd1a18f5a797fe740cb3d7a0e853a8ce6461cc62023b630caec80171a6b8097"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb3a86e703868561c5cad155a15c36c716e1ab513b7065bd2ac8ed353c503333"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5dc1b852337c6792aa111ca8becff5bacf576bf4a0255b0f05eb749da6a1643e"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c046781d422f0830de6329fa4b16796096f28a92c8aef3850674442cdcb87b7f"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f73f9f7a0ebd0db139253d27e5fc8d2866ceaeef19c30ab5d69dcbe35e1a6981"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b667189cf8efe008f55bbda321890bef628a67ab4147ebf90d182f2dadc78790"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:a9a83618c4f0757557c077ef71d708ddd9847ed66b7cc63416632af70d3e2308"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b121e9aeb15df416c2c960b8255a49d44b4038016ee17af03975992d03931"}, + {file = "wrapt-2.0.1-cp39-cp39-win32.whl", hash = "sha256:1f186e26ea0a55f809f232e92cc8556a0977e00183c3ebda039a807a42be1494"}, + {file = "wrapt-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bf4cb76f36be5de950ce13e22e7fdf462b35b04665a12b64f3ac5c1bbbcf3728"}, + {file = "wrapt-2.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:d6cc985b9c8b235bd933990cdbf0f891f8e010b65a3911f7a55179cd7b0fc57b"}, + {file = "wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca"}, + {file = "wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f"}, ] +[package.extras] +dev = ["pytest", "setuptools"] + [[package]] name = "zipp" version = "3.23.0" diff --git a/pyproject.toml b/pyproject.toml index 7890edee2e..01b16fec3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,33 +86,33 @@ semver = "commitizen.version_schemes:SemVer" semver2 = "commitizen.version_schemes:SemVer2" [dependency-groups] -dev = ["ipython>=8.0,<9.0", "tox>4", "poethepoet>=0.34.0,<1.0.0"] +dev = ["ipython>=8.0", "tox>4", "poethepoet>=0.34.0"] test = [ - "pytest>=7.2,<9.0", - "pytest-cov>=4,<7", - "pytest-mock>=3.10,<4.0", - "pytest-regressions>=2.4.0,<3.0.0", - "pytest-freezer>=0.4.6,<1.0.0", - "pytest-xdist>=3.1.0,<4.0.0", + "pytest>=7.2", + "pytest-cov>=4", + "pytest-mock>=3.10", + "pytest-regressions>=2.4.0", + "pytest-freezer>=0.4.6", + "pytest-xdist>=3.1.0", ] linters = [ - "ruff>=0.11.5,<1.0.0", - "pre-commit>=3.2.0,<5.0", - "mypy>=1.16.0,<2.0", - "types-deprecated>=1.2.9.2,<2.0", - "types-python-dateutil>=2.8.19.13,<3.0", - "types-PyYAML>=5.4.3,<7.0.0", - "types-termcolor>=0.1.1,<1.0", - "types-colorama>=0.4.15.20240311,<1.0", + "ruff>=0.11.5", + "pre-commit>=3.2.0", + "mypy>=1.16.0", + "types-deprecated>=1.2.9.2", + "types-python-dateutil>=2.8.19.13", + "types-PyYAML>=5.4.3", + "types-termcolor>=0.1.1", + "types-colorama>=0.4.15.20240311", ] -documentation = ["mkdocs>=1.4.2,<2.0", "mkdocs-material>=9.1.6,<10.0"] +documentation = ["mkdocs>=1.4.2", "mkdocs-material>=9.1.6"] script = [ # for scripts/gen_cli_help_screenshots.py - "rich>=13.7.1,<14.0", + "rich>=13.7.1", ] [build-system] From 583acf42deb14fb04f7c188b5f8d09b623e335cc Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 19 Nov 2025 17:12:08 +0800 Subject: [PATCH 251/275] docs(config): split the configuration documentation into pages Co-authored-by: Wei Lee --- docs/commands/bump.md | 233 +--------- docs/commands/changelog.md | 110 +++-- docs/commands/commit.md | 2 +- docs/commands/init.md | 16 +- docs/config.md | 421 ------------------ docs/config/bump.md | 217 ++++++++++ docs/config/changelog.md | 8 + docs/config/check.md | 27 ++ docs/config/commit.md | 26 ++ docs/config/configuration_file.md | 234 ++++++++++ docs/config/option.md | 38 ++ docs/config/version_provider.md | 318 ++++++++++++++ docs/customization.md | 529 ----------------------- docs/customization/changelog_template.md | 85 ++++ docs/customization/config_file.md | 187 ++++++++ docs/customization/python_class.md | 310 +++++++++++++ docs/faq.md | 11 +- docs/tutorials/tag_format.md | 5 +- docs/tutorials/writing_commits.md | 6 +- mkdocs.yml | 14 +- 20 files changed, 1553 insertions(+), 1244 deletions(-) delete mode 100644 docs/config.md create mode 100644 docs/config/bump.md create mode 100644 docs/config/changelog.md create mode 100644 docs/config/check.md create mode 100644 docs/config/commit.md create mode 100644 docs/config/configuration_file.md create mode 100644 docs/config/option.md create mode 100644 docs/config/version_provider.md delete mode 100644 docs/customization.md create mode 100644 docs/customization/changelog_template.md create mode 100644 docs/customization/config_file.md create mode 100644 docs/customization/python_class.md diff --git a/docs/commands/bump.md b/docs/commands/bump.md index ebd8b2774e..78ff9777cf 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -11,7 +11,7 @@ It analyzes your commits to determine the appropriate version increment accordin We will use `pyproject.toml` as the configuration file throughout the documentation. - See [Configuration file](../config.md#configuration-file) for more details. + See [Configuration file](../config/configuration_file.md) for more details. ## Key Features @@ -30,7 +30,7 @@ The version follows the `MAJOR.MINOR.PATCH` format, with increments determined b | `MINOR` | New features | `feat` | | `PATCH` | Fixes and improvements | `fix`, `perf`, `refactor`| -### Version Schemes (`--version-scheme`) +### `--version-scheme` By default, Commitizen uses [PEP 440][pep440] for version formatting. You can switch to semantic versioning using either: @@ -50,7 +50,7 @@ Available options are: - `pep440`: [PEP 440][pep440] (**default** and recommended for Python projects) - `semver`: [Semantic Versioning][semver] (recommended for non-Python projects) -You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`. +You can also set this in the configuration file with `version_scheme = "semver"`. !!! note [pep440][pep440] and [semver][semver] are quite similar, although their difference lies in @@ -111,7 +111,7 @@ Commitizen supports the [PEP 440][pep440] version format, which includes several ### `--files-only` -Bumps the version in the files defined in [`version_files`](#version_files) without creating a commit and tag on the git repository. +Bumps the version in the files defined in [`version_files`][version_files] without creating a commit and tag on the git repository. ```bash cz bump --files-only @@ -178,7 +178,7 @@ The following table illustrates the difference in behavior between the two modes ### `--check-consistency` -Check whether the versions defined in [`version_files`](#version_files) and the version in Commitizen configuration are consistent before bumping version. +Check whether the versions defined in [version_files][version_files] and the version in Commitizen configuration are consistent before bumping version. ```bash cz bump --check-consistency @@ -207,7 +207,7 @@ from setuptools import setup setup(..., version="1.0.5", ...) ``` -When you run `cz bump --check-consistency`, Commitizen will verify that the current version in `pyproject.toml` (`1.21.0`) exists in all files listed in [`version_files`](#version_files). +When you run `cz bump --check-consistency`, Commitizen will verify that the current version in `pyproject.toml` (`1.21.0`) exists in all files listed in [version_files][version_files]. In this example, it will detect that `setup.py` contains `1.0.5` instead of `1.21.0`, causing the bump to fail. !!! warning "Partial updates on failure" @@ -260,6 +260,9 @@ For example, in `pyproject.toml`: annotated_tag = true ``` +!!! note + By default, Commitizen uses lightweight tags. + ### `--annotated-tag-message` Create annotated tags with the given message. @@ -351,10 +354,13 @@ Creates gpg signed tags. cz bump --gpg-sign ``` +!!! note + By default, Commitizen uses lightweight tags. + ### `--template` Provides your own changelog jinja template. -See [the template customization section](../customization.md#customizing-the-changelog-template) +See [the template customization section](../customization/changelog_template.md) ### `--extra` @@ -364,7 +370,7 @@ Provides your own changelog extra variables by using the `extras` settings or th cz bump --changelog --extra key=value -e short="quoted value" ``` -See [the template customization section](../customization.md#customizing-the-changelog-template). +See [the template customization section](../customization/changelog_template.md). ### `--build-metadata` @@ -451,15 +457,13 @@ Allow the project version to be bumped even when there's no eligible version. cz bump --allow-no-commit ``` -### `--version-scheme` - -See [Version Schemes](#version-schemes-version-scheme). - -## Configuration file options + # bump version to 2.0.0 even when there's no breaking changes or even no commits + cz bump --allow-no-commit 2.0.0 + ``` -### `tag_format` +### `--tag-format` -`tag_format` and [`version_scheme`](#version-schemes-version-scheme) are combined to make Git tag names from versions. +`tag_format` and [version_scheme][version_scheme] are combined to make Git tag names from versions. These are used in: @@ -501,200 +505,6 @@ Supported variables: | `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) | | `$devrelease`, `${devrelease}` | Development release | -### `version_files` - -Identify the files or glob patterns which should be updated with the new version. - -Commitizen will update its configuration file automatically when bumping, -regardless of whether the file is present or not in `version_files`. - -You may specify the `version_files` in your configuration file. - -```toml title="pyproject.toml" -[tool.commitizen] -version_files = [ - "src/__version__.py", -] -``` - -It is also possible to provide a pattern for each file, separated by a colon (e.g. `file:pattern`). See the below example for more details. - -```toml title="pyproject.toml" -[tool.commitizen] -version_files = [ - "packages/*/pyproject.toml:version", - "setup.json:version", -] -``` - -#### Example scenario - -We have a project with the following configuration file `pyproject.toml`: - -```toml title="pyproject.toml" -[tool.commitizen] -version_files = [ - "src/__version__.py", - "packages/*/pyproject.toml:version", - "setup.json:version", -] -``` - -For the reference `"setup.json:version"`, it means that it will look for a file `setup.json` and will only change the lines that contain the substring `"version"`. - -For example, if the content of `setup.json` is: - - - -```json title="setup.json" -{ - "name": "magictool", - "version": "1.2.3", - "dependencies": { - "lodash": "1.2.3" - } -} -``` - -After running `cz bump 2.0.0`, its content will be updated to: - -```diff title="setup.json" -{ - "name": "magictool", -- "version": "1.2.3", -+ "version": "2.0.0", - "dependencies": { - "lodash": "1.2.3" - } -} -``` - -!!! note - Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. - -!!! note "Historical note" - This option was renamed from `files` to `version_files`. - -### `bump_message` - -Template used to specify the commit message generated when bumping. - -Defaults to: `bump: version $current_version → $new_version` - -| Variable | Description | -| ------------------ | ----------------------------------- | -| `$current_version` | the version existing before bumping | -| `$new_version` | version generated after bumping | - -#### Example configuration - -```toml title="pyproject.toml" -[tool.commitizen] -bump_message = "release $current_version → $new_version [skip-ci]" -``` - -### `update_changelog_on_bump` - -When set to `true`, `cz bump` is equivalent to `cz bump --changelog`. - -```toml title="pyproject.toml" -[tool.commitizen] -update_changelog_on_bump = true -``` - -### `annotated_tag` - -When set to `true`, `cz bump` is equivalent to `cz bump --annotated-tag`. - -```toml title="pyproject.toml" -[tool.commitizen] -annotated_tag = true -``` - -### `gpg_sign` - -When set to `true`, `cz bump` is equivalent to `cz bump --gpg-sign`. See [--gpg-sign](#-gpg-sign). - -```toml title="pyproject.toml" -[tool.commitizen] -gpg_sign = true -``` - -### `major_version_zero` - -When set to `true`, `cz bump` is equivalent to `cz bump --major-version-zero`. See [--major-version-zero](#-major-version-zero). - -```toml title="pyproject.toml" -[tool.commitizen] -major_version_zero = true -``` - -### `pre_bump_hooks` - -A list of optional commands that will run right _after_ updating [`version_files`](#version_files) -and _before_ actual committing and tagging the release. - -Useful when you need to generate documentation based on the new version. During -execution of the script, some environment variables are available: - -| Variable | Description | -| ---------------------------- | ---------------------------------------------------------- | -| `CZ_PRE_IS_INITIAL` | `True` when this is the initial release, `False` otherwise | -| `CZ_PRE_CURRENT_VERSION` | Current version, before the bump | -| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump | -| `CZ_PRE_NEW_VERSION` | New version, after the bump | -| `CZ_PRE_NEW_TAG_VERSION` | New version tag, after the bump | -| `CZ_PRE_MESSAGE` | Commit message of the bump | -| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATCH` release | -| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | - -```toml title="pyproject.toml" -[tool.commitizen] -pre_bump_hooks = [ - "scripts/generate_documentation.sh" -] -``` - -### `post_bump_hooks` - -A list of optional commands that will run right _after_ committing and tagging the release. - -Useful when you need to send notifications about a release, or further automate deploying the -release. During execution of the script, some environment variables are available: - -| Variable | Description | -| ------------------------------ | ----------------------------------------------------------- | -| `CZ_POST_WAS_INITIAL` | `True` when this was the initial release, `False` otherwise | -| `CZ_POST_PREVIOUS_VERSION` | Previous version, before the bump | -| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump | -| `CZ_POST_CURRENT_VERSION` | Current version, after the bump | -| `CZ_POST_CURRENT_TAG_VERSION` | Current version tag, after the bump | -| `CZ_POST_MESSAGE` | Commit message of the bump | -| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATCH` release | -| `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | - -```toml title="pyproject.toml" -[tool.commitizen] -post_bump_hooks = [ - "scripts/slack_notification.sh" -] -``` - -### `prerelease_offset` - -Offset with which to start counting prereleases. - -Defaults to: `0` - -```toml title="pyproject.toml" -[tool.commitizen] -prerelease_offset = 1 -``` - -### `version_scheme` - -See [Version Schemes](#version-schemes-version-scheme). - ## Avoid raising errors Some situations from Commitizen raise an exit code different from 0. @@ -758,9 +568,6 @@ on the list, and then you can use the exit code: cz -nr 21 bump ``` -## Custom bump - -Read the [customizing section](../customization.md). - [pep440]: https://www.python.org/dev/peps/pep-0440/ [semver]: https://semver.org/ +[version_files]: ../config/bump.md#version_files diff --git a/docs/commands/changelog.md b/docs/commands/changelog.md index aee1a0e075..3f2425a51f 100644 --- a/docs/commands/changelog.md +++ b/docs/commands/changelog.md @@ -2,44 +2,37 @@ This command will generate a changelog following the committing rules established. -To create the changelog automatically on bump, add the setting [update_changelog_on_bump](./bump.md#update_changelog_on_bump) +!!! tip + To create the changelog automatically on bump, add the setting [update_changelog_on_bump](../config/bump.md#update_changelog_on_bump) -```toml -[tool.commitizen] -update_changelog_on_bump = true -``` + ```toml + [tool.commitizen] + update_changelog_on_bump = true + ``` ## Usage ![cz changelog --help](../images/cli_help/cz_changelog___help.svg) -### Examples - -#### Generate full changelog +## Examples ```bash +# Generate full changelog cz changelog -``` -```bash +# or use the alias cz ch -``` -#### Get the changelog for the given version - -```bash +# Get the changelog for the given version cz changelog 0.3.0 --dry-run -``` - -#### Get the changelog for the given version range -```bash +# Get the changelog for the given version range cz changelog 0.3.0..0.4.0 --dry-run ``` ## Constrains -changelog generation is constrained only to **markdown** files. +Changelog generation is constrained only to **markdown** files. ## Description @@ -72,41 +65,19 @@ and the following variables are expected: - **required**: is the only one required to be parsed by the regex -## Configuration - -### `unreleased_version` - -There is usually a chicken and egg situation when automatically -bumping the version and creating the changelog. -If you bump the version first, you have no changelog, you have to -create it later, and it won't be included in -the release of the created version. - -If you create the changelog before bumping the version, then you -usually don't have the latest tag, and the _Unreleased_ title appears. - -By introducing `unreleased_version` you can prevent this situation. - -Before bumping you can run: - -```bash -cz changelog --unreleased-version="v1.0.0" -``` - -Remember to use the tag instead of the raw version number +## Command line options -For example if the format of your tag includes a `v` (`v1.0.0`), then you should use that, -if your tag is the same as the raw version, then ignore this. +### `--extras` -Alternatively you can directly bump the version and create the changelog by doing +Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter. ```bash -cz bump --changelog +cz changelog --extra key=value -e short="quoted value" ``` -### `file-name` +### `--file-name` -This value can be updated in the `toml` file with the key `changelog_file` under `tools.commitizen` +This value can be updated in the configuration file with the key `changelog_file` under `tools.commitizen` Specify the name of the output file, remember that changelog only works with Markdown. @@ -114,9 +85,9 @@ Specify the name of the output file, remember that changelog only works with Mar cz changelog --file-name="CHANGES.md" ``` -### `incremental` +### `--incremental` -This flag can be set in the `toml` file with the key `changelog_incremental` under `tools.commitizen` +This flag can be set in the configuration file with the key `changelog_incremental` under `tools.commitizen` Benefits: @@ -134,9 +105,9 @@ cz changelog --incremental changelog_incremental = true ``` -### `start-rev` +### `--start-rev` -This value can be set in the `toml` file with the key `changelog_start_rev` under `tools.commitizen` +This value can be set in the configuration file with the key `changelog_start_rev` under `tools.commitizen` Start from a given git rev to generate the changelog. Commits before that rev will not be considered. This is especially useful for long-running projects adopting conventional commits, where old commit messages might fail to be parsed for changelog generation. @@ -150,9 +121,9 @@ cz changelog --start-rev="v0.2.0" changelog_start_rev = "v0.2.0" ``` -### merge-prerelease +### `--merge-prerelease` -This flag can be set in the `toml` file with the key `changelog_merge_prerelease` under `tools.commitizen` +This flag can be set in the configuration file with the key `changelog_merge_prerelease` under `tools.commitizen` Collects changes from prereleases into the next non-prerelease. This means that if you have a prerelease version, and then a normal release, the changelog will show the prerelease changes as part of the changes of the normal release. If not set, it will include prereleases in the changelog. @@ -166,20 +137,39 @@ cz changelog --merge-prerelease changelog_merge_prerelease = true ``` -### `template` +### `--template` Provides your own changelog jinja template by using the `template` settings or the `--template` parameter. -See [the template customization section](../customization.md#customizing-the-changelog-template) -### `extras` +### `--unreleased-version` -Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter. +There is usually a chicken and egg situation when automatically +bumping the version and creating the changelog. +If you bump the version first, you have no changelog, you have to +create it later, and it won't be included in +the release of the created version. + +If you create the changelog before bumping the version, then you +usually don't have the latest tag, and the _Unreleased_ title appears. + +By introducing `--unreleased-version` you can prevent this situation. + +Before bumping you can run: ```bash -cz changelog --extra key=value -e short="quoted value" +cz changelog --unreleased-version="v1.0.0" ``` -See [the template customization section](../customization.md#customizing-the-changelog-template) +Remember to use the tag instead of the raw version number + +For example if the format of your tag includes a `v` (`v1.0.0`), then you should use that, +if your tag is the same as the raw version, then ignore this. + +Alternatively you can directly bump the version and create the changelog by doing + +```bash +cz bump --changelog +``` ## Hooks @@ -192,4 +182,4 @@ Read more about hooks in the [customization page][customization] [keepachangelog]: https://keepachangelog.com/ [semver]: https://semver.org/ -[customization]: ../customization.md +[customization]: ../customization/config_file.md diff --git a/docs/commands/commit.md b/docs/commands/commit.md index be9d193b97..54e0c8b07a 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -11,7 +11,7 @@ The `commit` command provides an interactive way to create structured commits. U - `cz commit` - `cz c` (shortcut) -By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization.md) for details. +By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization/config_file.md) for details. ## Basic Usage diff --git a/docs/commands/init.md b/docs/commands/init.md index 4d92112d34..b673ba1276 100644 --- a/docs/commands/init.md +++ b/docs/commands/init.md @@ -20,10 +20,7 @@ When you run `cz init`, Commitizen will guide you through an interactive setup p The initialization process will create a configuration file in your project root. -Choose the configuration file format based on your project type: - -- Use `pyproject.toml` for Python projects -- Use `.cz.toml`, `.cz.yaml`, `.cz.json`, etc. for other projects. +See [Configuration File][configuration_file] for more details. ## Configuration Options @@ -48,6 +45,8 @@ During the initialization process, you'll be prompted to configure the following 7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software 8. **Pre-commit Hooks**: Set up Git pre-commit hooks for automated commit message validation +See [Configuration Options][configuration_options] for more details. + ## Example ```sh @@ -61,6 +60,9 @@ cz init After initialization, you can: -1. Start using `cz commit` to create conventional commits -2. Use `cz bump` to manage versioning -3. Configure additional settings in your project's configuration file +1. Start using [`cz commit`](./commit.md) to create conventional commits +2. Use [`cz bump`](./bump.md) to manage versioning +3. Configure additional settings in your project's [configuration_file][configuration_file] + +[configuration_file]: ../config/configuration_file.md +[configuration_options]: ../config/option.md diff --git a/docs/config.md b/docs/config.md deleted file mode 100644 index 307cf54a9f..0000000000 --- a/docs/config.md +++ /dev/null @@ -1,421 +0,0 @@ -# Configuration - -## Settings - -### `name` - -- Type: `str` -- Default: `"cz_conventional_commits"` - -Name of the committing rules to use. - -### `version` - -- Type: `str` -- Default: `None` - -Current version. Example: `"0.1.2"`. Required if you use `version_provider = "commitizen"`. - -### `version_files` - -- Type: `list` -- Default: `[]` - -Files (or glob patterns) where the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] - -### `version_provider` - -- Type: `str` -- Default: `commitizen` - -Version provider used to read and write version. [Read more](#version-providers) - -### `version_scheme` - -- Type: `str` -- Default: `pep440` - -Select a version scheme from the following options: - -- `pep440` -- `semver` -- `semver2` - -Useful for non-python projects. [Read more][version-scheme] - -### `tag_format` - -- Type: `str` -- Default: `$version` - -Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] - -### `legacy_tag_formats` - -- Type: `list` -- Default: `[]` - -Legacy git tag formats, useful for old projects that changed tag format. -Tags matching those formats will be recognized as version tags and be included in the changelog. -Each entry uses the syntax as [`tag_format`](#tag_format). [Read more][tag_format] - -### `ignored_tag_formats` - -- Type: `list` -- Default: `[]` - -Tags matching those formats will be totally ignored and won't raise a warning. -Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*` -that will match everything (non-greedy). [Read more][tag_format] - -### `update_changelog_on_bump` - -- Type: `bool` -- Default: `False` - -Create changelog when running `cz bump`. - -### `gpg_sign` - -- Type: `bool` -- Default: `False` - -Use gpg signed tags instead of lightweight tags. - -### `annotated_tag` - -- Type: `bool` -- Default: `False` - -Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] - -### `bump_message` - -- Type: `str` -- Default: `None` - -Create custom commit message. Useful to skip CI. [Read more][bump_message] - -### `breaking_change_exclamation_in_title` - -Type: `bool` - -Default: `False` - -When true, breaking changes will be also indicated by an exclamation mark in the commit title (e.g., `feat!: breaking change`). -When false, breaking changes will be only indicated by `BREAKING CHANGE:` in the footer. [Read more][writing_commits] - -### `retry_after_failure` - -- Type: `bool` -- Default: `False` - -Automatically retry failed commit when running `cz commit`. [Read more][retry_after_failure] - -### `allow_abort` - -- Type: `bool` -- Default: `False` - -Disallow empty commit messages. Useful in CI. [Read more][allow_abort] - -### `message_length_limit` - -Type: `int` - -Default: `0` - -Maximum length of the commit message. Setting it to `0` disables the length limit. It can be overridden by the `-l/--message-length-limit` command line argument. - -### `allowed_prefixes` - -- Type: `list` -- Default: `["Merge", "Revert", "Pull request", "fixup!", "squash!"]` - -List of prefixes that commitizen ignores when verifying messages. [Read more][allowed_prefixes] - -### `changelog_file` - -- Type: `str` -- Default: `CHANGELOG.md` - -Filename of exported changelog - -### `changelog_format` - -- Type: `str` -- Default: `None` - -Format used to parse and generate the changelog. If not specified, guessed from [`changelog_file`](#changelog_file). - -### `changelog_incremental` - -- Type: `bool` -- Default: `False` - -Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. - -!!! note - When doing `cz bump --changelog` this is automatically set to `True` - -### `changelog_start_rev` - -- Type: `str` -- Default: `None` - -Start from a given git rev to generate the changelog - -### `changelog_merge_prerelease` - -- Type: `bool` -- Default: `False` - -Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog. - -### `style` - -- Type: `list` -- Default: `[]` - -Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] - -### `customize` - -- Type: `dict` -- Default: `None` - -**This is only supported when config through `toml`.** Custom rules for committing and bumping. [Read more][customization] - -### `use_shortcuts` - -- Type: `bool` -- Default: `False` - -If enabled, Commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] - -### `major_version_zero` - -- Type: `bool` -- Default: `False` - -If enabled, breaking changes on a `0.x` will remain as a `0.x` version. Otherwise, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero] - -### `prerelease_offset` - -- Type: `int` -- Default: `0` - -In some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. [Read more][prerelease-offset] - -### `pre_bump_hooks` - -- Type: `list[str]` -- Default: `[]` - -Calls the hook scripts **before** bumping version. [Read more][pre_bump_hooks] - -### `post_bump_hooks` - -- Type: `list[str]` -- Default: `[]` - -Calls the hook scripts **after** bumping the version. [Read more][post_bump_hooks] - -### `encoding` - -- Type: `str` -- Default: `"utf-8"` - -Sets the character encoding to be used when parsing commit messages. [Read more][encoding] - -### `template` - -- Type: `str` -- Default: `None` (provided by plugin) - -Provide custom changelog jinja template path relative to the current working directory. [Read more][template-customization] - -### `extras` - -- Type: `dict[str, Any]` -- Default: `{}` - -Provide extra variables to the changelog template. [Read more][template-customization] - -## Configuration file - -### `pyproject.toml`, `.cz.toml` or `cz.toml` - -Default and recommended configuration format for a project. -For a **python** project, we recommend adding an entry to your `pyproject.toml`. -You can also create a `.cz.toml` or `cz.toml` file at the root of your project folder. - -Example configuration: - -```toml title=".cz.toml" -[tool.commitizen] -name = "cz_conventional_commits" -version = "0.1.0" -version_files = [ - "src/__version__.py", - "pyproject.toml:version" -] -update_changelog_on_bump = true -style = [ - ["qmark", "fg:#ff9d00 bold"], - ["question", "bold"], - ["answer", "fg:#ff9d00 bold"], - ["pointer", "fg:#ff9d00 bold"], - ["highlighted", "fg:#ff9d00 bold"], - ["selected", "fg:#cc5454"], - ["separator", "fg:#cc5454"], - ["instruction", ""], - ["text", ""], - ["disabled", "fg:#858585 italic"] -] -``` - -### `.cz.json` or `cz.json` - -Commitizen has support for JSON configuration. Recommended for `NodeJS` projects. - -```json title=".cz.json" -{ - "commitizen": { - "name": "cz_conventional_commits", - "version": "0.1.0", - "version_files": ["src/__version__.py", "pyproject.toml:version"], - "style": [ - ["qmark", "fg:#ff9d00 bold"], - ["question", "bold"], - ["answer", "fg:#ff9d00 bold"], - ["pointer", "fg:#ff9d00 bold"], - ["highlighted", "fg:#ff9d00 bold"], - ["selected", "fg:#cc5454"], - ["separator", "fg:#cc5454"], - ["instruction", ""], - ["text", ""], - ["disabled", "fg:#858585 italic"] - ] - } -} -``` - -### `.cz.yaml` or `cz.yaml` - -YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects. - -```yaml title=".cz.yaml" -commitizen: - name: cz_conventional_commits - version: 0.1.0 - version_files: - - src/__version__.py - - pyproject.toml:version - style: - - - qmark - - fg:#ff9d00 bold - - - question - - bold - - - answer - - fg:#ff9d00 bold - - - pointer - - fg:#ff9d00 bold - - - highlighted - - fg:#ff9d00 bold - - - selected - - fg:#cc5454 - - - separator - - fg:#cc5454 - - - instruction - - "" - - - text - - "" - - - disabled - - fg:#858585 italic -``` - -## Version providers - -Commitizen can read and write version from different sources. -By default, it uses the `commitizen` one which is using the `version` field from the Commitizen settings. -But you can use any `commitizen.provider` entrypoint as value for `version_provider`. - -Commitizen provides some version providers for some well known formats: - -| name | description | -| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `commitizen` | Default version provider: Fetch and set version in Commitizen config. | -| `scm` | Fetch the version from git and does not need to set it back | -| `pep621` | Get and set version from `pyproject.toml` `project.version` field | -| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field | -| `uv` | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `package.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field | -| `cargo` | Get and set version from `Cargo.toml` `package.version` field and `Cargo.lock` `package.version` field whose `package.name` field is the same as `Cargo.toml` `package.name` field | -| `npm` | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists | -| `composer` | Get and set version from `composer.json` `project.version` field | - -!!! note - The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. - -An example in your `.cz.toml` or `cz.toml` would look like this: - -```toml -[tool.commitizen] -version_provider = "pep621" -``` - -### Custom version provider - -You can add your own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. - -Here is a quick example of a provider `my_provider.py`, reading and writing version in a `VERSION` file. - -```python title="my_provider.py" -from pathlib import Path -from commitizen.providers import VersionProvider - - -class MyProvider(VersionProvider): - file = Path() / "VERSION" - - def get_version(self) -> str: - return self.file.read_text() - - def set_version(self, version: str): - self.file.write_text(version) -``` - -```python title="setup.py" -from setuptools import setup - -setup( - name="my-commitizen-provider", - version="0.1.0", - py_modules=["my_provider"], - install_requires=["commitizen"], - entry_points={ - "commitizen.provider": [ - "my-provider = my_provider:MyProvider", - ] - }, -) -``` - -[version_files]: commands/bump.md#version_files -[tag_format]: commands/bump.md#tag_format -[bump_message]: commands/bump.md#bump_message -[major-version-zero]: commands/bump.md#-major-version-zero -[prerelease-offset]: commands/bump.md#prerelease_offset -[retry_after_failure]: commands/commit.md#retry -[allow_abort]: commands/check.md#-allow-abort -[version-scheme]: commands/bump.md#version-schemes-version-scheme -[pre_bump_hooks]: commands/bump.md#pre_bump_hooks -[post_bump_hooks]: commands/bump.md#post_bump_hooks -[allowed_prefixes]: commands/check.md#-allowed-prefixes -[additional-features]: https://github.com/tmbo/questionary#additional-features -[customization]: customization.md -[shortcuts]: customization.md#shortcut-keys -[template-customization]: customization.md#customizing-the-changelog-template -[annotated-tags-vs-lightweight]: https://stackoverflow.com/a/11514139/2047185 -[encoding]: tutorials/writing_commits.md#writing-commits -[writing_commits]: tutorials/writing_commits.md#conventional-commits diff --git a/docs/config/bump.md b/docs/config/bump.md new file mode 100644 index 0000000000..fa46e18f8d --- /dev/null +++ b/docs/config/bump.md @@ -0,0 +1,217 @@ +# Bump Options + + + +## `annotated_tag` + +When set to `true`, `cz bump` is equivalent to `cz bump --annotated-tag`. + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag = true +``` + +## `bump_message` + +Template used to specify the commit message generated when bumping. + +Defaults to: `bump: version $current_version → $new_version` + +| Variable | Description | +| ------------------ | ----------------------------------- | +| `$current_version` | the version existing before bumping | +| `$new_version` | version generated after bumping | + +```toml title="pyproject.toml" +[tool.commitizen] +bump_message = "release $current_version → $new_version [skip-ci]" +``` + +## `gpg_sign` + +When set to `true`, `cz bump` is equivalent to `cz bump --gpg-sign`. See [`--gpg-sign`](../commands/bump.md#-gpg-sign). + +```toml title="pyproject.toml" +[tool.commitizen] +gpg_sign = true +``` + +## `ignored_tag_formats` + +- Type: `list` +- Default: `[]` + +Tags matching those formats will be totally ignored and won't raise a warning. +Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*` that will match everything (non-greedy). + +## `major_version_zero` + +When set to `true`, `cz bump` is equivalent to `cz bump --major-version-zero`. See [`--major-version-zero`](../commands/bump.md#-major-version-zero). + +```toml title="pyproject.toml" +[tool.commitizen] +major_version_zero = true +``` + +## `legacy_tag_formats` + +- Type: `list` +- Default: `[]` + +Legacy git tag formats, useful for old projects that changed tag format. +Tags matching those formats will be recognized as version tags and be included in the changelog. +Each entry uses the syntax as `tag_format`. + +## `pre_bump_hooks` + +A list of optional commands that will run right *after* updating [`version_files`](#version_files) and *before* actual committing and tagging the release. + +Useful when you need to generate documentation based on the new version. During +execution of the script, some environment variables are available: + +| Variable | Description | +| ---------------------------- | ---------------------------------------------------------- | +| `CZ_PRE_IS_INITIAL` | `True` when this is the initial release, `False` otherwise | +| `CZ_PRE_CURRENT_VERSION` | Current version, before the bump | +| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump | +| `CZ_PRE_NEW_VERSION` | New version, after the bump | +| `CZ_PRE_NEW_TAG_VERSION` | New version tag, after the bump | +| `CZ_PRE_MESSAGE` | Commit message of the bump | +| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATCH` release | +| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | + +```toml title="pyproject.toml" +[tool.commitizen] +pre_bump_hooks = [ + "scripts/generate_documentation.sh" +] +``` + +## `post_bump_hooks` + +A list of optional commands that will run right *after* committing and tagging the release. + +Useful when you need to send notifications about a release, or further automate deploying the +release. During execution of the script, some environment variables are available: + +| Variable | Description | +| ------------------------------ | ----------------------------------------------------------- | +| `CZ_POST_WAS_INITIAL` | `True` when this was the initial release, `False` otherwise | +| `CZ_POST_PREVIOUS_VERSION` | Previous version, before the bump | +| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump | +| `CZ_POST_CURRENT_VERSION` | Current version, after the bump | +| `CZ_POST_CURRENT_TAG_VERSION` | Current version tag, after the bump | +| `CZ_POST_MESSAGE` | Commit message of the bump | +| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATCH` release | +| `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | + +```toml title="pyproject.toml" +[tool.commitizen] +post_bump_hooks = [ + "scripts/slack_notification.sh" +] +``` + +## `prerelease_offset` + +Offset with which to start counting prereleases. + +If not specified, defaults to `0`. + +```toml title="pyproject.toml" +[tool.commitizen] +prerelease_offset = 1 +``` + +!!! note + Under some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. + +## `tag_format` + +See [`--tag-format`](../commands/bump.md#-tag-format). + +## `update_changelog_on_bump` + +When set to `true`, `cz bump` is equivalent to `cz bump --changelog`. + +```toml title="pyproject.toml" +[tool.commitizen] +update_changelog_on_bump = true +``` + +## `version_files` + +Identify the files or glob patterns which should be updated with the new version. + +Commitizen will update its configuration file automatically when bumping, +regardless of whether the file is present or not in `version_files`. + +You may specify the `version_files` in your configuration file. + +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "src/__version__.py", +] +``` + +It is also possible to provide a pattern for each file, separated by a colon (e.g. `file:pattern`). See the below example for more details. + +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "packages/*/pyproject.toml:version", + "setup.json:version", +] +``` + +!!! note "Example scenario" + We have a project with the following configuration file `pyproject.toml`: + + ```toml title="pyproject.toml" + [tool.commitizen] + version_files = [ + "src/__version__.py", + "packages/*/pyproject.toml:version", + "setup.json:version", + ] + ``` + + For the reference `"setup.json:version"`, it means that it will look for a file `setup.json` and will only change the lines that contain the substring `"version"`. + + For example, if the content of `setup.json` is: + + + + ```json title="setup.json" + { + "name": "magictool", + "version": "1.2.3", + "dependencies": { + "lodash": "1.2.3" + } + } + ``` + + After running `cz bump 2.0.0`, its content will be updated to: + + ```diff title="setup.json" + { + "name": "magictool", + - "version": "1.2.3", + + "version": "2.0.0", + "dependencies": { + "lodash": "1.2.3" + } + } + ``` + +!!! note + Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. + +!!! note "Historical note" + This option was renamed from `files` to `version_files`. + +## `version_scheme` + +See [`--version-scheme`](../commands/bump.md#-version-scheme). diff --git a/docs/config/changelog.md b/docs/config/changelog.md new file mode 100644 index 0000000000..86d4363105 --- /dev/null +++ b/docs/config/changelog.md @@ -0,0 +1,8 @@ +# Changelog Options + + + + +As for now, each of the options that is used by `cz changelog` command can correlate to a command line option. + +See [changelog command line options](../commands/changelog.md#command-line-options) for more details. diff --git a/docs/config/check.md b/docs/config/check.md new file mode 100644 index 0000000000..2c8dda27b4 --- /dev/null +++ b/docs/config/check.md @@ -0,0 +1,27 @@ +# Check Options + + + +## `allow_abort` + +- Type: `bool` +- Default: `False` + +Disallow empty commit messages. Useful in CI. + +## `allowed_prefixes` + +- Type: `list` +- Default: `["Merge", "Revert", "Pull request", "fixup!", "squash!"]` + +List of prefixes that commitizen ignores when verifying messages. + +## `message_length_limit` + +- Type: `int` +- Default: `0` (no limit) + +Maximum length of the commit message. Setting it to `0` disables the length limit. + +!!! note + This option can be overridden by the `-l/--message-length-limit` command line argument. diff --git a/docs/config/commit.md b/docs/config/commit.md new file mode 100644 index 0000000000..72fdff947c --- /dev/null +++ b/docs/config/commit.md @@ -0,0 +1,26 @@ +# Commit Options + + + +## `breaking_change_exclamation_in_title` + +- Type: `bool` +- Default: `False` + +When true, breaking changes will be also indicated by an exclamation mark in the commit title (e.g., `feat!: breaking change`). + +When false, breaking changes will be only indicated by `BREAKING CHANGE:` in the footer. See [writing commits](../tutorials/writing_commits.md) for more details. + +## `encoding` + +- Type: `str` +- Default: `"utf-8"` + +Sets the character encoding to be used when parsing commit messages. + +## `retry_after_failure` + +- Type: `bool` +- Default: `False` + +Retries failed commit when running `cz commit`. diff --git a/docs/config/configuration_file.md b/docs/config/configuration_file.md new file mode 100644 index 0000000000..8469102550 --- /dev/null +++ b/docs/config/configuration_file.md @@ -0,0 +1,234 @@ +# Configuration File + +Commitizen uses configuration files to customize its behavior for your project. These files define settings such as which commit rules to use, version management preferences, changelog generation options, and more. + +## Creating a Configuration File + +It is recommended to create a configuration file via our [`cz init`](../commands/init.md) command. This command will guide you through setting up your configuration file with the appropriate settings for your project. + +## File Location and Search Order + +Configuration files are typically located in the root of your project directory. Commitizen searches for configuration files in the following order: + +1. `pyproject.toml` (in the `[tool.commitizen]` section) +2. `.cz.toml` +3. `.cz.json` +4. `cz.json` +5. `.cz.yaml` +6. `cz.yaml` +7. `cz.toml` + +The first valid configuration file found will be used. If no configuration file is found, Commitizen will use its default settings. + +!!! tip + For Python projects, it's recommended to add your Commitizen configuration to `pyproject.toml` to keep all project configuration in one place. + +## Supported Formats + +Commitizen supports three configuration file formats: + +- **TOML** (`.toml`) - Recommended for Python projects +- **JSON** (`.json`) +- **YAML** (`.yaml`) + +All formats support the same configuration options. Choose the format that best fits your project's ecosystem. + +## Configuration Structure + +### TOML Format + +For TOML files, Commitizen settings are placed under the `[tool.commitizen]` section. If you're using a standalone `.cz.toml` or `cz.toml` file, you can use `[tool.commitizen]` or just `[commitizen]`. + +**Example: `pyproject.toml`, `.cz.toml` or `cz.toml`** + +```toml title="pyproject.toml" +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.1.0" +version_provider = "commitizen" +version_scheme = "pep440" +version_files = [ + "src/__version__.py", + "pyproject.toml:version" +] +tag_format = "$version" +update_changelog_on_bump = true +changelog_file = "CHANGELOG.md" +changelog_incremental = false +bump_message = "bump: version $current_version → $new_version" +gpg_sign = false +annotated_tag = false +major_version_zero = false +prerelease_offset = 0 +retry_after_failure = false +allow_abort = false +message_length_limit = 0 +allowed_prefixes = [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!" +] +breaking_change_exclamation_in_title = false +use_shortcuts = false +pre_bump_hooks = [] +post_bump_hooks = [] +encoding = "utf-8" + +# Optional: Custom styling for prompts +style = [ + ["qmark", "fg:#ff9d00 bold"], + ["question", "bold"], + ["answer", "fg:#ff9d00 bold"], + ["pointer", "fg:#ff9d00 bold"], + ["highlighted", "fg:#ff9d00 bold"], + ["selected", "fg:#cc5454"], + ["separator", "fg:#cc5454"], + ["instruction", ""], + ["text", ""], + ["disabled", "fg:#858585 italic"] +] +``` + +### JSON Format + +For JSON files, Commitizen settings are placed under the `commitizen` key. + +**Example: `.cz.json` or `cz.json`** + +```json title=".cz.json" +{ + "commitizen": { + "name": "cz_conventional_commits", + "version": "0.1.0", + "version_provider": "commitizen", + "version_scheme": "pep440", + "version_files": [ + "src/__version__.py", + "pyproject.toml:version" + ], + "tag_format": "$version", + "update_changelog_on_bump": true, + "changelog_file": "CHANGELOG.md", + "changelog_incremental": false, + "bump_message": "bump: version $current_version → $new_version", + "gpg_sign": false, + "annotated_tag": false, + "major_version_zero": false, + "prerelease_offset": 0, + "retry_after_failure": false, + "allow_abort": false, + "message_length_limit": 0, + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!" + ], + "breaking_change_exclamation_in_title": false, + "use_shortcuts": false, + "pre_bump_hooks": [], + "post_bump_hooks": [], + "encoding": "utf-8", + "style": [ + ["qmark", "fg:#ff9d00 bold"], + ["question", "bold"], + ["answer", "fg:#ff9d00 bold"], + ["pointer", "fg:#ff9d00 bold"], + ["highlighted", "fg:#ff9d00 bold"], + ["selected", "fg:#cc5454"], + ["separator", "fg:#cc5454"], + ["instruction", ""], + ["text", ""], + ["disabled", "fg:#858585 italic"] + ] + } +} +``` + +### YAML Format + +For YAML files, Commitizen settings are placed under the `commitizen` key. + +**Example: `.cz.yaml` or `cz.yaml`** + +```yaml title=".cz.yaml" +commitizen: + name: cz_conventional_commits + version: "0.1.0" + version_provider: commitizen + version_scheme: pep440 + version_files: + - src/__version__.py + - pyproject.toml:version + tag_format: "$version" + update_changelog_on_bump: true + changelog_file: CHANGELOG.md + changelog_incremental: false + bump_message: "bump: version $current_version → $new_version" + gpg_sign: false + annotated_tag: false + major_version_zero: false + prerelease_offset: 0 + retry_after_failure: false + allow_abort: false + message_length_limit: 0 + allowed_prefixes: + - Merge + - Revert + - Pull request + - fixup! + - squash! + - amend! + breaking_change_exclamation_in_title: false + use_shortcuts: false + pre_bump_hooks: [] + post_bump_hooks: [] + encoding: utf-8 + style: + - - qmark + - fg:#ff9d00 bold + - - question + - bold + - - answer + - fg:#ff9d00 bold + - - pointer + - fg:#ff9d00 bold + - - highlighted + - fg:#ff9d00 bold + - - selected + - fg:#cc5454 + - - separator + - fg:#cc5454 + - - instruction + - "" + - - text + - "" + - - disabled + - fg:#858585 italic +``` + +## Configuration Options + +For a complete list of all available configuration options and their descriptions, see the [Configuration Settings](../config/option.md) documentation. + +Key configuration categories include: + +- **Commit Rules**: `name` - Select which commit convention to use +- **Version Management**: `version`, `version_provider`, `version_scheme`, `version_files` +- **Tagging**: `tag_format`, `legacy_tag_formats`, `ignored_tag_formats`, `gpg_sign`, `annotated_tag` +- **Changelog**: `changelog_file`, `changelog_format`, `changelog_incremental`, `update_changelog_on_bump` +- **Bumping**: `bump_message`, `major_version_zero`, `prerelease_offset`, `pre_bump_hooks`, `post_bump_hooks` +- **Commit Validation**: `allowed_prefixes`, `message_length_limit`, `allow_abort`, `retry_after_failure` +- **Customization**: `customize`, `style`, `use_shortcuts`, `template`, `extras` + +## Customization + +For advanced customization, including creating custom commit rules, see the [Customization](../customization/config_file.md) documentation. + +!!! note + The `customize` option is only supported when using TOML configuration files. diff --git a/docs/config/option.md b/docs/config/option.md new file mode 100644 index 0000000000..5b7ce807b3 --- /dev/null +++ b/docs/config/option.md @@ -0,0 +1,38 @@ +# Misc Options + +## `name` + +- Type: `str` +- Default: `"cz_conventional_commits"` + +Name of the committing rules to use. + +## `version` + +- Type: `str` +- Default: `None` + +Current version. Example: `"0.1.2"`. Required if you use `version_provider = "commitizen"`. + +## `style` + +- Type: `list` +- Default: `[]` + +Style for the prompts (It will merge this value with default style.) See [Styling your prompts with your favorite colors](https://github.com/tmbo/questionary#additional-features) for more details. + +## `customize` + +- Type: `dict` +- Default: `None` + +**This is only supported when config through `toml` configuration file.** + +Custom rules for committing and bumping. See [customization](../customization/config_file.md) for more details. + +## `use_shortcuts` + +- Type: `bool` +- Default: `False` + +Show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. See [shortcut keys](../customization/config_file.md#shortcut-keys) for more details. diff --git a/docs/config/version_provider.md b/docs/config/version_provider.md new file mode 100644 index 0000000000..4f070b2fd3 --- /dev/null +++ b/docs/config/version_provider.md @@ -0,0 +1,318 @@ +# Version Providers + +Version providers are the mechanism by which Commitizen reads and writes version information in your project. + +They abstract away the details of where and how version numbers are stored, allowing Commitizen to work seamlessly with different project types and package management systems. + +## Overview + +By default, Commitizen uses the `commitizen` provider, which stores the version in your Commitizen configuration file. +However, you can configure Commitizen to use any available provider that matches your project's setup. +This is particularly useful when you want Commitizen to manage versions in the same location as your package manager (e.g., `package.json` for Node.js projects, `pyproject.toml` for Python projects). + +## Built-in Providers + +Commitizen includes several built-in version providers for common package management formats: + +### `commitizen` (Default) + +The default version provider stores and retrieves the version from your Commitizen configuration file (e.g., `pyproject.toml`, `.cz.toml`, etc.). + +**Use when:** +- You want to keep version management separate from your package manager +- Your project doesn't use a standard package manager +- You need maximum flexibility in version management + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "commitizen" +version = "0.1.0" # Required when using this provider +``` + +### `scm` + +Fetches the version from Git tags using `git describe`. This provider **only reads** version information and never writes it back to files. It's designed to work with tools like `setuptools-scm` or other package manager `*-scm` plugins that derive version numbers from Git history. + +**Use when:** +- You're using `setuptools-scm` or similar tools +- You want version numbers derived from Git tags +- You don't want Commitizen to modify any files for version management + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "scm" +# No version field needed - it's read from Git tags +``` + +!!! note + The `scm` provider is read-only. When you run `cz bump`, it will create a Git tag but won't update any files. This is intentional and works well with tools that derive versions from Git tags. + +### `pep621` + +Manages version in `pyproject.toml` under the `project.version` field, following [PEP 621](https://peps.python.org/pep-0621/) standards. + +**Use when:** +- You're using a modern Python project with PEP 621-compliant `pyproject.toml` +- You want version management integrated with your Python project metadata + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "pep621" +``` + +**Example `pyproject.toml`:** +```toml +[project] +name = "my-package" +version = "0.1.0" # Managed by Commitizen +``` + +### `poetry` + +Manages version in `pyproject.toml` under the `tool.poetry.version` field, which is used by the [Poetry](https://python-poetry.org/) package manager. This approach is recommended only for users running Poetry versions earlier than 2.0 or relying on Poetry-specific features. For most users on Poetry 2.0 or later, it is recommended to use `pep621` instead. [Read More](https://python-poetry.org/docs/main/managing-dependencies/) + +**Use when:** +- You're using Poetry < 2.0 as your Python package manager +- You're using Poetry >= 2.0 as your Python package manager, but don't need poetry-specific features +- You want Commitizen to manage the version that Poetry uses + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "poetry" +``` + +**Example `pyproject.toml`:** +```toml +[tool.poetry] +name = "my-package" +version = "0.1.0" # Managed by Commitizen +``` + +### `uv` + +Manages version in both `pyproject.toml` (`project.version`) and `uv.lock` (`package.version` for the matching package name). This ensures consistency between your project metadata and lock file. + + +!!! note + Even though uv follows PEP 621 format, `pep621` does not manage the version in `uv.lock`. `uv` is still suggested for uv users. + +**Use when:** +- You're using `uv` as your Python package manager +- You want version synchronization between `pyproject.toml` and `uv.lock` + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "uv" +``` + +### `cargo` + +Manages version in both `Cargo.toml` (`package.version`) and `Cargo.lock` (`package.version` for the matching package name). This ensures consistency between your Rust project's manifest and lock file. + +**Use when:** +- You're working with a Rust project using Cargo +- You want Commitizen to manage Rust package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "cargo" +``` + +**Example `Cargo.toml`:** +```toml +[package] +name = "my-crate" +version = "0.1.0" # Managed by Commitizen +``` + +### `npm` + +Manages version in `package.json` and optionally synchronizes with `package-lock.json` and `npm-shrinkwrap.json` if they exist. + +**Use when:** +- You're working with a Node.js/JavaScript project +- You want Commitizen to manage npm package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "npm" +``` + +**Example `package.json`:** +```json +{ + "name": "my-package", + "version": "0.1.0" +} +``` + +### `composer` + +Manages version in `composer.json` under the `version` field, used by PHP's Composer package manager. + +**Use when:** +- You're working with a PHP project using Composer +- You want Commitizen to manage Composer package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "composer" +``` + +**Example `composer.json`:** +```json +{ + "name": "vendor/package", + "version": "0.1.0" +} +``` + +## Provider Comparison Table + +| Provider | File(s) Modified | Read-Only | Best For | +| ------------ | ----------------------------------- | --------- | --------------------------------- | +| `commitizen` | Commitizen config file | No | General use, flexible projects | +| `scm` | None (reads from Git tags) | Yes | `setuptools-scm` users | +| `pep621` | `pyproject.toml` (`project.version`) | No | Modern Python (PEP 621) | +| `poetry` | `pyproject.toml` (`tool.poetry.version`) | No | Poetry projects | +| `uv` | `pyproject.toml` + `uv.lock` | No | uv package manager | +| `cargo` | `Cargo.toml` + `Cargo.lock` | No | Rust/Cargo projects | +| `npm` | `package.json` (+ lock files) | No | Node.js/npm projects | +| `composer` | `composer.json` | No | PHP/Composer projects | + +## Creating Custom Version Providers + +If none of the built-in providers meet your needs, you can create a custom version provider by extending the `VersionProvider` base class and registering it as a plugin. + +### Step 1: Create Your Provider Class + +Create a Python file (e.g., `my_provider.py`) that extends `VersionProvider`: + +```python title="my_provider.py" +from pathlib import Path +from commitizen.providers import VersionProvider + + +class MyProvider(VersionProvider): + """ + Custom version provider that reads/writes from a VERSION file. + """ + + def get_version(self) -> str: + """Read version from VERSION file.""" + version_file = Path("VERSION") + if not version_file.exists(): + return "0.0.0" + return version_file.read_text().strip() + + def set_version(self, version: str) -> None: + """Write version to VERSION file.""" + version_file = Path("VERSION") + version_file.write_text(f"{version}\n") +``` + +### Step 2: Register as an Entry Point + +Register your provider using the `commitizen.provider` entry point. You can do this in your `setup.py`, `setup.cfg`, or `pyproject.toml`: + +**Using `pyproject.toml` (recommended):** + +```toml title="pyproject.toml" +[project] +name = "my-commitizen-provider" +version = "0.1.0" +dependencies = ["commitizen"] + +[project.entry-points."commitizen.provider"] +my-provider = "my_provider:MyProvider" +``` + +**Using `setup.py` (for legacy setup):** + +```python title="setup.py" +from setuptools import setup + +setup( + name="my-commitizen-provider", + version="0.1.0", + py_modules=["my_provider"], + install_requires=["commitizen"], + entry_points={ + "commitizen.provider": [ + "my-provider = my_provider:MyProvider", + ] + }, +) +``` + +### Step 3: Install and Use + +1. Install your provider package: + ```bash + pip install -e . + ``` + +2. Configure Commitizen to use your provider: + ```toml + [tool.commitizen] + version_provider = "my-provider" + ``` + +### Provider Implementation Guidelines + +When creating a custom provider, keep these guidelines in mind: + +- **`get_version()`** should return a string representing the current version. If no version is found, you can return `"0.0.0"` or raise an appropriate exception. +- **`set_version(version: str)`** should write the version to your chosen storage location. The version string will be properly formatted according to your `version_scheme` setting. +- The provider has access to `self.config`, which is a `BaseConfig` instance containing all Commitizen settings. +- For file-based providers, consider using the `FileProvider` or `JsonProvider`/`TomlProvider` base classes from `commitizen.providers.base_provider` to simplify implementation. + +### Example: JSON-based Provider + +Here's a more complete example using the `JsonProvider` base class: + +```python title="json_version_provider.py" +from commitizen.providers.base_provider import JsonProvider + + +class JsonVersionProvider(JsonProvider): + """ + Version provider that uses a custom version.json file. + """ + + filename = "version.json" + + def get(self, document): + """Extract version from JSON document.""" + return document["version"] + + def set(self, document, version): + """Set version in JSON document.""" + document["version"] = version +``` + +This example leverages the `JsonProvider` base class, which handles file reading, writing, and JSON parsing automatically. + +## Choosing the Right Provider + +Select a version provider based on your project's characteristics: + +- **Python projects** + - **with `uv`**: Use `uv` + - **with `pyproject.toml` that follows PEP 621**: Use `pep621` + - **with Poetry**: Use `poetry` + - **setuptools-scm**: Use `scm` +- **Rust projects**: Use `cargo` +- **Node.js projects**: Use `npm` +- **PHP projects**: Use `composer` +- **Other cases or custom needs**: Use `commitizen` (default) or create a custom provider + +Remember that you can always use `version_files` in combination with any provider to update additional files during version bumps, regardless of which provider you choose for reading/writing the primary version. diff --git a/docs/customization.md b/docs/customization.md deleted file mode 100644 index 50bb0c8fbb..0000000000 --- a/docs/customization.md +++ /dev/null @@ -1,529 +0,0 @@ -Customizing Commitizen is not hard at all. -We have two different ways to do so. - -## 1. Customize in configuration file - -The basic steps are: - -1. Define your custom committing or bumping rules in the configuration file. -2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen. - -Example: - -```toml title="pyproject.toml" -[tool.commitizen] -name = "cz_customize" - -[tool.commitizen.customize] -message_template = "{{change_type}}:{% if show_message %} {{message}}{% endif %}" -example = "feature: this feature enable customize through config file" -schema = ": " -schema_pattern = "(feature|bug fix):(\\s.*)" -bump_pattern = "^(break|new|fix|hotfix)" -bump_map = {"break" = "MAJOR", "new" = "MINOR", "fix" = "PATCH", "hotfix" = "PATCH"} -change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] -info_path = "cz_customize_info.txt" -info = """ -This is customized info -""" -commit_parser = "^(?Pfeature|bug fix):\\s(?P.*)?" -changelog_pattern = "^(feature|bug fix)?(!)?" -change_type_map = {"feature" = "Feat", "bug fix" = "Fix"} - -[[tool.commitizen.customize.questions]] -type = "list" -name = "change_type" -choices = [{value = "feature", name = "feature: A new feature."}, {value = "bug fix", name = "bug fix: A bug fix."}] -# choices = ["feature", "fix"] # short version -message = "Select the type of change you are committing" - -[[tool.commitizen.customize.questions]] -type = "input" -name = "message" -message = "Body." - -[[tool.commitizen.customize.questions]] -type = "confirm" -name = "show_message" -message = "Do you want to add body message in commit?" -``` - -The equivalent example for a json config file: - -```json title=".cz.json" -{ - "commitizen": { - "name": "cz_customize", - "customize": { - "message_template": "{{change_type}}:{% if show_message %} {{message}}{% endif %}", - "example": "feature: this feature enable customize through config file", - "schema": ": ", - "schema_pattern": "(feature|bug fix):(\\s.*)", - "bump_pattern": "^(break|new|fix|hotfix)", - "bump_map": { - "break": "MAJOR", - "new": "MINOR", - "fix": "PATCH", - "hotfix": "PATCH" - }, - "change_type_order": ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"], - "info_path": "cz_customize_info.txt", - "info": "This is customized info", - "commit_parser": "^(?Pfeature|bug fix):\\s(?P.*)?", - "changelog_pattern": "^(feature|bug fix)?(!)?", - "change_type_map": {"feature": "Feat", "bug fix": "Fix"}, - "questions": [ - { - "type": "list", - "name": "change_type", - "choices": [ - { - "value": "feature", - "name": "feature: A new feature." - }, - { - "value": "bug fix", - "name": "bug fix: A bug fix." - } - ], - "message": "Select the type of change you are committing" - }, - { - "type": "input", - "name": "message", - "message": "Body." - }, - { - "type": "confirm", - "name": "show_message", - "message": "Do you want to add body message in commit?" - } - ] - } - } -} -``` - -And the correspondent example for a yaml file: - -```yaml title=".cz.yaml" -commitizen: - name: cz_customize - customize: - message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' - example: 'feature: this feature enable customize through config file' - schema: ': ' - schema_pattern: '(feature|bug fix):(\\s.*)' - bump_pattern: '^(break|new|fix|hotfix)' - commit_parser: '^(?Pfeature|bug fix):\\s(?P.*)?' - changelog_pattern: '^(feature|bug fix)?(!)?' - change_type_map: - feature: Feat - bug fix: Fix - bump_map: - break: MAJOR - new: MINOR - fix: PATCH - hotfix: PATCH - change_type_order: ['BREAKING CHANGE', 'feat', 'fix', 'refactor', 'perf'] - info_path: cz_customize_info.txt - info: This is customized info - questions: - - type: list - name: change_type - choices: - - value: feature - name: 'feature: A new feature.' - - value: bug fix - name: 'bug fix: A bug fix.' - message: Select the type of change you are committing - - type: input - name: message - message: 'Body.' - - type: confirm - name: show_message - message: 'Do you want to add body message in commit?' -``` - -### Customize configuration - -| Parameter | Type | Default | Description | -| ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | -| `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | -| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | -| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | -| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | -| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | -| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | -| `bump_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | -| `bump_pattern` | `str` | `None` | (OPTIONAL) Regex to extract information from commit (subject and body) | -| `change_type_order` | `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | -| `commit_parser` | `str` | `None` | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec] | -| `changelog_pattern` | `str` | `None` | (OPTIONAL) Regex to understand which commits to include in the changelog | -| `change_type_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry | - -[jinja2]: https://jinja.palletsprojects.com/en/2.10.x/ -[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/ - -#### Detailed `questions` content - -| Parameter | Type | Default | Description | -| ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | -| `name` | `str` | `None` | The key for the value answered by user. It's used in `message_template` | -| `message` | `str` | `None` | Detail description for the question. | -| `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | -| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | -| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. **(Work in Progress)** | -| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | -| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices. | -| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only. | - -[different-question-types]: https://github.com/tmbo/questionary#different-question-types - -#### Shortcut keys - -When the [`use_shortcuts`](config.md#settings) config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly. -For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically. -To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize. - -## 2. Customize through customizing a class - -The basic steps are: - -1. Inheriting from `BaseCommitizen`. -2. Give a name to your rules. -3. Create a python package using `setup.py`, `poetry`, etc. -4. Expose the class as a `commitizen.plugin` entrypoint. - -Check an [example][convcomms] on how to configure `BaseCommitizen`. - -You can also automate the steps above through [cookiecutter](https://cookiecutter.readthedocs.io/en/1.7.0/). - -```sh -cookiecutter gh:commitizen-tools/commitizen_cz_template -``` - -See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. - -Once you publish your rules, you can send us a PR to the [Third-party section](./third-party-plugins/about.md). - -### Custom commit rules - -Create a Python module, for example `cz_jira.py`. - -Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. - -```python title="cz_jira.py" -from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions - - -class JiraCz(BaseCommitizen): - # Questions = Iterable[MutableMapping[str, Any]] - # It expects a list with dictionaries. - def questions(self) -> Questions: - """Questions regarding the commit message.""" - questions = [ - {"type": "input", "name": "title", "message": "Commit title"}, - {"type": "input", "name": "issue", "message": "Jira Issue number:"}, - ] - return questions - - def message(self, answers: dict) -> str: - """Generate the message with the given answers.""" - return "{0} (#{1})".format(answers["title"], answers["issue"]) - - def example(self) -> str: - """Provide an example to help understand the style (OPTIONAL) - - Used by `cz example`. - """ - return "Problem with user (#321)" - - def schema(self) -> str: - """Show the schema used (OPTIONAL) - - Used by `cz schema`. - """ - return " (<issue>)" - - def info(self) -> str: - """Explanation of the commit rules. (OPTIONAL) - - Used by `cz info`. - """ - return "We use this because is useful" -``` - -The next file required is `setup.py` modified from flask version. - -```python title="setup.py" -from setuptools import setup - -setup( - name="JiraCommitizen", - version="0.1.0", - py_modules=["cz_jira"], - license="MIT", - long_description="this is a long description", - install_requires=["commitizen"], - entry_points={"commitizen.plugin": ["cz_jira = cz_jira:JiraCz"]}, -) -``` - -So in the end, we would have - - . - ├── cz_jira.py - └── setup.py - -And that's it. You can install it without uploading to pypi by simply -doing `pip install .` - -If you feel like it should be part of this repo, create a PR. - -### Custom bump rules - -You need to define 2 parameters inside your custom `BaseCommitizen`. - -| Parameter | Type | Default | Description | -| -------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | -| `bump_pattern` | `str` | `None` | Regex to extract information from commit (subject and body) | -| `bump_map` | `dict` | `None` | Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | - -Let's see an example. - -```python title="cz_strange.py" -from commitizen.cz.base import BaseCommitizen - - -class StrangeCommitizen(BaseCommitizen): - bump_pattern = r"^(break|new|fix|hotfix)" - bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"} -``` - -That's it, your Commitizen now supports custom rules, and you can run. - -```bash -cz -n cz_strange bump -``` - -[convcomms]: https://github.com/commitizen-tools/commitizen/blob/master/commitizen/cz/conventional_commits/conventional_commits.py - -### Custom changelog generator - -The changelog generator should just work in a very basic manner without touching anything. -You can customize it of course, and the following variables are the ones you need to add to your custom `BaseCommitizen`. - -| Parameter | Type | Required | Description | -| -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] | -| `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern | -| `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided | -| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | -| `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | -| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release - -```python title="cz_strange.py" -from commitizen.cz.base import BaseCommitizen -import chat -import compliance - - -class StrangeCommitizen(BaseCommitizen): - changelog_pattern = r"^(break|new|fix|hotfix)" - commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" - change_type_map = { - "feat": "Features", - "fix": "Bug Fixes", - "refactor": "Code Refactor", - "perf": "Performance improvements", - } - - def changelog_message_builder_hook( - self, parsed_message: dict, commit: git.GitCommit - ) -> dict | list | None: - rev = commit.rev - m = parsed_message["message"] - parsed_message[ - "message" - ] = f"{m} {rev} [{commit.author}]({commit.author_email})" - return parsed_message - - def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict: - release["author"] = tag.author - return release - - def changelog_hook( - self, full_changelog: str, partial_changelog: Optional[str] - ) -> str: - """Executed at the end of the changelog generation - - full_changelog: it's the output about to being written into the file - partial_changelog: it's the new stuff, this is useful to send slack messages or - similar - - Return: - the new updated full_changelog - """ - if partial_changelog: - chat.room("#committers").notify(partial_changelog) - if full_changelog: - compliance.send(full_changelog) - full_changelog.replace(" fix ", " **fix** ") - return full_changelog -``` - -### Raise Customize Exception - -If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`. - -```python -from commitizen.cz.exception import CzException - - -class NoSubjectProvidedException(CzException): - ... -``` - -### Migrating from legacy plugin format - -Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. -Migration should be straight-forward for legacy plugins: - -- Remove the `discover_this` line from your plugin module -- Expose the plugin class under as a `commitizen.plugin` entrypoint. - -The name of the plugin is now determined by the name of the entrypoint. - -#### Example - -If you were having a `CzPlugin` class in a `cz_plugin.py` module like this: - -```python -from commitizen.cz.base import BaseCommitizen - - -class PluginCz(BaseCommitizen): - ... - - -discover_this = PluginCz -``` - -Then remove the `discover_this` line: - -```python -from commitizen.cz.base import BaseCommitizen - - -class PluginCz(BaseCommitizen): - ... -``` - -and expose the class as entrypoint in you setuptools: - -```python -from setuptools import setup - -setup( - name="MyPlugin", - version="0.1.0", - py_modules=["cz_plugin"], - entry_points={"commitizen.plugin": ["plugin = cz_plugin:PluginCz"]}, - ..., -) -``` - -Then your plugin will be available under the name `plugin`. - -## Customizing the changelog template - -Commitizen gives you the possibility to provide your own changelog template, by: - -- providing one with your customization class -- providing one from the current working directory and setting it: - - as [configuration][template-config] - - as `--template` parameter to both `bump` and `changelog` commands -- either by providing a template with the same name as the default template - -By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository. - -### Providing a template with your customization class - -There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`. - -| Parameter | Type | Default | Description | -| ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | -| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | -| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from your customization class) | -| `template_extras` | `dict` | `None` | Provide some extra template parameters | - -Let's see an example. - -```python -from commitizen.cz.base import BaseCommitizen -from jinja2 import PackageLoader - - -class MyPlugin(BaseCommitizen): - template = "CHANGELOG.md.jinja" - template_loader = PackageLoader("my_plugin", "templates") - template_extras = {"key": "value"} -``` - -This snippet will: - -- use `CHANGELOG.md.jinja` as template name -- search for it in the `templates` directory for `my_plugin` package -- add the `key=value` variable in the template - -### Providing a template from the current working directory - -Users can provide their own template from their current working directory (your project root) by: - -- providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class) -- setting your template path as `template` configuration -- giving your template path as `--template` parameter to `bump` and `changelog` commands - -!!! note - The path is relative to the current working directory, aka your project root most of the time. - -### Template variables - -The default template use a single `tree` variable which is a list of entries (a release) with the following format: - -| Name | Type | Description | -| ---- | ---- | ----------- | -| version | `str` | The release version | -| date | `datetime` | The release date | -| changes | `list[tuple[str, list[Change]]]` | The release sorted changes list in the form `(type, changes)` | - -Each `Change` has the following fields: - -| Name | Type | Description | -| ---- | ---- | ----------- | -| scope | `str | None` | An optional scope | -| message | `str` | The commit message body | -| sha1 | `str` | The commit `sha1` | -| parents | `list[str]` | The parent commit(s) `sha1`(s) | -| author | `str` | The commit author name | -| author_email | `str` | The commit author email | - -!!! note - The field values depend on the customization class and/or the settings you provide - -The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case -is listing commits that belong to the same pull request. - -When using another template (either provided by a plugin or by yourself), you can also pass extra template variables -by: - -- defining them in your configuration with the [`extras` settings][extras-config] -- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands - -[template-config]: config.md#template -[extras-config]: config.md#extras -[changelog-des]: ./commands/changelog.md#description diff --git a/docs/customization/changelog_template.md b/docs/customization/changelog_template.md new file mode 100644 index 0000000000..60f40ebea7 --- /dev/null +++ b/docs/customization/changelog_template.md @@ -0,0 +1,85 @@ + +# Customizing the changelog template + +Commitizen gives you the possibility to provide your own changelog template, by: + +- providing one with your customization class +- providing one from the current working directory and setting it: + - Through the configuration file + - as `--template` parameter to both `bump` and `changelog` commands +- either by providing a template with the same name as the default template + +By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository. + +## Providing a template with your customization class + +There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`. + +| Parameter | Type | Default | Description | +| ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | +| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | +| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from your customization class) | +| `template_extras` | `dict` | `None` | Provide some extra template parameters | + +Let's see an example. + +```python +from commitizen.cz.base import BaseCommitizen +from jinja2 import PackageLoader + + +class MyPlugin(BaseCommitizen): + template = "CHANGELOG.md.jinja" + template_loader = PackageLoader("my_plugin", "templates") + template_extras = {"key": "value"} +``` + +This snippet will: + +- use `CHANGELOG.md.jinja` as template name +- search for it in the `templates` directory for `my_plugin` package +- add the `key=value` variable in the template + +## Providing a template from the current working directory + +Users can provide their own template from their current working directory (your project root) by: + +- providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class) +- setting your template path as `template` configuration +- giving your template path as `--template` parameter to `bump` and `changelog` commands + +!!! note + The path is relative to the current working directory, aka your project root most of the time. + +## Template variables + +The default template use a single `tree` variable which is a list of entries (a release) with the following format: + +| Name | Type | Description | +| ---- | ---- | ----------- | +| version | `str` | The release version | +| date | `datetime` | The release date | +| changes | `list[tuple[str, list[Change]]]` | The release sorted changes list in the form `(type, changes)` | + +Each `Change` has the following fields: + +| Name | Type | Description | +| ---- | ---- | ----------- | +| scope | `str | None` | An optional scope | +| message | `str` | The commit message body | +| sha1 | `str` | The commit `sha1` | +| parents | `list[str]` | The parent commit(s) `sha1`(s) | +| author | `str` | The commit author name | +| author_email | `str` | The commit author email | + +!!! note + The field values depend on the customization class and/or the settings you provide + +The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case +is listing commits that belong to the same pull request. + +When using another template (either provided by a plugin or by yourself), you can also pass extra template variables +by: + +- defining them in your configuration with the `extras` settings +- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands diff --git a/docs/customization/config_file.md b/docs/customization/config_file.md new file mode 100644 index 0000000000..a5440342bf --- /dev/null +++ b/docs/customization/config_file.md @@ -0,0 +1,187 @@ +# Customize in configuration file + +The basic steps are: + +1. Define your custom committing or bumping rules in the configuration file. +2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen. + +Example: + +```toml title="pyproject.toml" +[tool.commitizen] +name = "cz_customize" + +[tool.commitizen.customize] +message_template = "{{change_type}}:{% if show_message %} {{message}}{% endif %}" +example = "feature: this feature enable customize through config file" +schema = "<type>: <body>" +schema_pattern = "(feature|bug fix):(\\s.*)" +bump_pattern = "^(break|new|fix|hotfix)" +bump_map = {"break" = "MAJOR", "new" = "MINOR", "fix" = "PATCH", "hotfix" = "PATCH"} +change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] +info_path = "cz_customize_info.txt" +info = """ +This is customized info +""" +commit_parser = "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?" +changelog_pattern = "^(feature|bug fix)?(!)?" +change_type_map = {"feature" = "Feat", "bug fix" = "Fix"} + +[[tool.commitizen.customize.questions]] +type = "list" +name = "change_type" +choices = [{value = "feature", name = "feature: A new feature."}, {value = "bug fix", name = "bug fix: A bug fix."}] +# choices = ["feature", "fix"] # short version +message = "Select the type of change you are committing" + +[[tool.commitizen.customize.questions]] +type = "input" +name = "message" +message = "Body." + +[[tool.commitizen.customize.questions]] +type = "confirm" +name = "show_message" +message = "Do you want to add body message in commit?" +``` + +The equivalent example for a json config file: + +```json title=".cz.json" +{ + "commitizen": { + "name": "cz_customize", + "customize": { + "message_template": "{{change_type}}:{% if show_message %} {{message}}{% endif %}", + "example": "feature: this feature enable customize through config file", + "schema": "<type>: <body>", + "schema_pattern": "(feature|bug fix):(\\s.*)", + "bump_pattern": "^(break|new|fix|hotfix)", + "bump_map": { + "break": "MAJOR", + "new": "MINOR", + "fix": "PATCH", + "hotfix": "PATCH" + }, + "change_type_order": ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"], + "info_path": "cz_customize_info.txt", + "info": "This is customized info", + "commit_parser": "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?", + "changelog_pattern": "^(feature|bug fix)?(!)?", + "change_type_map": {"feature": "Feat", "bug fix": "Fix"}, + "questions": [ + { + "type": "list", + "name": "change_type", + "choices": [ + { + "value": "feature", + "name": "feature: A new feature." + }, + { + "value": "bug fix", + "name": "bug fix: A bug fix." + } + ], + "message": "Select the type of change you are committing" + }, + { + "type": "input", + "name": "message", + "message": "Body." + }, + { + "type": "confirm", + "name": "show_message", + "message": "Do you want to add body message in commit?" + } + ] + } + } +} +``` + +And the correspondent example for a yaml file: + +```yaml title=".cz.yaml" +commitizen: + name: cz_customize + customize: + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' + example: 'feature: this feature enable customize through config file' + schema: '<type>: <body>' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' + change_type_map: + feature: Feat + bug fix: Fix + bump_map: + break: MAJOR + new: MINOR + fix: PATCH + hotfix: PATCH + change_type_order: ['BREAKING CHANGE', 'feat', 'fix', 'refactor', 'perf'] + info_path: cz_customize_info.txt + info: This is customized info + questions: + - type: list + name: change_type + choices: + - value: feature + name: 'feature: A new feature.' + - value: bug fix + name: 'bug fix: A bug fix.' + message: Select the type of change you are committing + - type: input + name: message + message: 'Body.' + - type: confirm + name: show_message + message: 'Do you want to add body message in commit?' +``` + +## Configuration File Options + +| Parameter | Type | Default | Description | +| ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | +| `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | +| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | +| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | +| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | +| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | +| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | +| `bump_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | +| `bump_pattern` | `str` | `None` | (OPTIONAL) Regex to extract information from commit (subject and body) | +| `change_type_order` | `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | +| `commit_parser` | `str` | `None` | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec] | +| `changelog_pattern` | `str` | `None` | (OPTIONAL) Regex to understand which commits to include in the changelog | +| `change_type_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry | + +[jinja2]: https://jinja.palletsprojects.com/en/2.10.x/ +[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/ + +### Detailed `questions` content + +| Parameter | Type | Default | Description | +| ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | +| `name` | `str` | `None` | The key for the value answered by user. It's used in `message_template` | +| `message` | `str` | `None` | Detail description for the question. | +| `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | +| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | +| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. **(Work in Progress)** | +| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | +| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices. | +| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only. | + +[different-question-types]: https://github.com/tmbo/questionary#different-question-types + +### Shortcut keys + +When the `use_shortcuts` config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly. +For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. +Unless otherwise defined, keyboard shortcuts will be numbered automatically. +To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize. diff --git a/docs/customization/python_class.md b/docs/customization/python_class.md new file mode 100644 index 0000000000..c698b03dee --- /dev/null +++ b/docs/customization/python_class.md @@ -0,0 +1,310 @@ +# Customizing through a python class + +The basic steps are: + +1. Inheriting from `BaseCommitizen`. +2. Give a name to your rules. +3. Create a Python package using proper [build backend](https://packaging.python.org/en/latest/glossary/#term-Build-Backend) +4. Expose the class as a `commitizen.plugin` entrypoint. + +Check an [example][convcomms] on how to configure `BaseCommitizen`. + +You can also automate the steps above through [cookiecutter](https://cookiecutter.readthedocs.io/en/1.7.0/). + +```sh +cookiecutter gh:commitizen-tools/commitizen_cz_template +``` + +See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. + +See [Third-party plugins](../third-party-plugins/about.md) for more details on how to create a third-party Commitizen plugin. + +## Custom commit rules + +Create a Python module, for example `cz_jira.py`. + +Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. + +```python title="cz_jira.py" +from commitizen.cz.base import BaseCommitizen +from commitizen.defaults import Questions + + +class JiraCz(BaseCommitizen): + # Questions = Iterable[MutableMapping[str, Any]] + # It expects a list with dictionaries. + def questions(self) -> Questions: + """Questions regarding the commit message.""" + questions = [ + {"type": "input", "name": "title", "message": "Commit title"}, + {"type": "input", "name": "issue", "message": "Jira Issue number:"}, + ] + return questions + + def message(self, answers: dict) -> str: + """Generate the message with the given answers.""" + return f"answers['title'] (#answers['issue'])" + + def example(self) -> str: + """Provide an example to help understand the style (OPTIONAL) + + Used by `cz example`. + """ + return "Problem with user (#321)" + + def schema(self) -> str: + """Show the schema used (OPTIONAL) + + Used by `cz schema`. + """ + return "<title> (<issue>)" + + def info(self) -> str: + """Explanation of the commit rules. (OPTIONAL) + + Used by `cz info`. + """ + return "We use this because is useful" +``` + +The next file required is `setup.py` modified from flask version. + +```python title="setup.py" +from setuptools import setup + +setup( + name="JiraCommitizen", + version="0.1.0", + py_modules=["cz_jira"], + license="MIT", + long_description="this is a long description", + install_requires=["commitizen"], + entry_points={"commitizen.plugin": ["cz_jira = cz_jira:JiraCz"]}, +) +``` + +So in the end, we would have + + . + ├── cz_jira.py + └── setup.py + +And that's it. You can install it without uploading to PyPI by simply +doing `pip install .` + +## Custom bump rules + +You need to define 2 parameters inside your custom `BaseCommitizen`. + +| Parameter | Type | Default | Description | +| -------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | +| `bump_pattern` | `str` | `None` | Regex to extract information from commit (subject and body) | +| `bump_map` | `dict` | `None` | Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | + +Let's see an example. + +```python title="cz_strange.py" +from commitizen.cz.base import BaseCommitizen + + +class StrangeCommitizen(BaseCommitizen): + bump_pattern = r"^(break|new|fix|hotfix)" + bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"} +``` + +That's it, your Commitizen now supports custom rules, and you can run. + +```bash +cz -n cz_strange bump +``` + +[convcomms]: https://github.com/commitizen-tools/commitizen/blob/master/commitizen/cz/conventional_commits/conventional_commits.py + +### Custom commit validation and error message + +The commit message validation can be customized by overriding the `validate_commit_message` and `format_error_message` +methods from `BaseCommitizen`. This allows for a more detailed feedback to the user where the error originates from. + +```python +import re +from commitizen.cz.base import BaseCommitizen, ValidationResult +from commitizen import git + + +class CustomValidationCz(BaseCommitizen): + def validate_commit_message( + self, + *, + commit_msg: str, + pattern: str | None, + allow_abort: bool, + allowed_prefixes: list[str], + max_msg_length: int, + ) -> ValidationResult: + """Validate commit message against the pattern.""" + if not commit_msg: + return allow_abort, [] if allow_abort else [f"commit message is empty"] + if pattern is None: + return True, [] + if any(map(commit_msg.startswith, allowed_prefixes)): + return True, [] + if max_msg_length: + msg_len = len(commit_msg.partition("\n")[0].strip()) + if msg_len > max_msg_length: + return False, [ + f"commit message is too long. Max length is {max_msg_length}" + ] + pattern_match = re.match(pattern, commit_msg) + if pattern_match: + return True, [] + else: + # Perform additional validation of the commit message format + # and add custom error messages as needed + return False, ["commit message does not match the pattern"] + + def format_exception_message( + self, ill_formatted_commits: list[tuple[git.GitCommit, list]] + ) -> str: + """Format commit errors.""" + displayed_msgs_content = "\n".join( + ( + f'commit "{commit.rev}": "{commit.message}"' + f"errors:\n" + "\n".join((f"- {error}" for error in errors)) + ) + for commit, errors in ill_formatted_commits + ) + return ( + "commit validation: failed!\n" + "please enter a commit message in the commitizen format.\n" + f"{displayed_msgs_content}\n" + f"pattern: {self.schema_pattern()}" + ) +``` + +## Custom changelog generator + +The changelog generator should just work in a very basic manner without touching anything. +You can customize it of course, and the following variables are the ones you need to add to your custom `BaseCommitizen`. + +| Parameter | Type | Required | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] | +| `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern | +| `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided | +| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | +| `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | +| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release + +```python title="cz_strange.py" +from commitizen.cz.base import BaseCommitizen +import chat +import compliance + + +class StrangeCommitizen(BaseCommitizen): + changelog_pattern = r"^(break|new|fix|hotfix)" + commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" + change_type_map = { + "feat": "Features", + "fix": "Bug Fixes", + "refactor": "Code Refactor", + "perf": "Performance improvements", + } + + def changelog_message_builder_hook( + self, parsed_message: dict, commit: git.GitCommit + ) -> dict | list | None: + rev = commit.rev + m = parsed_message["message"] + parsed_message[ + "message" + ] = f"{m} {rev} [{commit.author}]({commit.author_email})" + return parsed_message + + def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict: + release["author"] = tag.author + return release + + def changelog_hook( + self, full_changelog: str, partial_changelog: Optional[str] + ) -> str: + """Executed at the end of the changelog generation + + full_changelog: it's the output about to being written into the file + partial_changelog: it's the new stuff, this is useful to send slack messages or + similar + + Return: + the new updated full_changelog + """ + if partial_changelog: + chat.room("#committers").notify(partial_changelog) + if full_changelog: + compliance.send(full_changelog) + full_changelog.replace(" fix ", " **fix** ") + return full_changelog +``` + +## Raise Customize Exception + +If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`. + +```python +from commitizen.cz.exception import CzException + + +class NoSubjectProvidedException(CzException): + ... +``` + +## Migrating from legacy plugin format + +Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. +Migration should be straight-forward for legacy plugins: + +- Remove the `discover_this` line from your plugin module +- Expose the plugin class under as a `commitizen.plugin` entrypoint. + +The name of the plugin is now determined by the name of the entrypoint. + +### Example + +If you were having a `CzPlugin` class in a `cz_plugin.py` module like this: + +```python +from commitizen.cz.base import BaseCommitizen + + +class PluginCz(BaseCommitizen): + ... + + +discover_this = PluginCz +``` + +Then remove the `discover_this` line: + +```python +from commitizen.cz.base import BaseCommitizen + + +class PluginCz(BaseCommitizen): + ... +``` + +and expose the class as entrypoint in your `setuptools`: + +```python +from setuptools import setup + +setup( + name="MyPlugin", + version="0.1.0", + py_modules=["cz_plugin"], + entry_points={"commitizen.plugin": ["plugin = cz_plugin:PluginCz"]}, + ..., +) +``` + +Then your plugin will be available under the name `plugin`. diff --git a/docs/faq.md b/docs/faq.md index 22cd9e8d6c..9de0c43e41 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -10,7 +10,8 @@ name = "spam" version = "2.5.1" ``` -Commitizen provides a `PEP621` [version provider](config.md#version-providers) to get and set version from this field. +Commitizen provides a [`PEP621` version provider](config/version_provider.md) to get and set version from this field. + You just need to set the proper `version_provider` setting: ```toml @@ -24,8 +25,8 @@ version_provider = "pep621" ## Why are `revert` and `chore` valid types in the check pattern of `cz_conventional_commits` but not types we can select? -`revert` and `chore` are added to `pattern` in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). -However, you can create a customized `cz` with those extra types. See [Customization](customization.md) for more details. +`revert` and `chore` are added to the `pattern` in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). +However, you can create a customized `cz` with those extra types. (See [Customization](customization/config_file.md)). See more discussion in - [issue #142](https://github.com/commitizen-tools/commitizen/issues/142) @@ -128,7 +129,7 @@ If you would like to learn more about both schemes, there are plenty of good res ## How to change the tag format ? -You can use the [`legacy_tag_formats`](config.md#legacy_tag_formats) to list old tag formats. +You can use the [`legacy_tag_formats`](config/bump.md#legacy_tag_formats) to list old tag formats. New bumped tags will be in the new format but old ones will still work for: - changelog generation (full, incremental and version range) - bump new version computation (automatically guessed or increment given) @@ -147,7 +148,7 @@ legacy_tag_formats = [ ## How to avoid warnings for expected non-version tags -You can explicitly ignore them with [`ignored_tag_formats`](config.md#ignored_tag_formats). +You can explicitly ignore them with [`ignored_tag_formats`](config/bump.md#ignored_tag_formats). ```toml tag_format = "v${version}" diff --git a/docs/tutorials/tag_format.md b/docs/tutorials/tag_format.md index 8408b4c801..2d439e38ca 100644 --- a/docs/tutorials/tag_format.md +++ b/docs/tutorials/tag_format.md @@ -42,8 +42,7 @@ As a result, the tag generated on bump will have this format: `v1.0.0` and the v !!! note Both `$version` and `${version}` syntaxes are strictly equivalent. You can use the one you prefer. -See [the `version_scheme` section in `bump` command documentation](../commands/bump.md#version_scheme) for more details on version schemes and how to define your own. -See [`tag_format`](../config.md#tag_format) and [`version_scheme`](../config.md#version_scheme) settings in [Configuration reference](../config.md) for more details on these settings. +See [`tag_format`](../commands/bump.md#-tag-format) and [`version_scheme`](../commands/bump.md#-version-scheme) settings for more details. ## Changing convention @@ -71,7 +70,7 @@ Your new tag will be in the form `component-${version}`. Now let's say you have some known tags you want to ignore, either because they are not versions, or because they are not versions of the component you are dealing with. As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format. -Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config.md#ignored_tag_formats) setting: +Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config/bump.md#ignored_tag_formats) setting: ```yaml [tool.commitizen] diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md index a64480874a..a56d6494d4 100644 --- a/docs/tutorials/writing_commits.md +++ b/docs/tutorials/writing_commits.md @@ -44,8 +44,8 @@ Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the | `fix(commands): bump error when no user provided` | `fix: stuff` | | `feat: add new commit command` | `feat: commit command introduced` | -[customization]: ../customization.md +[customization]: ../customization/config_file.md [conventional_commits]: https://www.conventionalcommits.org [cz_emoji]: https://commitizen-tools.github.io/commitizen/third-party-commitizen/#cz-emoji -[configuration]: ../config.md#encoding -[breaking-change-config]: ../config.md#breaking_change_exclamation_in_title +[configuration]: ../config/commit.md#encoding +[breaking-change-config]: ../config/commit.md#breaking_change_exclamation_in_title diff --git a/mkdocs.yml b/mkdocs.yml index 8a55bb23fa..ac1fbfa097 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,8 +42,18 @@ nav: - ls: "commands/ls.md" - schema: "commands/schema.md" - version: "commands/version.md" - - Configuration: "config.md" - - Customization: "customization.md" + - Configuration: + - Configuration File: "config/configuration_file.md" + - Version Provider: "config/version_provider.md" + - bump: "config/bump.md" + - commit: "config/commit.md" + - check: "config/check.md" + - changelog: "config/changelog.md" + - Misc Options: "config/option.md" + - Advanced Customization: + - Configuration File: "customization/config_file.md" + - Customized Python Class: "customization/python_class.md" + - Changelog Template: "customization/changelog_template.md" - Tutorials: - Writing commits: "tutorials/writing_commits.md" - Managing tags formats: "tutorials/tag_format.md" From 3cb9b2ce0abbd4889d5c8a5f6bb958828fbe97f1 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Wed, 10 Dec 2025 22:53:22 +0800 Subject: [PATCH 252/275] docs(exit_codes.md): move "ignoring errors" from bump doc to exit_code doc --- docs/commands/bump.md | 65 +--------------- docs/exit_codes.md | 169 +++++++++++++++++++++++++++++++++--------- 2 files changed, 137 insertions(+), 97 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 78ff9777cf..7bbc9b6c91 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -431,7 +431,7 @@ Useful for determining the next version based on CI for non-production environme !!! warning The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump. - For information on how to suppress this exit, see [avoid raising errors](#avoid-raising-errors). + For information on how to suppress this exit, see [Ignoring Exit Codes](../exit_codes.md#ignoring-exit-codes). ### `--allow-no-commit` @@ -505,69 +505,6 @@ Supported variables: | `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) | | `$devrelease`, `${devrelease}` | Development release | -## Avoid raising errors - -Some situations from Commitizen raise an exit code different from 0. -If the error code is different from 0, any CI or script running Commitizen might be interrupted. - -If you have a special use case, where you don't want to raise one of these error codes, you can -tell Commitizen to not raise them. - -### Recommended use case - -At the moment, we've identified that the most common error code to skip is - -| Error name | Exit code | -| ----------------- | --------- | -| NoneIncrementExit | 21 | - -There are some situations where you don't want to get an error code when some -commits do not match your rules, you just want those commits to be skipped. - -```sh -cz -nr 21 bump -``` - -### Easy way - -Check which error code was raised by Commitizen by running in the terminal - -```sh -echo $? -``` - -The output should be an integer like this - -```sh -3 -``` - -And then you can tell Commitizen to ignore it: - -```sh -cz --no-raise 3 -``` - -You can tell Commitizen to skip more than one if needed: - -```sh -cz --no-raise 3,4,5 -``` - -### Longer way - -Check the list of [exit_codes](../exit_codes.md) and understand which one you have -to skip and why. - -Remember to document somewhere this, because you'll forget. - -For example if the system raises a `NoneIncrementExit` error, you look it up -on the list, and then you can use the exit code: - -```sh -cz -nr 21 bump -``` - [pep440]: https://www.python.org/dev/peps/pep-0440/ [semver]: https://semver.org/ [version_files]: ../config/bump.md#version_files diff --git a/docs/exit_codes.md b/docs/exit_codes.md index fd92961d38..1a214e2832 100644 --- a/docs/exit_codes.md +++ b/docs/exit_codes.md @@ -1,39 +1,142 @@ # Exit Codes -Commitizen handles expected exceptions through `CommitizenException` and returns different exit codes for different situations. They could be useful if you want to ignore specific errors in your pipeline. +Commitizen handles expected exceptions through `CommitizenException` and returns different exit codes for different situations. This reference is useful when you need to ignore specific errors in your CI/CD pipeline or automation scripts. -These exit codes can be found in `commitizen/exceptions.py::ExitCode`. +All exit codes are defined in [commitizen/exceptions.py](https://github.com/commitizen-tools/commitizen/blob/master/commitizen/exceptions.py). + +## Exit Code Reference | Exception | Exit Code | Description | | --------------------------- | --------- | ----------------------------------------------------------------------------------------------------------- | -| ExpectedExit | 0 | Expected exit | -| DryRunExit | 0 | Exit due to passing `--dry-run` option | -| NoCommitizenFoundException | 1 | Using a cz (e.g., `cz_jira`) that cannot be found in your system | -| NotAGitProjectError | 2 | Not in a git project | -| NoCommitsFoundError | 3 | No commit found | -| NoVersionSpecifiedError | 4 | Version can not be found in configuration file | -| NoPatternMapError | 5 | bump / changelog pattern or map can not be found in configuration file | -| BumpCommitFailedError | 6 | Commit error when bumping version | -| BumpTagFailedError | 7 | Tag error when bumping version | -| NoAnswersError | 8 | No user response given | -| CommitError | 9 | git commit error | -| NoCommitBackupError | 10 | Commit back up file cannot be found | -| NothingToCommitError | 11 | Nothing in staging to be committed | -| CustomError | 12 | `CzException` raised | -| NoCommandFoundError | 13 | No command found when running Commitizen cli (e.g., `cz --debug`) | -| InvalidCommitMessageError | 14 | The commit message does not pass `cz check` | -| MissingConfigError | 15 | Configuration missed for `cz_customize` | -| NoRevisionError | 16 | No revision found | -| CurrentVersionNotFoundError | 17 | current version cannot be found in _version_files_ | -| InvalidCommandArgumentError | 18 | The argument provide to command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`) | -| InvalidConfigurationError | 19 | An error was found in the Commitizen Configuration, such as duplicates in `change_type_order` | -| NotAllowed | 20 | `--incremental` cannot be combined with a `rev_range` | -| NoneIncrementExit | 21 | The commits found are not eligible to be bumped | -| CharacterSetDecodeError | 22 | The character encoding of the command output could not be determined | -| GitCommandError | 23 | Unexpected failure while calling a git command | -| InvalidManualVersion | 24 | Manually provided version is invalid | -| InitFailedError | 25 | Failed to initialize pre-commit | -| RunHookError | 26 | An error occurred during a hook execution | -| VersionProviderUnknown | 27 | `version_provider` setting is set to an unknown version provider identifier | -| VersionSchemeUnknown | 28 | `version_scheme` setting is set to an unknown version scheme identifier | -| ChangelogFormatUnknown | 29 | `changelog_format` setting is set to an unknown version scheme identifier or could not be guessed | +| `ExpectedExit` | 0 | Expected exit | +| `DryRunExit` | 0 | Exit due to passing `--dry-run` option | +| `NoCommitizenFoundException` | 1 | Using a cz (e.g., `cz_jira`) that cannot be found in your system | +| `NotAGitProjectError` | 2 | Not in a git project | +| `NoCommitsFoundError` | 3 | No commits found | +| `NoVersionSpecifiedError` | 4 | Version is not specified in configuration file | +| `NoPatternMapError` | 5 | bump / changelog pattern or map can not be found in configuration file | +| `BumpCommitFailedError` | 6 | Commit failed when bumping version | +| `BumpTagFailedError` | 7 | Tag failed when bumping version | +| `NoAnswersError` | 8 | No user response given | +| `CommitError` | 9 | git commit error | +| `NoCommitBackupError` | 10 | Commit backup file is not found | +| `NothingToCommitError` | 11 | Nothing in staging to be committed | +| `CustomError` | 12 | `CzException` raised | +| `NoCommandFoundError` | 13 | No command found when running Commitizen cli (e.g., `cz --debug`) | +| `InvalidCommitMessageError` | 14 | The commit message does not pass `cz check` | +| `MissingConfigError` | 15 | Configuration is missing for `cz_customize` | +| `NoRevisionError` | 16 | No revision found | +| `CurrentVersionNotFoundError`| 17 | Current version cannot be found in `version_files` | +| `InvalidCommandArgumentError`| 18 | The argument provided to the command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`) | +| `InvalidConfigurationError` | 19 | An error was found in the Commitizen Configuration, such as duplicates in `change_type_order` | +| `NotAllowed` | 20 | Invalid combination of command line / configuration file options | +| `NoneIncrementExit` | 21 | The commits found are not eligible to be bumped | +| `CharacterSetDecodeError` | 22 | The character encoding of the command output could not be determined | +| `GitCommandError` | 23 | Unexpected failure while calling a git command | +| `InvalidManualVersion` | 24 | Manually provided version is invalid | +| `InitFailedError` | 25 | Failed to initialize pre-commit | +| `RunHookError` | 26 | An error occurred during a hook execution | +| `VersionProviderUnknown` | 27 | Unknown `version_provider` | +| `VersionSchemeUnknown` | 28 | Unknown `version_scheme` | +| `ChangelogFormatUnknown` | 29 | Unknown `changelog_format` or cannot be determined by the file extension | +| `ConfigFileNotFound` | 30 | The configuration file is not found | +| `ConfigFileIsEmpty` | 31 | The configuration file is empty | +| `CommitMessageLengthLimitExceededError`| 32 | The commit message length exceeds the given limit. | + +## Ignoring Exit Codes + +In some scenarios, you may want Commitizen to continue execution even when certain errors occur. This is particularly useful in CI/CD pipelines where you want to handle specific errors gracefully. + +### Using `--no-raise` Flag + +The `--no-raise` (or `-nr`) flag allows you to specify exit codes that should not cause Commitizen to exit with an error. You can use either: + +- **Exit code numbers**: `21`, `3`, `4` +- **Exit code names**: `NO_INCREMENT`, `NO_COMMITS_FOUND`, `NO_VERSION_SPECIFIED` +- **Mixed format**: `21,NO_COMMITS_FOUND,4` + +Multiple exit codes can be specified as a comma-separated list. + +### Common Use Cases + +#### Ignoring No Increment Errors + +The most common use case is to ignore `NoneIncrementExit` (exit code 21) when running `cz bump`. This allows the command to succeed even when no commits are eligible for a version bump: + +```sh +cz -nr 21 bump +``` + +Or using the exit code name: + +```sh +cz -nr NO_INCREMENT bump +``` + +This is useful in CI pipelines where you want to run `cz bump` regularly, but don't want the pipeline to fail when there are no version-worthy commits. + +#### Ignoring Multiple Exit Codes + +You can ignore multiple exit codes at once: + +```sh +cz --no-raise 21,3,4 bump +``` + +This example ignores: + +- `21` (`NoneIncrementExit`) - No eligible commits for bump +- `3` (`NoCommitsFoundError`) - No commits found +- `4` (`NoVersionSpecifiedError`) - Version not specified + +### Finding the Exit Code + +If you encounter an error and want to ignore it, you can find the exit code in two ways: + +#### Method 1: Check the Exit Code After Running + +After running a Commitizen command that fails, check the exit code: + +```sh +cz bump +echo $? # Prints the exit code (e.g., 21) +``` + +Then use that exit code with `--no-raise`: + +```sh +cz -nr 21 bump +``` + +#### Method 2: Look Up the Exception + +1. Check the error message to identify the exception type +2. Find the corresponding exit code in the table above +3. Use that exit code with `--no-raise` + +For example, if you see `NoneIncrementExit` in the error, look it up in the table to find it's exit code 21, then use: + +```sh +cz -nr 21 bump +``` + +### Best Practices + +- **Document your usage**: If you use `--no-raise` in scripts or CI/CD, document why specific exit codes are ignored +- **Be specific**: Only ignore exit codes you understand and have a reason to ignore +- **Test thoroughly**: Ensure that ignoring certain exit codes doesn't mask real problems in your workflow +- **Use exit code names**: When possible, use exit code names (e.g., `NO_INCREMENT`) instead of numbers for better readability + +### Example: CI/CD Pipeline + +Here's an example of using `--no-raise` in a CI/CD pipeline: + +```yaml +# .github/workflows/release.yml +- name: Bump version + run: | + cz -nr NO_INCREMENT bump || true + # Continue even if no version bump is needed +``` + +This ensures the pipeline continues even when there are no commits eligible for a version bump. From d7cf81cf7e3bb156900a4ac3ecbd2f5dc0da0bd3 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Wed, 10 Dec 2025 22:43:19 +0800 Subject: [PATCH 253/275] docs(bump): fix broken documentation format --- docs/commands/bump.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/commands/bump.md b/docs/commands/bump.md index 7bbc9b6c91..e20a20d5f7 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -437,15 +437,15 @@ Useful for determining the next version based on CI for non-production environme Allow the project version to be bumped even when there's no eligible version. -!!! note "Example usage" - ```sh - # bump a minor version even when there's only bug fixes, documentation changes or even no commits +Example usage: - cz bump --increment MINOR --allow-no-commit +```sh +# Force to bump a minor version +cz bump --increment MINOR --allow-no-commit - # bump version to 2.0.0 even when there's no breaking changes or even no commits - cz bump --allow-no-commit 2.0.0 - ``` +# bump version to 2.0.0 even when there's no breaking changes or even no commits +cz bump --allow-no-commit 2.0.0 +``` !!! note "Default increment" The increment is overridden to `PATCH` if there is no increment detected or specified. @@ -455,7 +455,6 @@ Allow the project version to be bumped even when there's no eligible version. ```sh # will bump to `1.0.1` if the current version is `1.0.0`. cz bump --allow-no-commit - ``` # bump version to 2.0.0 even when there's no breaking changes or even no commits cz bump --allow-no-commit 2.0.0 From 6e8edcd7f4e5ad7f52f32f921227c2f110d6cac1 Mon Sep 17 00:00:00 2001 From: Wei Lee <weilee.rx@gmail.com> Date: Sun, 16 Nov 2025 22:59:40 +0800 Subject: [PATCH 254/275] ci(github-actions): add python 3.14 to github-actions and tox --- .github/workflows/pythonpackage.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 2c332391db..4e4738ac90 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,7 +6,7 @@ jobs: python-check: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] platform: [ubuntu-22.04, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/pyproject.toml b/pyproject.toml index 01b16fec3c..9b15e6e13c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -173,7 +173,7 @@ testpaths = ["tests/"] [tool.tox] requires = ["tox>=4.22"] -env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"] +env_list = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] [tool.tox.env_run_base] description = "Run tests suite against Python {base_python}" From 04d530425367c52edd57fd7c9d31c98296524f68 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Mon, 17 Nov 2025 23:35:59 +0800 Subject: [PATCH 255/275] refactor(bump): rename parameter and variables --- commitizen/bump.py | 4 ++-- commitizen/commands/bump.py | 23 ++++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 6672c5f509..a955c20b6c 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -62,7 +62,7 @@ def find_increment( def update_version_in_files( current_version: str, new_version: str, - files: Iterable[str], + version_files: Iterable[str], *, check_consistency: bool, encoding: str, @@ -77,7 +77,7 @@ def update_version_in_files( """ updated_files = [] - for path, pattern in _resolve_files_and_regexes(files, current_version): + for path, pattern in _resolve_files_and_regexes(version_files, current_version): current_version_found = False bumped_lines = [] diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 3dc678920e..5cad643b81 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -302,10 +302,10 @@ def __call__(self) -> None: "[NO_COMMITS_TO_BUMP]\nThe commits found are not eligible to be bumped" ) - files: list[str] = [] + updated_files: list[str] = [] dry_run = self.arguments["dry_run"] if self.changelog_flag: - args = { + changelog_args = { "unreleased_version": new_tag_version, "template": self.template, "extras": self.extras, @@ -313,22 +313,27 @@ def __call__(self) -> None: "dry_run": dry_run, } if self.changelog_to_stdout: - changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) # type: ignore[typeddict-item] + changelog_cmd = Changelog( + self.config, + {**changelog_args, "dry_run": True}, # type: ignore[typeddict-item] + ) try: changelog_cmd() except DryRunExit: pass - args["file_name"] = self.file_name - changelog_cmd = Changelog(self.config, args) # type: ignore[arg-type] + changelog_cmd = Changelog( + self.config, + {**changelog_args, "file_name": self.file_name}, # type: ignore[typeddict-item] + ) changelog_cmd() - files.append(changelog_cmd.file_name) + updated_files.append(changelog_cmd.file_name) # Do not perform operations over files or git. if dry_run: raise DryRunExit() - files.extend( + updated_files.extend( bump.update_version_in_files( str(current_version), str(new_version), @@ -360,13 +365,13 @@ def __call__(self) -> None: raise ExpectedExit() # FIXME: check if any changes have been staged - git.add(*files) + git.add(*updated_files) c = git.commit(message, args=self._get_commit_args()) if self.retry and c.return_code != 0 and self.changelog_flag: # Maybe pre-commit reformatted some files? Retry once logger.debug("1st git.commit error: %s", c.err) logger.info("1st commit attempt failed; retrying once") - git.add(*files) + git.add(*updated_files) c = git.commit(message, args=self._get_commit_args()) if c.return_code != 0: err = c.err.strip() or c.out From b91ceb1f1770dc3ad4d494a023a151623ab0674c Mon Sep 17 00:00:00 2001 From: Wei Lee <weilee.rx@gmail.com> Date: Sat, 8 Nov 2025 19:10:56 +0800 Subject: [PATCH 256/275] build: update dev dependencies --- poetry.lock | 227 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 157 insertions(+), 70 deletions(-) diff --git a/poetry.lock b/poetry.lock index 80051bf4de..e7600db5d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -440,15 +440,15 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev", "test"] markers = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] @@ -675,6 +675,92 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "librt" +version = "0.7.3" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["linters"] +files = [ + {file = "librt-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2682162855a708e3270eba4b92026b93f8257c3e65278b456c77631faf0f4f7a"}, + {file = "librt-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:440c788f707c061d237c1e83edf6164ff19f5c0f823a3bf054e88804ebf971ec"}, + {file = "librt-0.7.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399938edbd3d78339f797d685142dd8a623dfaded023cf451033c85955e4838a"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1975eda520957c6e0eb52d12968dd3609ffb7eef05d4223d097893d6daf1d8a7"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9da128d0edf990cf0d2ca011b02cd6f639e79286774bd5b0351245cbb5a6e51"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19acfde38cb532a560b98f473adc741c941b7a9bc90f7294bc273d08becb58b"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b4f57f7a0c65821c5441d98c47ff7c01d359b1e12328219709bdd97fdd37f90"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:256793988bff98040de23c57cf36e1f4c2f2dc3dcd17537cdac031d3b681db71"}, + {file = "librt-0.7.3-cp310-cp310-win32.whl", hash = "sha256:fcb72249ac4ea81a7baefcbff74df7029c3cb1cf01a711113fa052d563639c9c"}, + {file = "librt-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4887c29cadbdc50640179e3861c276325ff2986791e6044f73136e6e798ff806"}, + {file = "librt-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:687403cced6a29590e6be6964463835315905221d797bc5c934a98750fe1a9af"}, + {file = "librt-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24d70810f6e2ea853ff79338001533716b373cc0f63e2a0be5bc96129edb5fb5"}, + {file = "librt-0.7.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf8c7735fbfc0754111f00edda35cf9e98a8d478de6c47b04eaa9cef4300eaa7"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32d43610dff472eab939f4d7fbdd240d1667794192690433672ae22d7af8445"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:adeaa886d607fb02563c1f625cf2ee58778a2567c0c109378da8f17ec3076ad7"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572a24fc5958c61431da456a0ef1eeea6b4989d81eeb18b8e5f1f3077592200b"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6488e69d408b492e08bfb68f20c4a899a354b4386a446ecd490baff8d0862720"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed028fc3d41adda916320712838aec289956c89b4f0a361ceadf83a53b4c047a"}, + {file = "librt-0.7.3-cp311-cp311-win32.whl", hash = "sha256:2cf9d73499486ce39eebbff5f42452518cc1f88d8b7ea4a711ab32962b176ee2"}, + {file = "librt-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:35f1609e3484a649bb80431310ddbec81114cd86648f1d9482bc72a3b86ded2e"}, + {file = "librt-0.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:550fdbfbf5bba6a2960b27376ca76d6aaa2bd4b1a06c4255edd8520c306fcfc0"}, + {file = "librt-0.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fa9ac2e49a6bee56e47573a6786cb635e128a7b12a0dc7851090037c0d397a3"}, + {file = "librt-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e980cf1ed1a2420a6424e2ed884629cdead291686f1048810a817de07b5eb18"}, + {file = "librt-0.7.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e094e445c37c57e9ec612847812c301840239d34ccc5d153a982fa9814478c60"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca73d70c3f553552ba9133d4a09e767dcfeee352d8d8d3eb3f77e38a3beb3ed"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c634a0a6db395fdaba0361aa78395597ee72c3aad651b9a307a3a7eaf5efd67e"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a59a69deeb458c858b8fea6acf9e2acd5d755d76cd81a655256bc65c20dfff5b"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d91e60ac44bbe3a77a67af4a4c13114cbe9f6d540337ce22f2c9eaf7454ca71f"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:703456146dc2bf430f7832fd1341adac5c893ec3c1430194fdcefba00012555c"}, + {file = "librt-0.7.3-cp312-cp312-win32.whl", hash = "sha256:b7c1239b64b70be7759554ad1a86288220bbb04d68518b527783c4ad3fb4f80b"}, + {file = "librt-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef59c938f72bdbc6ab52dc50f81d0637fde0f194b02d636987cea2ab30f8f55a"}, + {file = "librt-0.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:ff21c554304e8226bf80c3a7754be27c6c3549a9fec563a03c06ee8f494da8fc"}, + {file = "librt-0.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56f2a47beda8409061bc1c865bef2d4bd9ff9255219402c0817e68ab5ad89aed"}, + {file = "librt-0.7.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14569ac5dd38cfccf0a14597a88038fb16811a6fede25c67b79c6d50fc2c8fdc"}, + {file = "librt-0.7.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6038ccbd5968325a5d6fd393cf6e00b622a8de545f0994b89dd0f748dcf3e19e"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d39079379a9a28e74f4d57dc6357fa310a1977b51ff12239d7271ec7e71d67f5"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8837d5a52a2d7aa9f4c3220a8484013aed1d8ad75240d9a75ede63709ef89055"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:399bbd7bcc1633c3e356ae274a1deb8781c7bf84d9c7962cc1ae0c6e87837292"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d8cf653e798ee4c4e654062b633db36984a1572f68c3aa25e364a0ddfbbb910"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f03484b54bf4ae80ab2e504a8d99d20d551bfe64a7ec91e218010b467d77093"}, + {file = "librt-0.7.3-cp313-cp313-win32.whl", hash = "sha256:44b3689b040df57f492e02cd4f0bacd1b42c5400e4b8048160c9d5e866de8abe"}, + {file = "librt-0.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6b407c23f16ccc36614c136251d6b32bf30de7a57f8e782378f1107be008ddb0"}, + {file = "librt-0.7.3-cp313-cp313-win_arm64.whl", hash = "sha256:abfc57cab3c53c4546aee31859ef06753bfc136c9d208129bad23e2eca39155a"}, + {file = "librt-0.7.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:120dd21d46ff875e849f1aae19346223cf15656be489242fe884036b23d39e93"}, + {file = "librt-0.7.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1617bea5ab31266e152871208502ee943cb349c224846928a1173c864261375e"}, + {file = "librt-0.7.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93b2a1f325fefa1482516ced160c8c7b4b8d53226763fa6c93d151fa25164207"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d4801db8354436fd3936531e7f0e4feb411f62433a6b6cb32bb416e20b529f"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11ad45122bbed42cfc8b0597450660126ef28fd2d9ae1a219bc5af8406f95678"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6b4e7bff1d76dd2b46443078519dc75df1b5e01562345f0bb740cea5266d8218"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d86f94743a11873317094326456b23f8a5788bad9161fd2f0e52088c33564620"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:754a0d09997095ad764ccef050dd5bf26cbf457aab9effcba5890dad081d879e"}, + {file = "librt-0.7.3-cp314-cp314-win32.whl", hash = "sha256:fbd7351d43b80d9c64c3cfcb50008f786cc82cba0450e8599fdd64f264320bd3"}, + {file = "librt-0.7.3-cp314-cp314-win_amd64.whl", hash = "sha256:d376a35c6561e81d2590506804b428fc1075fcc6298fc5bb49b771534c0ba010"}, + {file = "librt-0.7.3-cp314-cp314-win_arm64.whl", hash = "sha256:cbdb3f337c88b43c3b49ca377731912c101178be91cb5071aac48faa898e6f8e"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9f0e0927efe87cd42ad600628e595a1a0aa1c64f6d0b55f7e6059079a428641a"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:020c6db391268bcc8ce75105cb572df8cb659a43fd347366aaa407c366e5117a"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7af7785f5edd1f418da09a8cdb9ec84b0213e23d597413e06525340bcce1ea4f"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ccadf260bb46a61b9c7e89e2218f6efea9f3eeaaab4e3d1f58571890e54858e"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9883b2d819ce83f87ba82a746c81d14ada78784db431e57cc9719179847376e"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:59cb0470612d21fa1efddfa0dd710756b50d9c7fb6c1236bbf8ef8529331dc70"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1fe603877e1865b5fd047a5e40379509a4a60204aa7aa0f72b16f7a41c3f0712"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5460d99ed30f043595bbdc888f542bad2caeb6226b01c33cda3ae444e8f82d42"}, + {file = "librt-0.7.3-cp314-cp314t-win32.whl", hash = "sha256:d09f677693328503c9e492e33e9601464297c01f9ebd966ea8fc5308f3069bfd"}, + {file = "librt-0.7.3-cp314-cp314t-win_amd64.whl", hash = "sha256:25711f364c64cab2c910a0247e90b51421e45dbc8910ceeb4eac97a9e132fc6f"}, + {file = "librt-0.7.3-cp314-cp314t-win_arm64.whl", hash = "sha256:a9f9b661f82693eb56beb0605156c7fca57f535704ab91837405913417d6990b"}, + {file = "librt-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd8551aa21df6c60baa2624fd086ae7486bdde00c44097b32e1d1b1966e365e0"}, + {file = "librt-0.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6eb9295c730e26b849ed1f4022735f36863eb46b14b6e10604c1c39b8b5efaea"}, + {file = "librt-0.7.3-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3edbf257c40d21a42615e9e332a6b10a8bacaaf58250aed8552a14a70efd0d65"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b29e97273bd6999e2bfe9fe3531b1f4f64effd28327bced048a33e49b99674a"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e40520c37926166c24d0c2e0f3bc3a5f46646c34bdf7b4ea9747c297d6ee809"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6bdd9adfca615903578d2060ee8a6eb1c24eaf54919ff0ddc820118e5718931b"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f57aca20e637750a2c18d979f7096e2c2033cc40cf7ed201494318de1182f135"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cad9971881e4fec00d96af7eaf4b63aa7a595696fc221808b0d3ce7ca9743258"}, + {file = "librt-0.7.3-cp39-cp39-win32.whl", hash = "sha256:170cdb8436188347af17bf9cccf3249ba581c933ed56d926497119d4cf730cec"}, + {file = "librt-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:b278a9248a4e3260fee3db7613772ca9ab6763a129d6d6f29555e2f9b168216d"}, + {file = "librt-0.7.3.tar.gz", hash = "sha256:3ec50cf65235ff5c02c5b747748d9222e564ad48597122a361269dd3aa808798"}, +] + [[package]] name = "markdown" version = "3.9" @@ -954,53 +1040,54 @@ files = [ [[package]] name = "mypy" -version = "1.18.2" +version = "1.19.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, - {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, - {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, - {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, - {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, - {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, - {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, - {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, - {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, - {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, - {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, - {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, - {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, - {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, - {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, - {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"}, + {file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"}, + {file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"}, + {file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"}, + {file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"}, + {file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"}, + {file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"}, + {file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"}, + {file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"}, + {file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"}, + {file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"}, + {file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"}, + {file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"}, + {file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"}, + {file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"}, ] [package.dependencies] +librt = ">=0.6.2" mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1253,14 +1340,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.17.1" +version = "10.18" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pymdown_extensions-10.17.1-py3-none-any.whl", hash = "sha256:1f160209c82eecbb5d8a0d8f89a4d9bd6bdcbde9a8537761844cfc57ad5cd8a6"}, - {file = "pymdown_extensions-10.17.1.tar.gz", hash = "sha256:60d05fe55e7fb5a1e4740fc575facad20dc6ee3a748e8d3d36ba44142e75ce03"}, + {file = "pymdown_extensions-10.18-py3-none-any.whl", hash = "sha256:090bca72be43f7d3186374e23c782899dbef9dc153ef24c59dcd3c346f9ffcae"}, + {file = "pymdown_extensions-10.18.tar.gz", hash = "sha256:20252abe6367354b24191431617a072ee6be9f68c5afcc74ea5573508a61f9e5"}, ] [package.dependencies] @@ -1602,31 +1689,31 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.14.5" +version = "0.14.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["linters"] files = [ - {file = "ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594"}, - {file = "ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72"}, - {file = "ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367"}, - {file = "ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b"}, - {file = "ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621"}, - {file = "ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4"}, - {file = "ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1"}, + {file = "ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb"}, + {file = "ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9"}, + {file = "ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e"}, + {file = "ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7"}, + {file = "ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097"}, + {file = "ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99"}, + {file = "ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed"}, ] [[package]] @@ -1857,21 +1944,21 @@ markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"" [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, + {file = "urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b"}, + {file = "urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "virtualenv" From 82caf4b54c8cc45da79b3b05e5c32df09c5c3390 Mon Sep 17 00:00:00 2001 From: Wei Lee <weilee.rx@gmail.com> Date: Sat, 8 Nov 2025 19:17:07 +0800 Subject: [PATCH 257/275] refactor(changelog): raise NotAllow when file_name not passed instead of using assert --- commitizen/commands/changelog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 27b8ccb258..805d7472da 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -199,7 +199,8 @@ def __call__(self) -> None: raise NotAllowed("--incremental cannot be combined with a rev_range") # Don't continue if no `file_name` specified. - assert self.file_name + if not self.file_name: + raise NotAllowed("filename is required.") tags = self.tag_rules.get_version_tags(git.get_tags(), warn=True) changelog_meta = changelog.Metadata() From 1960970e6b63938f1a7794c85a8439f8b2204fc6 Mon Sep 17 00:00:00 2001 From: Wei Lee <weilee.rx@gmail.com> Date: Sat, 8 Nov 2025 19:13:51 +0800 Subject: [PATCH 258/275] style(ruff): enable S101 check to avoid assert usage in production code --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9b15e6e13c..097aed9c1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -206,11 +206,13 @@ select = [ "RUF022", # unused-noqa "RUF100", + # Checks for uses of the assert keyword. + "S101", ] ignore = ["E501", "D1", "D415"] [tool.ruff.lint.per-file-ignores] -"tests/*" = ["ANN"] +"tests/*" = ["ANN", "S101"] [tool.ruff.lint.isort] known-first-party = ["commitizen", "tests"] From 7d6ae2d849d3fa21f0509f46311a7b43eefb102a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Mon, 10 Nov 2025 21:11:35 +0800 Subject: [PATCH 259/275] fix(bump): remove NotAllowed related to --get-next option, other related refactoring --- commitizen/commands/bump.py | 44 +++++++++++++---------------- commitizen/exceptions.py | 4 --- tests/commands/test_bump_command.py | 38 ++----------------------- 3 files changed, 22 insertions(+), 64 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 5cad643b81..7d9241587f 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -16,7 +16,6 @@ BumpTagFailedError, DryRunExit, ExpectedExit, - GetNextExit, InvalidManualVersion, NoCommitsFoundError, NoneIncrementExit, @@ -47,7 +46,7 @@ class BumpArgs(Settings, total=False): dry_run: bool file_name: str files_only: bool | None - get_next: bool + get_next: bool # TODO: maybe rename to `next_version_to_stdout` git_output_to_stderr: bool increment_mode: str increment: Increment | None @@ -94,7 +93,6 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: ) self.cz = factory.committer_factory(self.config) self.changelog_flag = arguments["changelog"] - self.changelog_config = self.config.settings.get("update_changelog_on_bump") self.changelog_to_stdout = arguments["changelog_to_stdout"] self.git_output_to_stderr = arguments["git_output_to_stderr"] self.no_verify = arguments["no_verify"] @@ -169,7 +167,9 @@ def __call__(self) -> None: is_local_version = self.arguments["local_version"] manual_version = self.arguments["manual_version"] build_metadata = self.arguments["build_metadata"] - get_next = self.arguments["get_next"] + next_version_to_stdout = self.arguments[ + "get_next" + ] # TODO: maybe rename to `next_version_to_stdout` allow_no_commit = self.arguments["allow_no_commit"] major_version_zero = self.arguments["major_version_zero"] @@ -181,7 +181,6 @@ def __call__(self) -> None: (is_local_version, "--local-version"), (build_metadata, "--build-metadata"), (major_version_zero, "--major-version-zero"), - (get_next, "--get-next"), ): if val: raise NotAllowed(f"{option} cannot be combined with MANUAL_VERSION") @@ -194,24 +193,23 @@ def __call__(self) -> None: if build_metadata and is_local_version: raise NotAllowed("--local-version cannot be combined with --build-metadata") - if get_next: + if next_version_to_stdout: for value, option in ( (self.changelog_flag, "--changelog"), (self.changelog_to_stdout, "--changelog-to-stdout"), ): if value: - raise NotAllowed(f"{option} cannot be combined with --get-next") - - # --get-next is a special case, taking precedence over config for 'update_changelog_on_bump' - self.changelog_config = False - # Setting dry_run to prevent any unwanted changes to the repo or files - self.dry_run = True - else: - # If user specified changelog_to_stdout, they probably want the - # changelog to be generated as well, this is the most intuitive solution - self.changelog_flag = any( - (self.changelog_flag, self.changelog_to_stdout, self.changelog_config) + warnings.warn(f"{option} has no effect when used with --get-next") + + # If user specified changelog_to_stdout, they probably want the + # changelog to be generated as well, this is the most intuitive solution + self.changelog_flag = any( + ( + self.changelog_flag, + self.changelog_to_stdout, + self.config.settings.get("update_changelog_on_bump"), ) + ) rules = TagRules.from_settings(cast(Settings, self.bump_settings)) current_tag = rules.find_tag_for(git.get_tags(), current_version) @@ -270,20 +268,18 @@ def __call__(self) -> None: ) new_tag_version = rules.normalize_tag(new_version) - message = bump.create_commit_message( - current_version, new_version, self.bump_settings["bump_message"] - ) - - if get_next: + if next_version_to_stdout: if increment is None and new_tag_version == current_tag_version: raise NoneIncrementExit( "[NO_COMMITS_TO_BUMP]\n" "The commits found are not eligible to be bumped" ) - out.write(str(new_version)) - raise GetNextExit() + raise DryRunExit() + message = bump.create_commit_message( + current_version, new_version, self.bump_settings["bump_message"] + ) # Report found information information = f"{message}\ntag to create: {new_tag_version}\n" if increment: diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 75b0ab2fb7..49cd1832b0 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -75,10 +75,6 @@ class DryRunExit(ExpectedExit): pass -class GetNextExit(ExpectedExit): - pass - - class NoneIncrementExit(CommitizenException): exit_code = ExitCode.NO_INCREMENT diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 59297b1726..7b9edee82f 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -23,7 +23,6 @@ DryRunExit, ExitCode, ExpectedExit, - GetNextExit, InvalidManualVersion, NoCommitsFoundError, NoneIncrementExit, @@ -1456,7 +1455,7 @@ def test_bump_get_next(mocker: MockFixture, capsys): testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) - with pytest.raises(GetNextExit): + with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() @@ -1476,7 +1475,7 @@ def test_bump_get_next_update_changelog_on_bump( testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) - with pytest.raises(GetNextExit): + with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() @@ -1486,39 +1485,6 @@ def test_bump_get_next_update_changelog_on_bump( assert tag_exists is False -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__changelog_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "--changelog"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__changelog_to_stdout_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "--changelog-to-stdout"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__manual_version_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "0.2.1"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__no_eligible_commits_raises(mocker: MockFixture): create_file_and_commit("chore: new commit") From 34cff20e8503f29fb2eba9da255cd013aa973c45 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Tue, 11 Nov 2025 22:41:49 +0800 Subject: [PATCH 260/275] refactor(bump): extract option validation and new version resolution to new functions --- commitizen/commands/bump.py | 157 +++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 7d9241587f..acf127fe38 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -29,6 +29,7 @@ Increment, InvalidVersion, Prerelease, + VersionProtocol, get_version_scheme, ) @@ -156,43 +157,91 @@ def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None: ) return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map) - def __call__(self) -> None: - """Steps executed to bump.""" - provider = get_provider(self.config) - current_version = self.scheme(provider.get_version()) - - increment = self.arguments["increment"] - prerelease = self.arguments["prerelease"] - devrelease = self.arguments["devrelease"] - is_local_version = self.arguments["local_version"] - manual_version = self.arguments["manual_version"] - build_metadata = self.arguments["build_metadata"] - next_version_to_stdout = self.arguments[ - "get_next" - ] # TODO: maybe rename to `next_version_to_stdout` - allow_no_commit = self.arguments["allow_no_commit"] - major_version_zero = self.arguments["major_version_zero"] - - if manual_version: + def _validate_arguments(self, current_version: VersionProtocol) -> None: + errors: list[str] = [] + if self.arguments["manual_version"]: for val, option in ( - (increment, "--increment"), - (prerelease, "--prerelease"), - (devrelease is not None, "--devrelease"), - (is_local_version, "--local-version"), - (build_metadata, "--build-metadata"), - (major_version_zero, "--major-version-zero"), + (self.arguments["increment"], "--increment"), + (self.arguments["prerelease"], "--prerelease"), + (self.arguments["devrelease"] is not None, "--devrelease"), + (self.arguments["local_version"], "--local-version"), + (self.arguments["build_metadata"], "--build-metadata"), + (self.arguments["major_version_zero"], "--major-version-zero"), ): if val: - raise NotAllowed(f"{option} cannot be combined with MANUAL_VERSION") + errors.append(f"{option} cannot be combined with MANUAL_VERSION") - if major_version_zero and current_version.release[0]: - raise NotAllowed( + if self.arguments["major_version_zero"] and current_version.release[0]: + errors.append( f"--major-version-zero is meaningless for current version {current_version}" ) + if self.arguments["build_metadata"] and self.arguments["local_version"]: + errors.append("--local-version cannot be combined with --build-metadata") + + if errors: + raise NotAllowed("\n".join(errors)) + + def _resolve_increment_and_new_version( + self, current_version: VersionProtocol, current_tag: git.GitTag | None + ) -> tuple[Increment | None, VersionProtocol]: + increment = self.arguments["increment"] + if manual_version := self.arguments["manual_version"]: + try: + return increment, self.scheme(manual_version) + except InvalidVersion as exc: + raise InvalidManualVersion( + "[INVALID_MANUAL_VERSION]\n" + f"Invalid manual version: '{manual_version}'" + ) from exc + + if increment is None: + commits = git.get_commits(current_tag.name if current_tag else None) + + # No commits, there is no need to create an empty tag. + # Unless we previously had a prerelease. + if ( + not commits + and not current_version.is_prerelease + and not self.arguments["allow_no_commit"] + ): + raise NoCommitsFoundError("[NO_COMMITS_FOUND]\nNo new commits found.") + + increment = self._find_increment(commits) + + # It may happen that there are commits, but they are not eligible + # for an increment, this generates a problem when using prerelease (#281) + if ( + self.arguments["prerelease"] + and increment is None + and not current_version.is_prerelease + ): + raise NoCommitsFoundError( + "[NO_COMMITS_FOUND]\n" + "No commits found to generate a pre-release.\n" + "To avoid this error, manually specify the type of increment with `--increment`" + ) - if build_metadata and is_local_version: - raise NotAllowed("--local-version cannot be combined with --build-metadata") + # we create an empty PATCH increment for empty tag + if increment is None and self.arguments["allow_no_commit"]: + increment = "PATCH" + + return increment, current_version.bump( + increment, + prerelease=self.arguments["prerelease"], + prerelease_offset=self.bump_settings["prerelease_offset"], + devrelease=self.arguments["devrelease"], + is_local_version=self.arguments["local_version"], + build_metadata=self.arguments["build_metadata"], + exact_increment=self.arguments["increment_mode"] == "exact", + ) + def __call__(self) -> None: + """Steps executed to bump.""" + provider = get_provider(self.config) + current_version = self.scheme(provider.get_version()) + self._validate_arguments(current_version) + + next_version_to_stdout = self.arguments["get_next"] if next_version_to_stdout: for value, option in ( (self.changelog_flag, "--changelog"), @@ -219,53 +268,9 @@ def __call__(self) -> None: is_initial = self._is_initial_tag(current_tag, self.arguments["yes"]) - if manual_version: - try: - new_version = self.scheme(manual_version) - except InvalidVersion as exc: - raise InvalidManualVersion( - "[INVALID_MANUAL_VERSION]\n" - f"Invalid manual version: '{manual_version}'" - ) from exc - else: - if increment is None: - commits = git.get_commits(current_tag.name if current_tag else None) - - # No commits, there is no need to create an empty tag. - # Unless we previously had a prerelease. - if ( - not commits - and not current_version.is_prerelease - and not allow_no_commit - ): - raise NoCommitsFoundError( - "[NO_COMMITS_FOUND]\nNo new commits found." - ) - - increment = self._find_increment(commits) - - # It may happen that there are commits, but they are not eligible - # for an increment, this generates a problem when using prerelease (#281) - if prerelease and increment is None and not current_version.is_prerelease: - raise NoCommitsFoundError( - "[NO_COMMITS_FOUND]\n" - "No commits found to generate a pre-release.\n" - "To avoid this error, manually specify the type of increment with `--increment`" - ) - - # we create an empty PATCH increment for empty tag - if increment is None and allow_no_commit: - increment = "PATCH" - - new_version = current_version.bump( - increment, - prerelease=prerelease, - prerelease_offset=self.bump_settings["prerelease_offset"], - devrelease=devrelease, - is_local_version=is_local_version, - build_metadata=build_metadata, - exact_increment=self.arguments["increment_mode"] == "exact", - ) + increment, new_version = self._resolve_increment_and_new_version( + current_version, current_tag + ) new_tag_version = rules.normalize_tag(new_version) if next_version_to_stdout: From 8421bb16b8175dd357935baaec95286a919f4e64 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Sat, 13 Sep 2025 19:18:22 +0800 Subject: [PATCH 261/275] refactor(cargo_provider): cleanup and get rid of potential type errors --- commitizen/providers/cargo_provider.py | 99 +++++++++-------- tests/providers/test_cargo_provider.py | 147 +++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 45 deletions(-) diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 3e8c95e22a..02fdd2cc6d 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -3,19 +3,15 @@ import fnmatch import glob from pathlib import Path +from typing import TYPE_CHECKING -import tomlkit +from tomlkit import TOMLDocument, dumps, parse +from tomlkit.exceptions import NonExistentKey +from tomlkit.items import AoT from commitizen.providers.base_provider import TomlProvider -def matches_exclude(path: str, exclude_patterns: list[str]) -> bool: - for pattern in exclude_patterns: - if fnmatch.fnmatch(path, pattern): - return True - return False - - class CargoProvider(TomlProvider): """ Cargo version management @@ -30,22 +26,14 @@ class CargoProvider(TomlProvider): def lock_file(self) -> Path: return Path() / self.lock_filename - def get(self, document: tomlkit.TOMLDocument) -> str: - # If there is a root package, change its version (but not the workspace version) - try: - return document["package"]["version"] # type: ignore[index,return-value] - # Else, bump the workspace version - except tomlkit.exceptions.NonExistentKey: - ... - return document["workspace"]["package"]["version"] # type: ignore[index,return-value] + def get(self, document: TOMLDocument) -> str: + out = _try_get_workspace(document)["package"]["version"] + if TYPE_CHECKING: + assert isinstance(out, str) + return out - def set(self, document: tomlkit.TOMLDocument, version: str) -> None: - try: - document["workspace"]["package"]["version"] = version # type: ignore[index] - return - except tomlkit.exceptions.NonExistentKey: - ... - document["package"]["version"] = version # type: ignore[index] + def set(self, document: TOMLDocument, version: str) -> None: + _try_get_workspace(document)["package"]["version"] = version def set_version(self, version: str) -> None: super().set_version(version) @@ -53,42 +41,63 @@ def set_version(self, version: str) -> None: self.set_lock_version(version) def set_lock_version(self, version: str) -> None: - cargo_toml_content = tomlkit.parse(self.file.read_text()) - cargo_lock_content = tomlkit.parse(self.lock_file.read_text()) - packages: tomlkit.items.AoT = cargo_lock_content["package"] # type: ignore[assignment] + cargo_toml_content = parse(self.file.read_text()) + cargo_lock_content = parse(self.lock_file.read_text()) + packages = cargo_lock_content["package"] + + if TYPE_CHECKING: + assert isinstance(packages, AoT) + try: - package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + cargo_package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + if TYPE_CHECKING: + assert isinstance(cargo_package_name, str) for i, package in enumerate(packages): - if package["name"] == package_name: + if package["name"] == cargo_package_name: cargo_lock_content["package"][i]["version"] = version # type: ignore[index] break - except tomlkit.exceptions.NonExistentKey: - workspace_members = cargo_toml_content.get("workspace", {}).get( - "members", [] - ) - excluded_workspace_members = cargo_toml_content.get("workspace", {}).get( - "exclude", [] - ) - members_inheriting = [] + except NonExistentKey: + workspace = cargo_toml_content.get("workspace", {}) + if TYPE_CHECKING: + assert isinstance(workspace, dict) + workspace_members = workspace.get("members", []) + excluded_workspace_members = workspace.get("exclude", []) + members_inheriting: list[str] = [] for member in workspace_members: for path in glob.glob(member, recursive=True): - if matches_exclude(path, excluded_workspace_members): + if any( + fnmatch.fnmatch(path, pattern) + for pattern in excluded_workspace_members + ): continue + cargo_file = Path(path) / "Cargo.toml" - cargo_toml_content = tomlkit.parse(cargo_file.read_text()) + package_content = parse(cargo_file.read_text()).get("package", {}) + if TYPE_CHECKING: + assert isinstance(package_content, dict) try: - version_workspace = cargo_toml_content["package"]["version"][ # type: ignore[index] - "workspace" - ] + version_workspace = package_content["version"]["workspace"] if version_workspace is True: - package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + package_name = package_content["name"] + if TYPE_CHECKING: + assert isinstance(package_name, str) members_inheriting.append(package_name) - except tomlkit.exceptions.NonExistentKey: - continue + except NonExistentKey: + pass for i, package in enumerate(packages): if package["name"] in members_inheriting: cargo_lock_content["package"][i]["version"] = version # type: ignore[index] - self.lock_file.write_text(tomlkit.dumps(cargo_lock_content)) + self.lock_file.write_text(dumps(cargo_lock_content)) + + +def _try_get_workspace(document: TOMLDocument) -> dict: + try: + workspace = document["workspace"] + if TYPE_CHECKING: + assert isinstance(workspace, dict) + return workspace + except NonExistentKey: + return document diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index 5e7b2d8cb7..b7dc932d38 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -301,3 +301,150 @@ def test_cargo_provider_with_lock( provider.set_version("42.1") assert file.read_text() == dedent(toml_expected) assert lock_file.read_text() == dedent(lock_expected) + + +def test_cargo_provider_workspace_member_without_version_key( + config: BaseConfig, + chdir: Path, +): + """Test workspace member that has no version key at all (should not crash).""" + workspace_toml = """\ +[workspace] +members = ["member_without_version"] + +[workspace.package] +version = "0.1.0" +""" + + # Create a member that has no version key at all + member_content = """\ +[package] +name = "member_without_version" +# No version key - this should trigger NonExistentKey exception +""" + + lock_content = """\ +[[package]] +name = "member_without_version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + expected_workspace_toml = """\ +[workspace] +members = ["member_without_version"] + +[workspace.package] +version = "42.1" +""" + + expected_lock_content = """\ +[[package]] +name = "member_without_version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + # Create the workspace file + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(workspace_toml)) + + # Create the member directory and file + os.mkdir(chdir / "member_without_version") + member_file = chdir / "member_without_version" / "Cargo.toml" + member_file.write_text(dedent(member_content)) + + # Create the lock file + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + # This should not crash even though the member has no version key + provider.set_version("42.1") + assert file.read_text() == dedent(expected_workspace_toml) + # The lock file should remain unchanged since the member doesn't inherit workspace version + assert lock_file.read_text() == dedent(expected_lock_content) + + +def test_cargo_provider_workspace_member_without_workspace_key( + config: BaseConfig, + chdir: Path, +): + """Test workspace member that has version key but no workspace subkey.""" + workspace_toml = """\ +[workspace] +members = ["member_without_workspace"] + +[workspace.package] +version = "0.1.0" +""" + + # Create a member that has version as a table but no workspace subkey + # This should trigger NonExistentKey when trying to access version["workspace"] + member_content = """\ +[package] +name = "member_without_workspace" + +[package.version] +# Has version table but no workspace key - should trigger NonExistentKey +""" + + lock_content = """\ +[[package]] +name = "member_without_workspace" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + expected_workspace_toml = """\ +[workspace] +members = ["member_without_workspace"] + +[workspace.package] +version = "42.1" +""" + + expected_lock_content = """\ +[[package]] +name = "member_without_workspace" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + # Create the workspace file + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(workspace_toml)) + + # Create the member directory and file + os.mkdir(chdir / "member_without_workspace") + member_file = chdir / "member_without_workspace" / "Cargo.toml" + member_file.write_text(dedent(member_content)) + + # Create the lock file + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + # This should not crash even though the member has no version.workspace key + provider.set_version("42.1") + assert file.read_text() == dedent(expected_workspace_toml) + # The lock file should remain unchanged since the member doesn't inherit workspace version + assert lock_file.read_text() == dedent(expected_lock_content) From 7529611c006a67a0338e53fe2bb3d01ff9eff491 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Thu, 20 Nov 2025 23:49:38 +0800 Subject: [PATCH 262/275] docs(exit-codes): general update docs(exception): add comment for updating exit code doc --- commitizen/exceptions.py | 73 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 49cd1832b0..52193e6ccc 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -29,7 +29,7 @@ class ExitCode(IntEnum): INVALID_CONFIGURATION = 19 NOT_ALLOWED = 20 NO_INCREMENT = 21 - UNRECOGNIZED_CHARACTERSET_ENCODING = 22 + CHARACTER_SET_DECODE_ERROR = 22 GIT_COMMAND_ERROR = 23 INVALID_MANUAL_VERSION = 24 INIT_FAILED = 25 @@ -72,32 +72,44 @@ def __init__(self, *args: str, **kwargs: Any) -> None: class DryRunExit(ExpectedExit): - pass + """Exit due to passing `--dry-run` option""" class NoneIncrementExit(CommitizenException): + """The commits found are not eligible to be bumped""" + exit_code = ExitCode.NO_INCREMENT class NoCommitizenFoundException(CommitizenException): + """Using a cz (e.g., `cz_jira`) that cannot be found in your system""" + exit_code = ExitCode.NO_COMMITIZEN_FOUND class NotAGitProjectError(CommitizenException): + """Not in a git project""" + exit_code = ExitCode.NOT_A_GIT_PROJECT message = "fatal: not a git repository (or any of the parent directories): .git" class MissingCzCustomizeConfigError(CommitizenException): + """Configuration is missing for `cz_customize`""" + exit_code = ExitCode.MISSING_CZ_CUSTOMIZE_CONFIG message = "fatal: customize is not set in configuration file." class NoCommitsFoundError(CommitizenException): + """No commits found""" + exit_code = ExitCode.NO_COMMITS_FOUND class NoVersionSpecifiedError(CommitizenException): + """Version is not specified in configuration file""" + exit_code = ExitCode.NO_VERSION_SPECIFIED message = ( "[NO_VERSION_SPECIFIED]\n" @@ -107,111 +119,166 @@ class NoVersionSpecifiedError(CommitizenException): class NoPatternMapError(CommitizenException): + """bump / changelog pattern or map can not be found in configuration file""" + exit_code = ExitCode.NO_PATTERN_MAP class BumpCommitFailedError(CommitizenException): + """Commit failed when bumping version""" + exit_code = ExitCode.BUMP_COMMIT_FAILED class BumpTagFailedError(CommitizenException): + """Tag failed when bumping version""" + exit_code = ExitCode.BUMP_TAG_FAILED class CurrentVersionNotFoundError(CommitizenException): + """Current version cannot be found in `version_files`""" + exit_code = ExitCode.CURRENT_VERSION_NOT_FOUND class NoAnswersError(CommitizenException): + """No user response given""" + exit_code = ExitCode.NO_ANSWERS class CommitError(CommitizenException): + """git commit error""" + exit_code = ExitCode.COMMIT_ERROR class NoCommitBackupError(CommitizenException): + """Commit backup file is not found""" + exit_code = ExitCode.NO_COMMIT_BACKUP message = "No commit backup found" class NothingToCommitError(CommitizenException): + """Nothing in staging to be committed""" + exit_code = ExitCode.NOTHING_TO_COMMIT class CustomError(CommitizenException): + """`CzException` raised""" + exit_code = ExitCode.CUSTOM_ERROR class InvalidCommitMessageError(CommitizenException): + """The commit message does not pass `cz check`""" + exit_code = ExitCode.INVALID_COMMIT_MSG class NoRevisionError(CommitizenException): + """No revision found""" + exit_code = ExitCode.NO_REVISION message = "No tag found to do an incremental changelog" class NoCommandFoundError(CommitizenException): + """No command found when running Commitizen cli (e.g., `cz --debug`)""" + exit_code = ExitCode.NO_COMMAND_FOUND message = "Command is required" class InvalidCommandArgumentError(CommitizenException): + """The argument provided to the command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`)""" + exit_code = ExitCode.INVALID_COMMAND_ARGUMENT class InvalidConfigurationError(CommitizenException): + """An error was found in the Commitizen Configuration, such as duplicates in `change_type_order`""" + exit_code = ExitCode.INVALID_CONFIGURATION class NotAllowed(CommitizenException): + """`--incremental` cannot be combined with a `rev_range`""" + exit_code = ExitCode.NOT_ALLOWED class CharacterSetDecodeError(CommitizenException): - exit_code = ExitCode.UNRECOGNIZED_CHARACTERSET_ENCODING + """The character encoding of the command output could not be determined""" + + exit_code = ExitCode.CHARACTER_SET_DECODE_ERROR class GitCommandError(CommitizenException): + """Unexpected failure while calling a git command""" + exit_code = ExitCode.GIT_COMMAND_ERROR class InvalidManualVersion(CommitizenException): + """Manually provided version is invalid""" + exit_code = ExitCode.INVALID_MANUAL_VERSION class InitFailedError(CommitizenException): + """Failed to initialize pre-commit""" + exit_code = ExitCode.INIT_FAILED class RunHookError(CommitizenException): + """An error occurred during a hook execution""" + exit_code = ExitCode.RUN_HOOK_FAILED class VersionProviderUnknown(CommitizenException): + """Unknown `version_provider`""" + exit_code = ExitCode.VERSION_PROVIDER_UNKNOWN class VersionSchemeUnknown(CommitizenException): + """Unknown `version_scheme`""" + exit_code = ExitCode.VERSION_SCHEME_UNKNOWN class ChangelogFormatUnknown(CommitizenException): + """Unknown `changelog_format` or cannot be determined by the file extension""" + exit_code = ExitCode.CHANGELOG_FORMAT_UNKNOWN message = "Unknown changelog format identifier" class ConfigFileNotFound(CommitizenException): + """The configuration file is not found""" + exit_code = ExitCode.CONFIG_FILE_NOT_FOUND message = "Cannot found the config file, please check your file path again." class ConfigFileIsEmpty(CommitizenException): + """The configuration file is empty""" + exit_code = ExitCode.CONFIG_FILE_IS_EMPTY message = "Config file is empty, please check your file path again." class CommitMessageLengthExceededError(CommitizenException): + """The commit message length exceeds the given limit.""" + exit_code = ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED message = "Length of commit message exceeds the given limit." + + +# When adding / updating a new exit code, please update the documentation of the exit codes in docs/exit_codes.md From c8c91f1722399ba8ed862eb70f6129639db0c7e6 Mon Sep 17 00:00:00 2001 From: Tim Hsiung <bear890707@gmail.com> Date: Wed, 3 Dec 2025 22:55:17 +0800 Subject: [PATCH 263/275] test: replace try with pytest.raises (#1654) --- tests/commands/test_bump_command.py | 12 +++++------- tests/commands/test_changelog_command.py | 7 ++++--- tests/test_git.py | 14 +++++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 7b9edee82f..8871a6bf0f 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -646,13 +646,11 @@ def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( dummy_value = git.tag("0.0.2") git.tag = MagicMock(return_value=dummy_value) - with pytest.raises(NoneIncrementExit): - try: - cli.main() - except NoneIncrementExit as e: - git.tag.assert_not_called() - assert e.exit_code == ExitCode.NO_INCREMENT - raise e + with pytest.raises(NoneIncrementExit) as e: + cli.main() + + git.tag.assert_not_called() + assert e.value.exit_code == ExitCode.NO_INCREMENT # restore pop stashed git.tag = stashed_git_tag diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index f147c419b8..f2e2ecbd38 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -292,10 +292,11 @@ def test_changelog_hook(mocker: MockFixture, config: BaseConfig, dry_run: bool): config, {"unreleased_version": None, "incremental": True, "dry_run": dry_run} ) mocker.patch.object(changelog.cz, "changelog_hook", changelog_hook_mock) - try: + if dry_run: + with pytest.raises(DryRunExit): + changelog() + else: changelog() - except DryRunExit: - pass full_changelog = ( "## Unreleased\n\n### Refactor\n\n- is in changelog\n\n### Feat\n\n- new file\n" diff --git a/tests/test_git.py b/tests/test_git.py index 2a31d9c0b8..f433ed633a 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -8,7 +8,8 @@ import pytest from pytest_mock import MockFixture -from commitizen import cmd, exceptions, git +from commitizen import cmd, git +from commitizen.exceptions import GitCommandError from tests.utils import ( FakeCommand, create_branch, @@ -110,10 +111,13 @@ def test_git_message_with_empty_body(): @pytest.mark.usefixtures("tmp_commitizen_project") def test_get_log_as_str_list_empty(): - """ensure an exception or empty list in an empty project""" + """ + Ensure an exception is raised or empty list in an empty project. + The behavior is different depending on the version of git. + """ try: gitlog = git._get_log_as_str_list(start=None, end="HEAD", args="") - except exceptions.GitCommandError: + except GitCommandError: return assert len(gitlog) == 0, "list should be empty if no assert" @@ -409,7 +413,7 @@ def test_get_filenames_in_commit_error(mocker: MockFixture): "commitizen.cmd.run", return_value=FakeCommand(out="", err="fatal: bad object HEAD", return_code=1), ) - with pytest.raises(exceptions.GitCommandError) as excinfo: + with pytest.raises(GitCommandError) as excinfo: git.get_filenames_in_commit() assert str(excinfo.value) == "fatal: bad object HEAD" @@ -497,5 +501,5 @@ def test_get_default_branch_error(mocker: MockFixture): return_code=1, ), ) - with pytest.raises(exceptions.GitCommandError): + with pytest.raises(GitCommandError): git.get_default_branch() From e2081c8202cc72a02bd6e7f9217a8f59f5dd75c6 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Wed, 3 Dec 2025 23:23:36 +0800 Subject: [PATCH 264/275] fix(git): replace lstrip with strip for compatibility issue #1032 --- commitizen/git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/git.py b/commitizen/git.py index c124cd9371..e598ff065c 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -237,7 +237,7 @@ def get_tags( ) -> list[GitTag]: inner_delimiter = "---inner_delimiter---" formatter = ( - f'"%(refname:lstrip=2){inner_delimiter}' + f'"%(refname:strip=2){inner_delimiter}' f"%(objectname){inner_delimiter}" f"%(creatordate:format:{dateformat}){inner_delimiter}" f'%(object)"' From 755fd82ee8d9ba7f822833571c3886c2af90262a Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Wed, 3 Dec 2025 21:53:58 +0800 Subject: [PATCH 265/275] perf: add TYPE_CHECKING to CzQuestion imports --- commitizen/cz/base.py | 6 ++++-- .../cz/conventional_commits/conventional_commits.py | 8 ++++++-- commitizen/cz/customize/customize.py | 4 ++-- commitizen/cz/jira/jira.py | 7 ++++++- commitizen/defaults.py | 5 +++-- tests/conftest.py | 5 ++++- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 8466b58bba..f7d6ea24f2 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -2,14 +2,16 @@ from abc import ABCMeta, abstractmethod from collections.abc import Iterable, Mapping -from typing import Any, Callable, Protocol +from typing import TYPE_CHECKING, Any, Callable, Protocol from jinja2 import BaseLoader, PackageLoader from prompt_toolkit.styles import Style from commitizen import git from commitizen.config.base_config import BaseConfig -from commitizen.question import CzQuestion + +if TYPE_CHECKING: + from commitizen.question import CzQuestion class MessageBuilderHook(Protocol): diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index f0b254eb10..1d1930595b 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,10 +1,14 @@ +from __future__ import annotations + import os -from typing import TypedDict +from typing import TYPE_CHECKING, TypedDict from commitizen import defaults from commitizen.cz.base import BaseCommitizen from commitizen.cz.utils import multiple_line_breaker, required_validator -from commitizen.question import CzQuestion + +if TYPE_CHECKING: + from commitizen.question import CzQuestion __all__ = ["ConventionalCommitsCz"] diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index dde2685496..fa774e4202 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -3,10 +3,10 @@ from collections.abc import Mapping from typing import TYPE_CHECKING, Any -from commitizen.question import CzQuestion - if TYPE_CHECKING: from jinja2 import Template + + from commitizen.question import CzQuestion else: try: from jinja2 import Template diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 4e6024ff74..9c9fbf8fa2 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,8 +1,13 @@ +from __future__ import annotations + import os from collections.abc import Mapping +from typing import TYPE_CHECKING from commitizen.cz.base import BaseCommitizen -from commitizen.question import CzQuestion + +if TYPE_CHECKING: + from commitizen.question import CzQuestion __all__ = ["JiraSmartCz"] diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 9b3b76a686..c78dc18f8c 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -4,9 +4,10 @@ import warnings from collections import OrderedDict from collections.abc import Iterable, MutableMapping, Sequence -from typing import Any, TypedDict +from typing import TYPE_CHECKING, Any, TypedDict -from commitizen.question import CzQuestion +if TYPE_CHECKING: + from commitizen.question import CzQuestion class CzSettings(TypedDict, total=False): diff --git a/tests/conftest.py b/tests/conftest.py index 04a448d99a..95e4a33c65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import tempfile from collections.abc import Iterator, Mapping from pathlib import Path +from typing import TYPE_CHECKING import pytest from pytest_mock import MockerFixture @@ -17,7 +18,9 @@ from commitizen.config import BaseConfig from commitizen.cz import registry from commitizen.cz.base import BaseCommitizen -from commitizen.question import CzQuestion + +if TYPE_CHECKING: + from commitizen.question import CzQuestion from tests.utils import create_file_and_commit SIGNER = "GitHub Action" From bc2ba3951db4bac596925ca8bfaea4bfe20d4440 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Thu, 4 Dec 2025 00:35:05 +0800 Subject: [PATCH 266/275] fix(cli): debug and no_raise can be used together in sys.excepthook Closes #1294 --- commitizen/cli.py | 28 ++++++++-------------------- tests/test_cli.py | 14 +++++++------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index c11e9078dc..f4f92cb0a4 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -559,8 +559,6 @@ def __call__( }, } -original_excepthook = sys.excepthook - def commitizen_excepthook( type: type[BaseException], @@ -571,26 +569,19 @@ def commitizen_excepthook( ) -> None: traceback = traceback if isinstance(traceback, TracebackType) else None if not isinstance(value, CommitizenException): - original_excepthook(type, value, traceback) + sys.__excepthook__(type, value, traceback) return - if not no_raise: - no_raise = [] if value.message: value.output_method(value.message) if debug: - original_excepthook(type, value, traceback) + sys.__excepthook__(type, value, traceback) exit_code = value.exit_code - if exit_code in no_raise: - exit_code = ExitCode.EXPECTED_EXIT + if no_raise is not None and exit_code in no_raise: + sys.exit(ExitCode.EXPECTED_EXIT) sys.exit(exit_code) -commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True) - -sys.excepthook = commitizen_excepthook - - def parse_no_raise(comma_separated_no_raise: str) -> list[int]: """Convert the given string to exit codes. @@ -682,15 +673,12 @@ def main() -> None: elif not conf.path: conf.update({"name": "cz_conventional_commits"}) + sys.excepthook = commitizen_excepthook if args.debug: logging.getLogger("commitizen").setLevel(logging.DEBUG) - sys.excepthook = commitizen_debug_excepthook - elif args.no_raise: - no_raise_exit_codes = parse_no_raise(args.no_raise) - no_raise_debug_excepthook = partial( - commitizen_excepthook, no_raise=no_raise_exit_codes - ) - sys.excepthook = no_raise_debug_excepthook + sys.excepthook = partial(sys.excepthook, debug=True) + if args.no_raise: + sys.excepthook = partial(sys.excepthook, no_raise=parse_no_raise(args.no_raise)) args.func(conf, arguments)() # type: ignore[arg-type] diff --git a/tests/test_cli.py b/tests/test_cli.py index 31371caea4..f91a27373a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -186,10 +186,10 @@ def test_unknown_args_before_double_dash_raises(mocker: MockFixture): def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture): - """Test that commitizen_excepthook delegates to original_excepthook for non-CommitizenException.""" + """Test that commitizen_excepthook delegates to sys.__excepthook__ for non-CommitizenException.""" # Mock the original excepthook mock_original_excepthook = mocker.Mock() - mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) # Create a regular exception test_exception = ValueError("test error") @@ -197,7 +197,7 @@ def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture): # Call commitizen_excepthook with the regular exception cli.commitizen_excepthook(ValueError, test_exception, None) - # Verify original_excepthook was called with correct arguments + # Verify sys.__excepthook__ was called with correct arguments mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) @@ -207,7 +207,7 @@ def test_commitizen_excepthook_non_commitizen_exception_with_traceback( """Test that commitizen_excepthook handles traceback correctly for non-CommitizenException.""" # Mock the original excepthook mock_original_excepthook = mocker.Mock() - mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) # Create a regular exception with a traceback test_exception = ValueError("test error") @@ -216,7 +216,7 @@ def test_commitizen_excepthook_non_commitizen_exception_with_traceback( # Call commitizen_excepthook with the regular exception and traceback cli.commitizen_excepthook(ValueError, test_exception, test_traceback) - # Verify original_excepthook was called with correct arguments including traceback + # Verify sys.__excepthook__ was called with correct arguments including traceback mock_original_excepthook.assert_called_once_with( ValueError, test_exception, test_traceback ) @@ -228,7 +228,7 @@ def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback( """Test that commitizen_excepthook handles invalid traceback correctly for non-CommitizenException.""" # Mock the original excepthook mock_original_excepthook = mocker.Mock() - mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook) + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) # Create a regular exception with an invalid traceback test_exception = ValueError("test error") @@ -237,5 +237,5 @@ def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback( # Call commitizen_excepthook with the regular exception and invalid traceback cli.commitizen_excepthook(ValueError, test_exception, test_traceback) - # Verify original_excepthook was called with None as traceback + # Verify sys.__excepthook__ was called with None as traceback mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) From 1441c3d4b128eb6f11818730b94692dd7496c5dc Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Thu, 4 Dec 2025 21:42:28 +0800 Subject: [PATCH 267/275] perf(ruff): enable ruff rules TC001~TC006 --- commitizen/bump.py | 11 +++++++---- commitizen/changelog.py | 8 ++++---- commitizen/changelog_formats/__init__.py | 8 +++++--- commitizen/changelog_formats/base.py | 6 ++++-- commitizen/cmd.py | 6 ++++-- commitizen/commands/bump.py | 10 ++++++---- commitizen/commands/changelog.py | 11 +++++++---- commitizen/commands/check.py | 6 ++++-- commitizen/commands/commit.py | 9 ++++++--- commitizen/commands/init.py | 10 ++++++---- commitizen/config/__init__.py | 5 ++++- commitizen/config/factory.py | 8 ++++++-- commitizen/config/json_config.py | 2 +- commitizen/config/toml_config.py | 2 +- commitizen/config/yaml_config.py | 2 +- commitizen/cz/__init__.py | 8 ++++++-- commitizen/cz/base.py | 8 ++++---- commitizen/cz/customize/customize.py | 5 +++-- commitizen/cz/jira/jira.py | 3 ++- commitizen/defaults.py | 3 ++- commitizen/hooks.py | 5 ++++- commitizen/providers/__init__.py | 10 ++++++---- commitizen/providers/base_provider.py | 8 +++++--- commitizen/providers/cargo_provider.py | 4 +++- commitizen/providers/npm_provider.py | 6 ++++-- commitizen/providers/poetry_provider.py | 5 ++++- commitizen/providers/uv_provider.py | 5 +---- commitizen/tags.py | 2 +- commitizen/version_schemes.py | 4 ++-- pyproject.toml | 7 +++++++ tests/commands/test_bump_command.py | 12 ++++++++---- tests/commands/test_check_command.py | 5 ++++- tests/commands/test_init_command.py | 9 ++++++--- tests/conftest.py | 8 +++++--- tests/providers/conftest.py | 5 ++++- tests/providers/test_base_provider.py | 6 +++++- tests/providers/test_cargo_provider.py | 5 ++++- tests/providers/test_commitizen_provider.py | 3 ++- tests/providers/test_composer_provider.py | 8 ++++++-- tests/providers/test_npm_provider.py | 8 ++++++-- tests/providers/test_pep621_provider.py | 8 ++++++-- tests/providers/test_poetry_provider.py | 8 ++++++-- tests/providers/test_scm_provider.py | 6 +++++- tests/providers/test_uv_provider.py | 3 ++- tests/test_changelog.py | 6 ++++-- tests/test_changelog_format_asciidoc.py | 8 ++++++-- tests/test_changelog_format_markdown.py | 8 ++++++-- tests/test_changelog_format_restructuredtext.py | 6 ++++-- tests/test_changelog_format_textile.py | 8 ++++++-- tests/test_changelog_formats.py | 6 +++++- tests/test_git.py | 5 ++++- tests/test_version_schemes.py | 9 +++++++-- tests/utils.py | 6 ++++-- 53 files changed, 235 insertions(+), 108 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index a955c20b6c..cb572d3612 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -3,16 +3,19 @@ import os import re from collections import OrderedDict -from collections.abc import Generator, Iterable from glob import iglob from logging import getLogger from string import Template -from typing import cast +from typing import TYPE_CHECKING, cast from commitizen.defaults import BUMP_MESSAGE, MAJOR, MINOR, PATCH from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open -from commitizen.version_schemes import Increment, Version + +if TYPE_CHECKING: + from collections.abc import Generator, Iterable + + from commitizen.version_schemes import Increment, Version VERSION_TYPES = [None, PATCH, MINOR, MAJOR] @@ -56,7 +59,7 @@ def find_increment( if increment == MAJOR: break - return cast(Increment, increment) + return cast("Increment", increment) def update_version_in_files( diff --git a/commitizen/changelog.py b/commitizen/changelog.py index bdf11326be..dfc4157725 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -29,7 +29,6 @@ import re from collections import OrderedDict, defaultdict -from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence from dataclasses import dataclass from datetime import date from itertools import chain @@ -44,13 +43,14 @@ Template, ) -from commitizen.cz.base import ChangelogReleaseHook from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError -from commitizen.git import GitCommit, GitTag from commitizen.tags import TagRules if TYPE_CHECKING: - from commitizen.cz.base import MessageBuilderHook + from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence + + from commitizen.cz.base import ChangelogReleaseHook, MessageBuilderHook + from commitizen.git import GitCommit, GitTag @dataclass diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py index 9a5eea7ab2..e4eb5e0a59 100644 --- a/commitizen/changelog_formats/__init__.py +++ b/commitizen/changelog_formats/__init__.py @@ -1,17 +1,19 @@ from __future__ import annotations import sys -from typing import Callable, ClassVar, Protocol +from typing import TYPE_CHECKING, Callable, ClassVar, Protocol if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.changelog import Metadata -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ChangelogFormatUnknown +if TYPE_CHECKING: + from commitizen.changelog import Metadata + from commitizen.config.base_config import BaseConfig + CHANGELOG_FORMAT_ENTRYPOINT = "commitizen.changelog_format" TEMPLATE_EXTENSION = "j2" diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index 64a795207a..eef6a23689 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -2,15 +2,17 @@ import os from abc import ABCMeta -from typing import IO, Any, ClassVar +from typing import IO, TYPE_CHECKING, Any, ClassVar from commitizen.changelog import Metadata -from commitizen.config.base_config import BaseConfig from commitizen.tags import TagRules, VersionTag from commitizen.version_schemes import get_version_scheme from . import ChangelogFormat +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + class BaseFormat(ChangelogFormat, metaclass=ABCMeta): """ diff --git a/commitizen/cmd.py b/commitizen/cmd.py index c8d4f33010..fe70da9c9b 100644 --- a/commitizen/cmd.py +++ b/commitizen/cmd.py @@ -2,13 +2,15 @@ import os import subprocess -from collections.abc import Mapping -from typing import NamedTuple +from typing import TYPE_CHECKING, NamedTuple from charset_normalizer import from_bytes from commitizen.exceptions import CharacterSetDecodeError +if TYPE_CHECKING: + from collections.abc import Mapping + class Command(NamedTuple): out: str diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index acf127fe38..0ed5ffd5b8 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -2,14 +2,13 @@ import warnings from logging import getLogger -from typing import cast +from typing import TYPE_CHECKING, cast import questionary from commitizen import bump, factory, git, hooks, out from commitizen.changelog_formats import get_changelog_format from commitizen.commands.changelog import Changelog -from commitizen.config import BaseConfig from commitizen.defaults import Settings from commitizen.exceptions import ( BumpCommitFailedError, @@ -33,6 +32,9 @@ get_version_scheme, ) +if TYPE_CHECKING: + from commitizen.config import BaseConfig + logger = getLogger("commitizen") @@ -69,7 +71,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: self.config: BaseConfig = config self.arguments = arguments self.bump_settings = cast( - BumpArgs, + "BumpArgs", { **config.settings, **{ @@ -260,7 +262,7 @@ def __call__(self) -> None: ) ) - rules = TagRules.from_settings(cast(Settings, self.bump_settings)) + rules = TagRules.from_settings(cast("Settings", self.bump_settings)) current_tag = rules.find_tag_for(git.get_tags(), current_version) current_tag_version = ( current_tag.name if current_tag else rules.normalize_tag(current_version) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 805d7472da..215e03206a 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -2,15 +2,13 @@ import os import os.path -from collections.abc import Generator, Iterable from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path -from typing import Any, TypedDict, cast +from typing import TYPE_CHECKING, Any, TypedDict, cast from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format -from commitizen.config import BaseConfig from commitizen.cz.utils import strip_local_version from commitizen.exceptions import ( DryRunExit, @@ -24,6 +22,11 @@ from commitizen.tags import TagRules from commitizen.version_schemes import get_version_scheme +if TYPE_CHECKING: + from collections.abc import Generator, Iterable + + from commitizen.config import BaseConfig + class ChangelogArgs(TypedDict, total=False): change_type_map: dict[str, str] @@ -97,7 +100,7 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: self.config.settings.get("change_type_map") or self.cz.change_type_map ) self.change_type_order = cast( - list[str], + "list[str]", self.config.settings.get("change_type_order") or self.cz.change_type_order or defaults.CHANGE_TYPE_ORDER, diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index a6101f7df7..0ea5885eba 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -2,10 +2,9 @@ import re import sys -from typing import TypedDict +from typing import TYPE_CHECKING, TypedDict from commitizen import factory, git, out -from commitizen.config import BaseConfig from commitizen.exceptions import ( CommitMessageLengthExceededError, InvalidCommandArgumentError, @@ -13,6 +12,9 @@ NoCommitsFoundError, ) +if TYPE_CHECKING: + from commitizen.config import BaseConfig + class CheckArgs(TypedDict, total=False): commit_msg_file: str diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 7144bced87..3894d0b77e 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -5,13 +5,11 @@ import shutil import subprocess import tempfile -from pathlib import Path -from typing import TypedDict +from typing import TYPE_CHECKING, TypedDict import questionary from commitizen import factory, git, out -from commitizen.config import BaseConfig from commitizen.cz.exceptions import CzException from commitizen.cz.utils import get_backup_file_path from commitizen.exceptions import ( @@ -27,6 +25,11 @@ ) from commitizen.git import smart_open +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config import BaseConfig + class CommitArgs(TypedDict, total=False): all: bool diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 4a8a69d9e9..62678a2244 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -1,16 +1,13 @@ from __future__ import annotations from pathlib import Path -from typing import Any, NamedTuple +from typing import TYPE_CHECKING, Any, NamedTuple import questionary import yaml from commitizen import cmd, factory, out, project_info from commitizen.__version__ import __version__ -from commitizen.config import ( - BaseConfig, -) from commitizen.config.factory import create_config from commitizen.cz import registry from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS @@ -18,6 +15,11 @@ from commitizen.git import get_latest_tag_name, get_tag_names, smart_open from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme +if TYPE_CHECKING: + from commitizen.config import ( + BaseConfig, + ) + class _VersionProviderOption(NamedTuple): provider_name: str diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index 6cf8b840d8..e30f9f789c 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations -from collections.abc import Generator from pathlib import Path +from typing import TYPE_CHECKING from commitizen import defaults, git from commitizen.config.factory import create_config @@ -9,6 +9,9 @@ from .base_config import BaseConfig +if TYPE_CHECKING: + from collections.abc import Generator + def _resolve_config_paths(filepath: str | None = None) -> Generator[Path, None, None]: if filepath is not None: diff --git a/commitizen/config/factory.py b/commitizen/config/factory.py index b77aea5f32..d0a212b73f 100644 --- a/commitizen/config/factory.py +++ b/commitizen/config/factory.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING -from commitizen.config.base_config import BaseConfig from commitizen.config.json_config import JsonConfig from commitizen.config.toml_config import TomlConfig from commitizen.config.yaml_config import YAMLConfig +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + def create_config(*, data: bytes | str | None = None, path: Path) -> BaseConfig: if "toml" in path.suffix: diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 4e7097aa1a..860ca8ed5a 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -from pathlib import Path from typing import TYPE_CHECKING from commitizen.exceptions import InvalidConfigurationError @@ -11,6 +10,7 @@ if TYPE_CHECKING: import sys + from pathlib import Path # Self is Python 3.11+ but backported in typing-extensions if sys.version_info < (3, 11): diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 4ea1dca7d4..b10cf9bd3e 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -1,7 +1,6 @@ from __future__ import annotations import os -from pathlib import Path from typing import TYPE_CHECKING from tomlkit import TOMLDocument, exceptions, parse, table @@ -12,6 +11,7 @@ if TYPE_CHECKING: import sys + from pathlib import Path # Self is Python 3.11+ but backported in typing-extensions if sys.version_info < (3, 11): diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index c048ab272a..58722d0f60 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING import yaml @@ -12,6 +11,7 @@ if TYPE_CHECKING: import sys + from pathlib import Path # Self is Python 3.11+ but backported in typing-extensions if sys.version_info < (3, 11): diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py index cb17fe32cd..58753036ff 100644 --- a/commitizen/cz/__init__.py +++ b/commitizen/cz/__init__.py @@ -4,14 +4,18 @@ import pkgutil import sys import warnings -from collections.abc import Iterable if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.cz.base import BaseCommitizen +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterable + + from commitizen.cz.base import BaseCommitizen def discover_plugins( diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index f7d6ea24f2..e4bf88cc1c 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -1,16 +1,16 @@ from __future__ import annotations from abc import ABCMeta, abstractmethod -from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Any, Callable, Protocol from jinja2 import BaseLoader, PackageLoader from prompt_toolkit.styles import Style -from commitizen import git -from commitizen.config.base_config import BaseConfig - if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + from commitizen import git + from commitizen.config.base_config import BaseConfig from commitizen.question import CzQuestion diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index fa774e4202..0bc31db30a 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -1,11 +1,13 @@ from __future__ import annotations -from collections.abc import Mapping from typing import TYPE_CHECKING, Any if TYPE_CHECKING: + from collections.abc import Mapping + from jinja2 import Template + from commitizen.config import BaseConfig from commitizen.question import CzQuestion else: try: @@ -15,7 +17,6 @@ from commitizen import defaults -from commitizen.config import BaseConfig from commitizen.cz.base import BaseCommitizen from commitizen.exceptions import MissingCzCustomizeConfigError diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 9c9fbf8fa2..bbe4fc5ee3 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,12 +1,13 @@ from __future__ import annotations import os -from collections.abc import Mapping from typing import TYPE_CHECKING from commitizen.cz.base import BaseCommitizen if TYPE_CHECKING: + from collections.abc import Mapping + from commitizen.question import CzQuestion __all__ = ["JiraSmartCz"] diff --git a/commitizen/defaults.py b/commitizen/defaults.py index c78dc18f8c..b91fe4879c 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,12 +1,13 @@ from __future__ import annotations -import pathlib import warnings from collections import OrderedDict from collections.abc import Iterable, MutableMapping, Sequence from typing import TYPE_CHECKING, Any, TypedDict if TYPE_CHECKING: + import pathlib + from commitizen.question import CzQuestion diff --git a/commitizen/hooks.py b/commitizen/hooks.py index f60bd9b43e..10560d5eae 100644 --- a/commitizen/hooks.py +++ b/commitizen/hooks.py @@ -1,11 +1,14 @@ from __future__ import annotations import os -from collections.abc import Mapping +from typing import TYPE_CHECKING from commitizen import cmd, out from commitizen.exceptions import RunHookError +if TYPE_CHECKING: + from collections.abc import Mapping + def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None: if isinstance(hooks, str): diff --git a/commitizen/providers/__init__.py b/commitizen/providers/__init__.py index 3e01fe22f8..4cef578ab9 100644 --- a/commitizen/providers/__init__.py +++ b/commitizen/providers/__init__.py @@ -1,16 +1,14 @@ from __future__ import annotations import sys -from typing import cast +from typing import TYPE_CHECKING, cast if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionProviderUnknown -from commitizen.providers.base_provider import VersionProvider from commitizen.providers.cargo_provider import CargoProvider from commitizen.providers.commitizen_provider import CommitizenProvider from commitizen.providers.composer_provider import ComposerProvider @@ -20,6 +18,10 @@ from commitizen.providers.scm_provider import ScmProvider from commitizen.providers.uv_provider import UvProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + from commitizen.providers.base_provider import VersionProvider + __all__ = [ "CargoProvider", "CommitizenProvider", @@ -48,4 +50,4 @@ def get_provider(config: BaseConfig) -> VersionProvider: except ValueError: raise VersionProviderUnknown(f'Version Provider "{provider_name}" unknown.') provider_cls = ep.load() - return cast(VersionProvider, provider_cls(config)) + return cast("VersionProvider", provider_cls(config)) diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index c91bfdae20..84b745e326 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -2,13 +2,15 @@ import json from abc import ABC, abstractmethod -from collections.abc import Mapping from pathlib import Path -from typing import Any, ClassVar +from typing import TYPE_CHECKING, Any, ClassVar import tomlkit -from commitizen.config.base_config import BaseConfig +if TYPE_CHECKING: + from collections.abc import Mapping + + from commitizen.config.base_config import BaseConfig class VersionProvider(ABC): diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 02fdd2cc6d..ca00f05e7b 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -7,10 +7,12 @@ from tomlkit import TOMLDocument, dumps, parse from tomlkit.exceptions import NonExistentKey -from tomlkit.items import AoT from commitizen.providers.base_provider import TomlProvider +if TYPE_CHECKING: + from tomlkit.items import AoT + class CargoProvider(TomlProvider): """ diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py index 3125447250..7aeb0ee7df 100644 --- a/commitizen/providers/npm_provider.py +++ b/commitizen/providers/npm_provider.py @@ -1,12 +1,14 @@ from __future__ import annotations import json -from collections.abc import Mapping from pathlib import Path -from typing import Any, ClassVar +from typing import TYPE_CHECKING, Any, ClassVar from commitizen.providers.base_provider import VersionProvider +if TYPE_CHECKING: + from collections.abc import Mapping + class NpmProvider(VersionProvider): """ diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py index f63b13b793..d9a174a0a4 100644 --- a/commitizen/providers/poetry_provider.py +++ b/commitizen/providers/poetry_provider.py @@ -1,9 +1,12 @@ from __future__ import annotations -import tomlkit +from typing import TYPE_CHECKING from commitizen.providers.base_provider import TomlProvider +if TYPE_CHECKING: + import tomlkit + class PoetryProvider(TomlProvider): """ diff --git a/commitizen/providers/uv_provider.py b/commitizen/providers/uv_provider.py index 21e8322d94..4574dc4b6b 100644 --- a/commitizen/providers/uv_provider.py +++ b/commitizen/providers/uv_provider.py @@ -1,15 +1,12 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING import tomlkit +import tomlkit.items from commitizen.providers.base_provider import TomlProvider -if TYPE_CHECKING: - import tomlkit.items - class UvProvider(TomlProvider): """ diff --git a/commitizen/tags.py b/commitizen/tags.py index e3f54370f1..68c74a72e6 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -2,7 +2,6 @@ import re import warnings -from collections.abc import Iterable, Sequence from dataclasses import dataclass, field from functools import cached_property from itertools import chain @@ -22,6 +21,7 @@ if TYPE_CHECKING: import sys + from collections.abc import Iterable, Sequence from commitizen.version_schemes import VersionScheme diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 0696d85aa5..0c71bb06cc 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -277,7 +277,7 @@ def bump( current_base = ".".join(str(part) for part in release) pre_version = ( - self if base == current_base else cast(BaseVersion, self.scheme(base)) + self if base == current_base else cast("BaseVersion", self.scheme(base)) ).generate_prerelease(prerelease, offset=prerelease_offset) # TODO: post version @@ -433,7 +433,7 @@ def get_version_scheme(settings: Settings, name: str | None = None) -> VersionSc (ep,) = metadata.entry_points(name=name, group=SCHEMES_ENTRYPOINT) except ValueError: raise VersionSchemeUnknown(f'Version scheme "{name}" unknown.') - scheme = cast(VersionScheme, ep.load()) + scheme = cast("VersionScheme", ep.load()) if not isinstance(scheme, VersionProtocol): warnings.warn(f"Version scheme {name} does not implement the VersionProtocol") diff --git a/pyproject.toml b/pyproject.toml index 097aed9c1e..7ea99cc69f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,6 +208,13 @@ select = [ "RUF100", # Checks for uses of the assert keyword. "S101", + # flake8-type-checking (TC) + "TC001", + "TC002", + "TC003", + "TC004", + "TC005", + "TC006", ] ignore = ["E501", "D1", "D415"] diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 8871a6bf0f..8dbe8f447e 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -5,17 +5,14 @@ import sys from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING from unittest.mock import MagicMock, call -import py import pytest -from pytest_mock import MockFixture import commitizen.commands.bump as bump from commitizen import cli, cmd, defaults, git, hooks -from commitizen.changelog_formats import ChangelogFormat from commitizen.config.base_config import BaseConfig -from commitizen.cz.base import BaseCommitizen from commitizen.exceptions import ( BumpTagFailedError, CommitizenException, @@ -33,6 +30,13 @@ ) from tests.utils import create_file_and_commit, create_tag, skip_below_py_3_13 +if TYPE_CHECKING: + import py + from pytest_mock import MockFixture + + from commitizen.changelog_formats import ChangelogFormat + from commitizen.cz.base import BaseCommitizen + @pytest.mark.parametrize( "commit_msg", diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index d2a82a903a..e7918ca238 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -2,9 +2,9 @@ import sys from io import StringIO +from typing import TYPE_CHECKING import pytest -from pytest_mock import MockFixture from commitizen import cli, commands, git from commitizen.exceptions import ( @@ -15,6 +15,9 @@ ) from tests.utils import create_file_and_commit, skip_below_py_3_13 +if TYPE_CHECKING: + from pytest_mock import MockFixture + COMMIT_LOG = [ "refactor: A code change that neither fixes a bug nor adds a feature", r"refactor(cz/connventional_commit): use \S to check scope", diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index cfecfebeb1..54fa271fd9 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -4,18 +4,21 @@ import os import sys from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any import pytest import yaml -from pytest_mock import MockFixture from commitizen import cli, cmd, commands from commitizen.__version__ import __version__ -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import InitFailedError, NoAnswersError from tests.utils import skip_below_py_3_10 +if TYPE_CHECKING: + from pytest_mock import MockFixture + + from commitizen.config.base_config import BaseConfig + class FakeQuestion: def __init__(self, expected_return): diff --git a/tests/conftest.py b/tests/conftest.py index 95e4a33c65..5eaa17440a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,12 +3,9 @@ import os import re import tempfile -from collections.abc import Iterator, Mapping -from pathlib import Path from typing import TYPE_CHECKING import pytest -from pytest_mock import MockerFixture from commitizen import cmd, defaults from commitizen.changelog_formats import ( @@ -20,6 +17,11 @@ from commitizen.cz.base import BaseCommitizen if TYPE_CHECKING: + from collections.abc import Iterator, Mapping + from pathlib import Path + + from pytest_mock import MockerFixture + from commitizen.question import CzQuestion from tests.utils import create_file_and_commit diff --git a/tests/providers/conftest.py b/tests/providers/conftest.py index f73cdb72a5..41b7bd02f1 100644 --- a/tests/providers/conftest.py +++ b/tests/providers/conftest.py @@ -1,11 +1,14 @@ from __future__ import annotations import os -from collections.abc import Iterator from pathlib import Path +from typing import TYPE_CHECKING import pytest +if TYPE_CHECKING: + from collections.abc import Iterator + @pytest.fixture def chdir(tmp_path: Path) -> Iterator[Path]: diff --git a/tests/providers/test_base_provider.py b/tests/providers/test_base_provider.py index 482bd698ea..782a8ba89e 100644 --- a/tests/providers/test_base_provider.py +++ b/tests/providers/test_base_provider.py @@ -1,12 +1,16 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionProviderUnknown from commitizen.providers import get_provider from commitizen.providers.commitizen_provider import CommitizenProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + def test_default_version_provider_is_commitizen_config(config: BaseConfig): provider = get_provider(config) diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index b7dc932d38..ea15fdbf39 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -3,13 +3,16 @@ import os from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.cargo_provider import CargoProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + CARGO_TOML = """\ [package] name = "whatever" diff --git a/tests/providers/test_commitizen_provider.py b/tests/providers/test_commitizen_provider.py index b8df60da93..c28dc90f79 100644 --- a/tests/providers/test_commitizen_provider.py +++ b/tests/providers/test_commitizen_provider.py @@ -2,12 +2,13 @@ from typing import TYPE_CHECKING -from commitizen.config.base_config import BaseConfig from commitizen.providers.commitizen_provider import CommitizenProvider if TYPE_CHECKING: from pytest_mock import MockerFixture + from commitizen.config.base_config import BaseConfig + def test_commitizen_provider(config: BaseConfig, mocker: MockerFixture): config.settings["version"] = "42" diff --git a/tests/providers/test_composer_provider.py b/tests/providers/test_composer_provider.py index 45cbc8afa4..22357b7a7f 100644 --- a/tests/providers/test_composer_provider.py +++ b/tests/providers/test_composer_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.composer_provider import ComposerProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + COMPOSER_JSON = """\ { "name": "whatever", diff --git a/tests/providers/test_npm_provider.py b/tests/providers/test_npm_provider.py index bc9399916d..785a2cb7fd 100644 --- a/tests/providers/test_npm_provider.py +++ b/tests/providers/test_npm_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.npm_provider import NpmProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + NPM_PACKAGE_JSON = """\ { "name": "whatever", diff --git a/tests/providers/test_pep621_provider.py b/tests/providers/test_pep621_provider.py index 16bb479cc4..f44cef38c8 100644 --- a/tests/providers/test_pep621_provider.py +++ b/tests/providers/test_pep621_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.pep621_provider import Pep621Provider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + PEP621_TOML = """\ [project] version = "0.1.0" diff --git a/tests/providers/test_poetry_provider.py b/tests/providers/test_poetry_provider.py index e26e2a44fb..ad998d41c7 100644 --- a/tests/providers/test_poetry_provider.py +++ b/tests/providers/test_poetry_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.poetry_provider import PoetryProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + POETRY_TOML = """\ [tool.poetry] version = "0.1.0" diff --git a/tests/providers/test_scm_provider.py b/tests/providers/test_scm_provider.py index 9d955b2323..e28a6782ab 100644 --- a/tests/providers/test_scm_provider.py +++ b/tests/providers/test_scm_provider.py @@ -1,8 +1,9 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.scm_provider import ScmProvider from tests.utils import ( @@ -13,6 +14,9 @@ switch_branch, ) +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + @pytest.mark.parametrize( "tag_format,tag,expected_version", diff --git a/tests/providers/test_uv_provider.py b/tests/providers/test_uv_provider.py index 4093709376..7cb0782dd6 100644 --- a/tests/providers/test_uv_provider.py +++ b/tests/providers/test_uv_provider.py @@ -2,13 +2,14 @@ from typing import TYPE_CHECKING -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.uv_provider import UvProvider if TYPE_CHECKING: from pytest_regressions.file_regression import FileRegressionFixture + from commitizen.config.base_config import BaseConfig + PYPROJECT_TOML = """ [project] diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 4465fcccbc..e32a9bcf9e 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -4,14 +4,13 @@ import re from dataclasses import dataclass from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any from unittest.mock import Mock import pytest from jinja2 import FileSystemLoader from commitizen import changelog, git -from commitizen.changelog_formats import ChangelogFormat from commitizen.commands.changelog import Changelog from commitizen.config import BaseConfig from commitizen.cz.conventional_commits.conventional_commits import ( @@ -20,6 +19,9 @@ from commitizen.exceptions import InvalidConfigurationError from commitizen.version_schemes import Pep440 +if TYPE_CHECKING: + from commitizen.changelog_formats import ChangelogFormat + COMMITS_DATA: list[dict[str, Any]] = [ { "rev": "141ee441c9c9da0809c554103a558eb17c30ed17", diff --git a/tests/test_changelog_format_asciidoc.py b/tests/test_changelog_format_asciidoc.py index cc81a24f2a..199c4b9729 100644 --- a/tests/test_changelog_format_asciidoc.py +++ b/tests/test_changelog_format_asciidoc.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.asciidoc import AsciiDoc -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ = Changelog diff --git a/tests/test_changelog_format_markdown.py b/tests/test_changelog_format_markdown.py index 1abc63f29f..e09d68cfed 100644 --- a/tests/test_changelog_format_markdown.py +++ b/tests/test_changelog_format_markdown.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.markdown import Markdown -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ # Changelog diff --git a/tests/test_changelog_format_restructuredtext.py b/tests/test_changelog_format_restructuredtext.py index ca79620ad3..e6eceff4fa 100644 --- a/tests/test_changelog_format_restructuredtext.py +++ b/tests/test_changelog_format_restructuredtext.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING @@ -12,11 +11,14 @@ _is_overlined_title, _is_underlined_title, ) -from commitizen.config.base_config import BaseConfig if TYPE_CHECKING: + from pathlib import Path + from _pytest.mark.structures import ParameterSet + from commitizen.config.base_config import BaseConfig + CASES: list[ParameterSet] = [] diff --git a/tests/test_changelog_format_textile.py b/tests/test_changelog_format_textile.py index 812fa6bf60..752cf229e5 100644 --- a/tests/test_changelog_format_textile.py +++ b/tests/test_changelog_format_textile.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.textile import Textile -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ h1. Changelog diff --git a/tests/test_changelog_formats.py b/tests/test_changelog_formats.py index e0d99e0325..ff7126d345 100644 --- a/tests/test_changelog_formats.py +++ b/tests/test_changelog_formats.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest from commitizen import defaults @@ -9,9 +11,11 @@ _guess_changelog_format, get_changelog_format, ) -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ChangelogFormatUnknown +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + @pytest.mark.parametrize("format", KNOWN_CHANGELOG_FORMATS.values()) def test_guess_format(format: type[ChangelogFormat]): diff --git a/tests/test_git.py b/tests/test_git.py index f433ed633a..45e6028c9e 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -4,9 +4,9 @@ import os import platform import shutil +from typing import TYPE_CHECKING import pytest -from pytest_mock import MockFixture from commitizen import cmd, git from commitizen.exceptions import GitCommandError @@ -18,6 +18,9 @@ switch_branch, ) +if TYPE_CHECKING: + from pytest_mock import MockFixture + @pytest.mark.parametrize("date", ["2020-01-21", "1970-01-01"]) def test_git_tag_date(date: str): diff --git a/tests/test_version_schemes.py b/tests/test_version_schemes.py index 8e2dae9027..afaae76188 100644 --- a/tests/test_version_schemes.py +++ b/tests/test_version_schemes.py @@ -7,13 +7,18 @@ else: import importlib_metadata as metadata +from typing import TYPE_CHECKING + import pytest -from pytest_mock import MockerFixture -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionSchemeUnknown from commitizen.version_schemes import Pep440, SemVer, get_version_scheme +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from commitizen.config.base_config import BaseConfig + def test_default_version_scheme_is_pep440(config: BaseConfig): scheme = get_version_scheme(config.settings) diff --git a/tests/utils.py b/tests/utils.py index 43e0cf79a0..bea7f20a1d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,13 +4,15 @@ import time import uuid from pathlib import Path -from typing import NamedTuple +from typing import TYPE_CHECKING, NamedTuple import pytest from deprecated import deprecated from commitizen import cmd, exceptions, git -from commitizen.version_schemes import Increment, Prerelease + +if TYPE_CHECKING: + from commitizen.version_schemes import Increment, Prerelease skip_below_py_3_10 = pytest.mark.skipif( sys.version_info < (3, 10), From 609011ce85a4d66ab3f54a84fd0db2b1551ea5a0 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Sat, 22 Nov 2025 16:35:16 +0800 Subject: [PATCH 268/275] fix(version): fix the behavior of cz version --major --- commitizen/cli.py | 4 +- commitizen/commands/version.py | 47 ++++++++----- tests/commands/test_version_command.py | 69 ++++++++++++------- ...shows_description_when_use_help_option.txt | 6 +- 4 files changed, 80 insertions(+), 46 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index f4f92cb0a4..e5538aeb49 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -543,13 +543,13 @@ def __call__( }, { "name": ["--major"], - "help": "get just the major version", + "help": "get just the major version. Need to be used with --project or --verbose.", "action": "store_true", "exclusive_group": "group2", }, { "name": ["--minor"], - "help": "get just the minor version", + "help": "get just the minor version. Need to be used with --project or --verbose.", "action": "store_true", "exclusive_group": "group2", }, diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 7ccadb5135..338fce0760 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -11,6 +11,7 @@ class VersionArgs(TypedDict, total=False): + commitizen: bool report: bool project: bool verbose: bool @@ -19,46 +20,58 @@ class VersionArgs(TypedDict, total=False): class Version: - """Get the version of the installed commitizen or the current project.""" + """Get the version of the installed commitizen or the current project. + Precedence: + 1. report + 2. commitizen + 3. verbose, project + """ def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None: self.config: BaseConfig = config self.parameter = arguments - self.operating_system = platform.system() - self.python_version = sys.version def __call__(self) -> None: if self.parameter.get("report"): out.write(f"Commitizen Version: {__version__}") - out.write(f"Python Version: {self.python_version}") - out.write(f"Operating System: {self.operating_system}") + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") return - if (verbose := self.parameter.get("verbose")) or self.parameter.get("project"): - if verbose: - out.write(f"Installed Commitizen Version: {__version__}") + if self.parameter.get("verbose"): + out.write(f"Installed Commitizen Version: {__version__}") + if not self.parameter.get("commitizen") and ( + self.parameter.get("project") or self.parameter.get("verbose") + ): try: version = get_provider(self.config).get_version() except NoVersionSpecifiedError: out.error("No project information in this project.") return - try: - version_scheme = get_version_scheme(self.config.settings) + version_scheme = get_version_scheme(self.config.settings)(version) except VersionSchemeUnknown: out.error("Unknown version scheme.") return - _version = version_scheme(version) - if self.parameter.get("major"): - version = f"{_version.major}" + version = f"{version_scheme.major}" elif self.parameter.get("minor"): - version = f"{_version.minor}" + version = f"{version_scheme.minor}" + + out.write( + f"Project Version: {version}" + if self.parameter.get("verbose") + else version + ) + return - out.write(f"Project Version: {version}" if verbose else version) + if self.parameter.get("major") or self.parameter.get("minor"): + out.error( + "Major or minor version can only be used with --project or --verbose." + ) return - # if no argument is given, show installed commitizen version - out.write(f"{__version__}") + # If no arguments are provided, just show the installed commitizen version + out.write(__version__) diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 7b5b13a7e1..a5faf4e16d 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -11,54 +11,51 @@ from tests.utils import skip_below_py_3_10 -def test_version_for_showing_project_version(config, capsys): - # No version exist +def test_version_for_showing_project_version_error(config, capsys): + # No version specified in config commands.Version( config, - {"report": False, "project": True, "commitizen": False, "verbose": False}, + {"project": True}, )() captured = capsys.readouterr() assert "No project information in this project." in captured.err + +def test_version_for_showing_project_version(config, capsys): config.settings["version"] = "v0.0.1" commands.Version( config, - {"report": False, "project": True, "commitizen": False, "verbose": False}, + {"project": True}, )() captured = capsys.readouterr() assert "v0.0.1" in captured.out -def test_version_for_showing_commitizen_version(config, capsys): - commands.Version( - config, - {"report": False, "project": False, "commitizen": True, "verbose": False}, - )() - captured = capsys.readouterr() - assert f"{__version__}" in captured.out - - # default showing commitizen version +@pytest.mark.parametrize("project", (True, False)) +def test_version_for_showing_commitizen_version(config, capsys, project: bool): commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": False}, + {"project": project, "commitizen": True}, )() captured = capsys.readouterr() assert f"{__version__}" in captured.out -def test_version_for_showing_both_versions(config, capsys): +def test_version_for_showing_both_versions_no_project(config, capsys): commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": True}, + {"verbose": True}, )() captured = capsys.readouterr() assert f"Installed Commitizen Version: {__version__}" in captured.out assert "No project information in this project." in captured.err + +def test_version_for_showing_both_versions(config, capsys): config.settings["version"] = "v0.0.1" commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": True}, + {"verbose": True}, )() captured = capsys.readouterr() expected_out = ( @@ -70,7 +67,7 @@ def test_version_for_showing_both_versions(config, capsys): def test_version_for_showing_commitizen_system_info(config, capsys): commands.Version( config, - {"report": True, "project": False, "commitizen": False, "verbose": False}, + {"report": True}, )() captured = capsys.readouterr() assert f"Commitizen Version: {__version__}" in captured.out @@ -96,7 +93,6 @@ def test_version_use_version_provider( commands.Version( config, { - "report": False, "project": project, "verbose": not project, }, @@ -135,16 +131,20 @@ def test_version_command_shows_description_when_use_help_option( @pytest.mark.parametrize( - "version, expected_version", (("1.0.0", "1\n"), ("2.1.3", "2\n"), ("0.0.1", "0\n")) + "version, expected_version", + [ + ("1.0.0", "1\n"), + ("2.1.3", "2\n"), + ("0.0.1", "0\n"), + ("0.1.0", "0\n"), + ], ) def test_version_just_major(config, capsys, version: str, expected_version: str): config.settings["version"] = version commands.Version( config, { - "report": False, "project": True, - "verbose": False, "major": True, }, )() @@ -154,18 +154,37 @@ def test_version_just_major(config, capsys, version: str, expected_version: str) @pytest.mark.parametrize( "version, expected_version", - (("1.0.0", "0\n"), ("2.1.3", "1\n"), ("0.0.1", "0\n"), ("0.1.0", "1\n")), + [ + ("1.0.0", "0\n"), + ("2.1.3", "1\n"), + ("0.0.1", "0\n"), + ("0.1.0", "1\n"), + ], ) def test_version_just_minor(config, capsys, version: str, expected_version: str): config.settings["version"] = version commands.Version( config, { - "report": False, "project": True, - "verbose": False, "minor": True, }, )() captured = capsys.readouterr() assert expected_version == captured.out + + +@pytest.mark.parametrize("argument", ("major", "minor")) +def test_version_just_major_error_no_project(config, capsys, argument: str): + commands.Version( + config, + { + argument: True, # type: ignore[misc] + }, + )() + captured = capsys.readouterr() + assert not captured.out + assert ( + "Major or minor version can only be used with --project or --verbose." + in captured.err + ) diff --git a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt index b1ed94124e..a194615a98 100644 --- a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt @@ -10,5 +10,7 @@ options: -c, --commitizen get the version of the installed commitizen -v, --verbose get the version of both the installed commitizen and the current project - --major get just the major version - --minor get just the minor version + --major get just the major version. Need to be used with --project + or --verbose. + --minor get just the minor version. Need to be used with --project + or --verbose. From 5b471c3e51e35382ef9911923b671b7cb5a7b45f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Thu, 27 Nov 2025 23:26:34 +0800 Subject: [PATCH 269/275] refactor(version): rename class member to align with other classes --- commitizen/commands/version.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 338fce0760..9290e80b8f 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -29,20 +29,20 @@ class Version: def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None: self.config: BaseConfig = config - self.parameter = arguments + self.arguments = arguments def __call__(self) -> None: - if self.parameter.get("report"): + if self.arguments.get("report"): out.write(f"Commitizen Version: {__version__}") out.write(f"Python Version: {sys.version}") out.write(f"Operating System: {platform.system()}") return - if self.parameter.get("verbose"): + if self.arguments.get("verbose"): out.write(f"Installed Commitizen Version: {__version__}") - if not self.parameter.get("commitizen") and ( - self.parameter.get("project") or self.parameter.get("verbose") + if not self.arguments.get("commitizen") and ( + self.arguments.get("project") or self.arguments.get("verbose") ): try: version = get_provider(self.config).get_version() @@ -55,19 +55,19 @@ def __call__(self) -> None: out.error("Unknown version scheme.") return - if self.parameter.get("major"): + if self.arguments.get("major"): version = f"{version_scheme.major}" - elif self.parameter.get("minor"): + elif self.arguments.get("minor"): version = f"{version_scheme.minor}" out.write( f"Project Version: {version}" - if self.parameter.get("verbose") + if self.arguments.get("verbose") else version ) return - if self.parameter.get("major") or self.parameter.get("minor"): + if self.arguments.get("major") or self.arguments.get("minor"): out.error( "Major or minor version can only be used with --project or --verbose." ) From 9473ef3c83bb90f9e398462b84aa70913192d668 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:24:38 +0000 Subject: [PATCH 270/275] docs(cli/screenshots): update CLI screenshots [skip ci] --- docs/images/cli_help/cz_version___help.svg | 90 ++++++++++++---------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/docs/images/cli_help/cz_version___help.svg b/docs/images/cli_help/cz_version___help.svg index 7ddec177d8..a8e0bd844e 100644 --- a/docs/images/cli_help/cz_version___help.svg +++ b/docs/images/cli_help/cz_version___help.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 994 440.4" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 994 489.2" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -19,100 +19,108 @@ font-weight: 700; } - .terminal-2062799951-matrix { + .terminal-752487608-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2062799951-title { + .terminal-752487608-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2062799951-r1 { fill: #c5c8c6 } -.terminal-2062799951-r2 { fill: #c5c8c6;font-weight: bold } + .terminal-752487608-r1 { fill: #c5c8c6 } +.terminal-752487608-r2 { fill: #c5c8c6;font-weight: bold } </style> <defs> - <clipPath id="terminal-2062799951-clip-terminal"> - <rect x="0" y="0" width="975.0" height="389.4" /> + <clipPath id="terminal-752487608-clip-terminal"> + <rect x="0" y="0" width="975.0" height="438.2" /> </clipPath> - <clipPath id="terminal-2062799951-line-0"> + <clipPath id="terminal-752487608-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-1"> +<clipPath id="terminal-752487608-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-2"> +<clipPath id="terminal-752487608-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-3"> +<clipPath id="terminal-752487608-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-4"> +<clipPath id="terminal-752487608-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-5"> +<clipPath id="terminal-752487608-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-6"> +<clipPath id="terminal-752487608-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-7"> +<clipPath id="terminal-752487608-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-8"> +<clipPath id="terminal-752487608-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-9"> +<clipPath id="terminal-752487608-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-10"> +<clipPath id="terminal-752487608-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-11"> +<clipPath id="terminal-752487608-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-12"> +<clipPath id="terminal-752487608-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-13"> +<clipPath id="terminal-752487608-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2062799951-line-14"> +<clipPath id="terminal-752487608-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> +<clipPath id="terminal-752487608-line-15"> + <rect x="0" y="367.5" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-752487608-line-16"> + <rect x="0" y="391.9" width="976" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="438.4" rx="8"/> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="487.2" rx="8"/> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-2062799951-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-752487608-clip-terminal)"> - <g class="terminal-2062799951-matrix"> - <text class="terminal-2062799951-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-2062799951-line-0)">$ cz version --help</text><text class="terminal-2062799951-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2062799951-line-0)"> -</text><text class="terminal-2062799951-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-2062799951-line-1)">usage: cz version </text><text class="terminal-2062799951-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">[</text><text class="terminal-2062799951-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-2062799951-line-1)">-h</text><text class="terminal-2062799951-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">]</text><text class="terminal-2062799951-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">[</text><text class="terminal-2062799951-r1" x="292.8" y="44.4" textLength="207.4" clip-path="url(#terminal-2062799951-line-1)">-r | -p | -c | -v</text><text class="terminal-2062799951-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">]</text><text class="terminal-2062799951-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">[</text><text class="terminal-2062799951-r1" x="536.8" y="44.4" textLength="207.4" clip-path="url(#terminal-2062799951-line-1)">--major | --minor</text><text class="terminal-2062799951-r2" x="744.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)">]</text><text class="terminal-2062799951-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-1)"> -</text><text class="terminal-2062799951-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2062799951-line-2)"> -</text><text class="terminal-2062799951-r1" x="0" y="93.2" textLength="817.4" clip-path="url(#terminal-2062799951-line-3)">get the version of the installed commitizen or the current project </text><text class="terminal-2062799951-r2" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-2062799951-line-3)">(</text><text class="terminal-2062799951-r1" x="829.6" y="93.2" textLength="97.6" clip-path="url(#terminal-2062799951-line-3)">default:</text><text class="terminal-2062799951-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2062799951-line-3)"> -</text><text class="terminal-2062799951-r1" x="0" y="117.6" textLength="244" clip-path="url(#terminal-2062799951-line-4)">installed commitizen</text><text class="terminal-2062799951-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-2062799951-line-4)">)</text><text class="terminal-2062799951-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2062799951-line-4)"> -</text><text class="terminal-2062799951-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2062799951-line-5)"> -</text><text class="terminal-2062799951-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-2062799951-line-6)">options:</text><text class="terminal-2062799951-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-6)"> -</text><text class="terminal-2062799951-r1" x="0" y="190.8" textLength="622.2" clip-path="url(#terminal-2062799951-line-7)">  -h, --help        show this help message and exit</text><text class="terminal-2062799951-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2062799951-line-7)"> -</text><text class="terminal-2062799951-r1" x="0" y="215.2" textLength="744.2" clip-path="url(#terminal-2062799951-line-8)">  -r, --report      get system information for reporting bugs</text><text class="terminal-2062799951-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2062799951-line-8)"> -</text><text class="terminal-2062799951-r1" x="0" y="239.6" textLength="707.6" clip-path="url(#terminal-2062799951-line-9)">  -p, --project     get the version of the current project</text><text class="terminal-2062799951-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2062799951-line-9)"> -</text><text class="terminal-2062799951-r1" x="0" y="264" textLength="768.6" clip-path="url(#terminal-2062799951-line-10)">  -c, --commitizen  get the version of the installed commitizen</text><text class="terminal-2062799951-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2062799951-line-10)"> -</text><text class="terminal-2062799951-r1" x="0" y="288.4" textLength="927.2" clip-path="url(#terminal-2062799951-line-11)">  -v, --verbose     get the version of both the installed commitizen and the</text><text class="terminal-2062799951-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2062799951-line-11)"> -</text><text class="terminal-2062799951-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-2062799951-line-12)">                    current project</text><text class="terminal-2062799951-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2062799951-line-12)"> -</text><text class="terminal-2062799951-r1" x="0" y="337.2" textLength="561.2" clip-path="url(#terminal-2062799951-line-13)">  --major           get just the major version</text><text class="terminal-2062799951-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2062799951-line-13)"> -</text><text class="terminal-2062799951-r1" x="0" y="361.6" textLength="561.2" clip-path="url(#terminal-2062799951-line-14)">  --minor           get just the minor version</text><text class="terminal-2062799951-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2062799951-line-14)"> -</text><text class="terminal-2062799951-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2062799951-line-15)"> + <g class="terminal-752487608-matrix"> + <text class="terminal-752487608-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-752487608-line-0)">$ cz version --help</text><text class="terminal-752487608-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-752487608-line-0)"> +</text><text class="terminal-752487608-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-752487608-line-1)">usage: cz version </text><text class="terminal-752487608-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-752487608-line-1)">-h</text><text class="terminal-752487608-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="292.8" y="44.4" textLength="207.4" clip-path="url(#terminal-752487608-line-1)">-r | -p | -c | -v</text><text class="terminal-752487608-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="536.8" y="44.4" textLength="207.4" clip-path="url(#terminal-752487608-line-1)">--major | --minor</text><text class="terminal-752487608-r2" x="744.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)"> +</text><text class="terminal-752487608-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-752487608-line-2)"> +</text><text class="terminal-752487608-r1" x="0" y="93.2" textLength="817.4" clip-path="url(#terminal-752487608-line-3)">get the version of the installed commitizen or the current project </text><text class="terminal-752487608-r2" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-752487608-line-3)">(</text><text class="terminal-752487608-r1" x="829.6" y="93.2" textLength="97.6" clip-path="url(#terminal-752487608-line-3)">default:</text><text class="terminal-752487608-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-752487608-line-3)"> +</text><text class="terminal-752487608-r1" x="0" y="117.6" textLength="244" clip-path="url(#terminal-752487608-line-4)">installed commitizen</text><text class="terminal-752487608-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-752487608-line-4)">)</text><text class="terminal-752487608-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-752487608-line-4)"> +</text><text class="terminal-752487608-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-752487608-line-5)"> +</text><text class="terminal-752487608-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-752487608-line-6)">options:</text><text class="terminal-752487608-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-752487608-line-6)"> +</text><text class="terminal-752487608-r1" x="0" y="190.8" textLength="622.2" clip-path="url(#terminal-752487608-line-7)">  -h, --help        show this help message and exit</text><text class="terminal-752487608-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-752487608-line-7)"> +</text><text class="terminal-752487608-r1" x="0" y="215.2" textLength="744.2" clip-path="url(#terminal-752487608-line-8)">  -r, --report      get system information for reporting bugs</text><text class="terminal-752487608-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-752487608-line-8)"> +</text><text class="terminal-752487608-r1" x="0" y="239.6" textLength="707.6" clip-path="url(#terminal-752487608-line-9)">  -p, --project     get the version of the current project</text><text class="terminal-752487608-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-752487608-line-9)"> +</text><text class="terminal-752487608-r1" x="0" y="264" textLength="768.6" clip-path="url(#terminal-752487608-line-10)">  -c, --commitizen  get the version of the installed commitizen</text><text class="terminal-752487608-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-752487608-line-10)"> +</text><text class="terminal-752487608-r1" x="0" y="288.4" textLength="927.2" clip-path="url(#terminal-752487608-line-11)">  -v, --verbose     get the version of both the installed commitizen and the</text><text class="terminal-752487608-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-752487608-line-11)"> +</text><text class="terminal-752487608-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-752487608-line-12)">                    current project</text><text class="terminal-752487608-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-752487608-line-12)"> +</text><text class="terminal-752487608-r1" x="0" y="337.2" textLength="951.6" clip-path="url(#terminal-752487608-line-13)">  --major           get just the major version. Need to be used with --project</text><text class="terminal-752487608-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-752487608-line-13)"> +</text><text class="terminal-752487608-r1" x="0" y="361.6" textLength="402.6" clip-path="url(#terminal-752487608-line-14)">                    or --verbose.</text><text class="terminal-752487608-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-752487608-line-14)"> +</text><text class="terminal-752487608-r1" x="0" y="386" textLength="951.6" clip-path="url(#terminal-752487608-line-15)">  --minor           get just the minor version. Need to be used with --project</text><text class="terminal-752487608-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-752487608-line-15)"> +</text><text class="terminal-752487608-r1" x="0" y="410.4" textLength="402.6" clip-path="url(#terminal-752487608-line-16)">                    or --verbose.</text><text class="terminal-752487608-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-752487608-line-16)"> +</text><text class="terminal-752487608-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-752487608-line-17)"> </text> </g> </g> From c2ae121f848c77df906d2208cd17c2c311d01118 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:24:40 +0000 Subject: [PATCH 271/275] =?UTF-8?q?bump:=20version=204.10.0=20=E2=86=92=20?= =?UTF-8?q?4.10.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf207ea60b..f70fbf2f10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.10.0 # automatically updated by Commitizen + rev: v4.10.1 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index f8bca478b3..7ee748085f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## v4.10.1 (2025-12-11) + +### Fix + +- **version**: fix the behavior of cz version --major +- **cli**: debug and no_raise can be used together in sys.excepthook +- **git**: replace lstrip with strip for compatibility issue +- **bump**: remove NotAllowed related to --get-next option, other related refactoring + +### Refactor + +- **version**: rename class member to align with other classes +- **cargo_provider**: cleanup and get rid of potential type errors +- **bump**: extract option validation and new version resolution to new functions +- **changelog**: raise NotAllow when file_name not passed instead of using assert +- **bump**: rename parameter and variables + +### Perf + +- **ruff**: enable ruff rules TC001~TC006 +- add TYPE_CHECKING to CzQuestion imports + ## v4.10.0 (2025-11-10) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 897e6be2ff..4e499ea83a 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.10.0" +__version__ = "4.10.1" diff --git a/pyproject.toml b/pyproject.toml index 7ea99cc69f..3e9c45ae63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.10.0" +version = "4.10.1" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -121,7 +121,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.10.0" +version = "4.10.1" tag_format = "v$version" version_files = [ "pyproject.toml:version", From 2b0b4d89e7d24efa211411ae733ddbfdbf64efa9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Fri, 12 Dec 2025 00:31:15 +0800 Subject: [PATCH 272/275] docs: add missing site_url --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index ac1fbfa097..fe42da177b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,5 @@ site_name: Commitizen +site_url: https://commitizen-tools.github.io/commitizen/ site_description: commit rules, semantic version, conventional commits theme: From 30bb513b993cec111abf255f666ee190f4041530 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Fri, 12 Dec 2025 00:22:15 +0800 Subject: [PATCH 273/275] build: add missing dark mode back --- mkdocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index fe42da177b..656e8dd423 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,9 +3,8 @@ site_url: https://commitizen-tools.github.io/commitizen/ site_description: commit rules, semantic version, conventional commits theme: - name: "material" + name: material palette: - - primary: 'deep purple' # Palette toggle for automatic mode - media: "(prefers-color-scheme)" toggle: @@ -15,6 +14,7 @@ theme: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" scheme: default + primary: deep purple toggle: icon: material/brightness-7 name: Switch to dark mode @@ -22,6 +22,7 @@ theme: # Palette toggle for dark mode - media: "(prefers-color-scheme: dark)" scheme: slate + primary: deep purple toggle: icon: material/brightness-4 name: Switch to system preference From 1e5869ec87c3886e1e2d313fd9d3d273db974b33 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Fri, 12 Dec 2025 00:42:12 +0800 Subject: [PATCH 274/275] style(mkdocs.yml): make indent consistent in nav section --- mkdocs.yml | 80 +++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 656e8dd423..9666a203db 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,53 +34,53 @@ edit_uri: "" nav: - Introduction: "README.md" - Commands: - - init: "commands/init.md" - - commit: "commands/commit.md" - - bump: "commands/bump.md" - - check: "commands/check.md" - - changelog: "commands/changelog.md" - - example: "commands/example.md" - - info: "commands/info.md" - - ls: "commands/ls.md" - - schema: "commands/schema.md" - - version: "commands/version.md" + - init: "commands/init.md" + - commit: "commands/commit.md" + - bump: "commands/bump.md" + - check: "commands/check.md" + - changelog: "commands/changelog.md" + - example: "commands/example.md" + - info: "commands/info.md" + - ls: "commands/ls.md" + - schema: "commands/schema.md" + - version: "commands/version.md" - Configuration: - - Configuration File: "config/configuration_file.md" - - Version Provider: "config/version_provider.md" - - bump: "config/bump.md" - - commit: "config/commit.md" - - check: "config/check.md" - - changelog: "config/changelog.md" - - Misc Options: "config/option.md" + - Configuration File: "config/configuration_file.md" + - Version Provider: "config/version_provider.md" + - bump: "config/bump.md" + - commit: "config/commit.md" + - check: "config/check.md" + - changelog: "config/changelog.md" + - Misc Options: "config/option.md" - Advanced Customization: - - Configuration File: "customization/config_file.md" - - Customized Python Class: "customization/python_class.md" - - Changelog Template: "customization/changelog_template.md" + - Configuration File: "customization/config_file.md" + - Customized Python Class: "customization/python_class.md" + - Changelog Template: "customization/changelog_template.md" - Tutorials: - - Writing commits: "tutorials/writing_commits.md" - - Managing tags formats: "tutorials/tag_format.md" - - Auto check commits: "tutorials/auto_check.md" - - Auto prepare commit message: "tutorials/auto_prepare_commit_message.md" - - GitLab CI: "tutorials/gitlab_ci.md" - - GitHub Actions: "tutorials/github_actions.md" - - Jenkins pipeline: "tutorials/jenkins_pipeline.md" - - Developmental releases: "tutorials/dev_releases.md" - - Monorepo support: "tutorials/monorepo_guidance.md" + - Writing commits: "tutorials/writing_commits.md" + - Managing tags formats: "tutorials/tag_format.md" + - Auto check commits: "tutorials/auto_check.md" + - Auto prepare commit message: "tutorials/auto_prepare_commit_message.md" + - GitLab CI: "tutorials/gitlab_ci.md" + - GitHub Actions: "tutorials/github_actions.md" + - Jenkins pipeline: "tutorials/jenkins_pipeline.md" + - Developmental releases: "tutorials/dev_releases.md" + - Monorepo support: "tutorials/monorepo_guidance.md" - FAQ: "faq.md" - Features we won't add: "features_wont_add.md" - Exit Codes: "exit_codes.md" - Third-Party Commitizen Plugins: - - About: "third-party-plugins/about.md" - # Please sort the plugins alphabetically - - "third-party-plugins/commitizen-deno-provider.md" - - "third-party-plugins/commitizen-emoji.md" - - "third-party-plugins/conventional-jira.md" - - "third-party-plugins/cz-ai.md" - - "third-party-plugins/cz-conventional-gitmoji.md" - - "third-party-plugins/cz-emoji.md" - - "third-party-plugins/cz-legacy.md" - - "third-party-plugins/cz-path.md" - - "third-party-plugins/github-jira-conventional.md" + - About: "third-party-plugins/about.md" + # Please sort the plugins alphabetically + - "third-party-plugins/commitizen-deno-provider.md" + - "third-party-plugins/commitizen-emoji.md" + - "third-party-plugins/conventional-jira.md" + - "third-party-plugins/cz-ai.md" + - "third-party-plugins/cz-conventional-gitmoji.md" + - "third-party-plugins/cz-emoji.md" + - "third-party-plugins/cz-legacy.md" + - "third-party-plugins/cz-path.md" + - "third-party-plugins/github-jira-conventional.md" - Contributing: "contributing.md" - Contributing TL;DR: "contributing_tldr.md" - Resources: "external_links.md" From c0da2e6824ce30273bc84ddccf82681e137fa9c4 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung <bear890707@gmail.com> Date: Fri, 12 Dec 2025 12:00:38 +0800 Subject: [PATCH 275/275] docs: fix some format issues in docs --- docs/config/bump.md | 22 +++++++++++----------- docs/config/version_provider.md | 8 ++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/config/bump.md b/docs/config/bump.md index fa46e18f8d..10ca5bcf8d 100644 --- a/docs/config/bump.md +++ b/docs/config/bump.md @@ -185,11 +185,11 @@ version_files = [ ```json title="setup.json" { - "name": "magictool", - "version": "1.2.3", - "dependencies": { - "lodash": "1.2.3" - } + "name": "magictool", + "version": "1.2.3", + "dependencies": { + "lodash": "1.2.3" + } } ``` @@ -197,12 +197,12 @@ version_files = [ ```diff title="setup.json" { - "name": "magictool", - - "version": "1.2.3", - + "version": "2.0.0", - "dependencies": { - "lodash": "1.2.3" - } + "name": "magictool", + - "version": "1.2.3", + + "version": "2.0.0", + "dependencies": { + "lodash": "1.2.3" + } } ``` diff --git a/docs/config/version_provider.md b/docs/config/version_provider.md index 4f070b2fd3..859f84f781 100644 --- a/docs/config/version_provider.md +++ b/docs/config/version_provider.md @@ -19,6 +19,7 @@ Commitizen includes several built-in version providers for common package manage The default version provider stores and retrieves the version from your Commitizen configuration file (e.g., `pyproject.toml`, `.cz.toml`, etc.). **Use when:** + - You want to keep version management separate from your package manager - Your project doesn't use a standard package manager - You need maximum flexibility in version management @@ -35,6 +36,7 @@ version = "0.1.0" # Required when using this provider Fetches the version from Git tags using `git describe`. This provider **only reads** version information and never writes it back to files. It's designed to work with tools like `setuptools-scm` or other package manager `*-scm` plugins that derive version numbers from Git history. **Use when:** + - You're using `setuptools-scm` or similar tools - You want version numbers derived from Git tags - You don't want Commitizen to modify any files for version management @@ -54,6 +56,7 @@ version_provider = "scm" Manages version in `pyproject.toml` under the `project.version` field, following [PEP 621](https://peps.python.org/pep-0621/) standards. **Use when:** + - You're using a modern Python project with PEP 621-compliant `pyproject.toml` - You want version management integrated with your Python project metadata @@ -75,6 +78,7 @@ version = "0.1.0" # Managed by Commitizen Manages version in `pyproject.toml` under the `tool.poetry.version` field, which is used by the [Poetry](https://python-poetry.org/) package manager. This approach is recommended only for users running Poetry versions earlier than 2.0 or relying on Poetry-specific features. For most users on Poetry 2.0 or later, it is recommended to use `pep621` instead. [Read More](https://python-poetry.org/docs/main/managing-dependencies/) **Use when:** + - You're using Poetry < 2.0 as your Python package manager - You're using Poetry >= 2.0 as your Python package manager, but don't need poetry-specific features - You want Commitizen to manage the version that Poetry uses @@ -101,6 +105,7 @@ Manages version in both `pyproject.toml` (`project.version`) and `uv.lock` (`pac Even though uv follows PEP 621 format, `pep621` does not manage the version in `uv.lock`. `uv` is still suggested for uv users. **Use when:** + - You're using `uv` as your Python package manager - You want version synchronization between `pyproject.toml` and `uv.lock` @@ -115,6 +120,7 @@ version_provider = "uv" Manages version in both `Cargo.toml` (`package.version`) and `Cargo.lock` (`package.version` for the matching package name). This ensures consistency between your Rust project's manifest and lock file. **Use when:** + - You're working with a Rust project using Cargo - You want Commitizen to manage Rust package versions @@ -136,6 +142,7 @@ version = "0.1.0" # Managed by Commitizen Manages version in `package.json` and optionally synchronizes with `package-lock.json` and `npm-shrinkwrap.json` if they exist. **Use when:** + - You're working with a Node.js/JavaScript project - You want Commitizen to manage npm package versions @@ -158,6 +165,7 @@ version_provider = "npm" Manages version in `composer.json` under the `version` field, used by PHP's Composer package manager. **Use when:** + - You're working with a PHP project using Composer - You want Commitizen to manage Composer package versions