From a5e3cc6e11148f4978a4771cb9ed46137ee61b0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Jun 2025 14:18:53 +0000 Subject: [PATCH 001/143] 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 002/143] 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 003/143] 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 004/143] 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 005/143] 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 006/143] 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 007/143] 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 008/143] 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 009/143] 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 010/143] 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 011/143] 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 012/143] 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 013/143] 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 014/143] 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 015/143] 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 016/143] 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 017/143] 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 018/143] 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 019/143] 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 020/143] 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 021/143] 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 022/143] 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 023/143] =?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 024/143] 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 025/143] 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 026/143] 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 027/143] 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 028/143] 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 029/143] 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 030/143] 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 031/143] 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 032/143] 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 033/143] 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 034/143] 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 035/143] 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 036/143] 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 037/143] 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 038/143] 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 039/143] 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 040/143] 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 041/143] 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 042/143] 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 043/143] 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 044/143] 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 045/143] 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 046/143] 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 047/143] 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 048/143] 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 049/143] 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 050/143] 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 051/143] 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 052/143] 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 053/143] 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 054/143] 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 055/143] 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 056/143] 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 057/143] 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 058/143] 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 059/143] 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 060/143] =?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 061/143] 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 062/143] 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 063/143] =?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 064/143] 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 065/143] 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 066/143] 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 067/143] 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 068/143] 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 069/143] 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 070/143] 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 071/143] 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 072/143] 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 073/143] 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 074/143] 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 075/143] 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 076/143] 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 077/143] 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 078/143] 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 079/143] 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 080/143] 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 081/143] 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 082/143] 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 083/143] 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 084/143] 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 085/143] 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 086/143] 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 087/143] 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 088/143] 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 089/143] 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 090/143] 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 091/143] 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 092/143] 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 093/143] 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 094/143] 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 095/143] 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 096/143] 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 097/143] 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 098/143] 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 099/143] 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 100/143] =?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 101/143] 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 102/143] 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 103/143] 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 104/143] 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 105/143] 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 106/143] 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 107/143] 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 108/143] 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 109/143] 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 110/143] 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 111/143] 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 112/143] 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 113/143] 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 114/143] 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 115/143] 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 116/143] 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 117/143] 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 118/143] 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 119/143] 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 120/143] 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 121/143] 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 122/143] 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 123/143] 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 124/143] 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 125/143] 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 126/143] 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 127/143] 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 128/143] 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 129/143] 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 130/143] 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 131/143] 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 132/143] 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 133/143] 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 134/143] 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 135/143] 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 136/143] 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 137/143] 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 138/143] 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 139/143] =?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 140/143] 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 141/143] 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 142/143] 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 143/143] 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