From efd154989ae7de035fdd1cd63b9c5128bfe34801 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Mon, 19 Jan 2026 22:59:57 +0800 Subject: [PATCH] fix(message_length_limit): align the behavior of message_length_limit The document says 0 is no limit --- commitizen/commands/check.py | 4 +- commitizen/commands/commit.py | 22 ++++++---- commitizen/cz/base.py | 2 +- commitizen/defaults.py | 4 +- tests/commands/test_check_command.py | 59 +-------------------------- tests/commands/test_commit_command.py | 2 +- tests/test_conf.py | 4 +- 7 files changed, 23 insertions(+), 74 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 8ec5b47f8..182839910 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -20,7 +20,7 @@ class CheckArgs(TypedDict, total=False): commit_msg: str rev_range: str allow_abort: bool - message_length_limit: int | None + message_length_limit: int allowed_prefixes: list[str] message: str use_default_range: bool @@ -46,7 +46,7 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N self.use_default_range = bool(arguments.get("use_default_range")) self.max_msg_length = arguments.get( - "message_length_limit", config.settings.get("message_length_limit", None) + "message_length_limit", config.settings.get("message_length_limit", 0) ) # we need to distinguish between None and [], which is a valid value diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 3894d0b77..5776af420 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -36,7 +36,7 @@ class CommitArgs(TypedDict, total=False): dry_run: bool edit: bool extra_cli_args: str - message_length_limit: int | None + message_length_limit: int no_retry: bool signoff: bool write_message_to_file: Path | None @@ -83,19 +83,23 @@ def _get_message_by_prompt_commit_questions(self) -> str: raise NoAnswersError() message = self.cz.message(answers) - 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) - + self._validate_subject_length(message) return message - def _validate_subject_length(self, *, message: str, length_limit: int) -> None: + def _validate_subject_length(self, message: str) -> None: + message_length_limit = self.arguments.get( + "message_length_limit", self.config.settings.get("message_length_limit", 0) + ) # By the contract, message_length_limit is set to 0 for no limit + if ( + message_length_limit is None or message_length_limit <= 0 + ): # do nothing for no limit + return + subject = message.partition("\n")[0].strip() - if len(subject) > length_limit: + if len(subject) > message_length_limit: raise CommitMessageLengthExceededError( - f"Length of commit message exceeds limit ({len(subject)}/{length_limit}), subject: '{subject}'" + f"Length of commit message exceeds limit ({len(subject)}/{message_length_limit}), subject: '{subject}'" ) def manual_edit(self, message: str) -> str: diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 90633c42e..5e7f2663c 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -130,7 +130,7 @@ def validate_commit_message( if any(map(commit_msg.startswith, allowed_prefixes)): return ValidationResult(True, []) - if max_msg_length is not None: + if max_msg_length is not None and max_msg_length > 0: msg_len = len(commit_msg.partition("\n")[0].strip()) if msg_len > max_msg_length: # TODO: capitalize the first letter of the error message for consistency in v5 diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 6de41f63d..4865ccc18 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -48,7 +48,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 + message_length_limit: int name: str post_bump_hooks: list[str] | None pre_bump_hooks: list[str] | None @@ -114,7 +114,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 + "message_length_limit": 0, # 0 for no limit } MAJOR = "MAJOR" diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index b5e3fd2b0..f225e912e 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -7,7 +7,7 @@ from commitizen import commands, git from commitizen.cz import registry -from commitizen.cz.base import BaseCommitizen, ValidationResult +from commitizen.cz.base import BaseCommitizen from commitizen.exceptions import ( CommitMessageLengthExceededError, InvalidCommandArgumentError, @@ -16,7 +16,6 @@ ) if TYPE_CHECKING: - import re from collections.abc import Mapping from pytest_mock import MockFixture, MockType @@ -385,7 +384,7 @@ def test_check_command_cli_overrides_config_message_length_limit( ): message = "fix(scope): some commit message" config.settings["message_length_limit"] = len(message) - 1 - for message_length_limit in [len(message) + 1, None]: + for message_length_limit in [len(message) + 1, 0]: success_mock.reset_mock() commands.Check( config=config, @@ -419,60 +418,6 @@ def example(self) -> str: def info(self) -> str: return "Commit message must start with an issue number like ABC-123" - def validate_commit_message( - self, - *, - commit_msg: str, - pattern: re.Pattern[str], - allow_abort: bool, - allowed_prefixes: list[str], - max_msg_length: int | None, - commit_hash: str, - ) -> ValidationResult: - """Validate commit message against the pattern.""" - if not commit_msg: - return ValidationResult( - allow_abort, [] if allow_abort else ["commit message is empty"] - ) - - if any(map(commit_msg.startswith, allowed_prefixes)): - return ValidationResult(True, []) - - if max_msg_length: - msg_len = len(commit_msg.partition("\n")[0].strip()) - if msg_len > max_msg_length: - # TODO: capitalize the first letter of the error message for consistency in v5 - 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: {max_msg_length} (actual: {msg_len})" - ) - - return ValidationResult( - bool(pattern.match(commit_msg)), [f"pattern: {pattern.pattern}"] - ) - - def format_exception_message( - self, invalid_commits: list[tuple[git.GitCommit, list]] - ) -> str: - """Format commit errors.""" - displayed_msgs_content = "\n".join( - [ - ( - f'commit "{commit.rev}": "{commit.message}"\nerrors:\n\n'.join( - f"- {error}" for error in errors - ) - ) - for (commit, errors) in invalid_commits - ] - ) - return ( - "commit validation: failed!\n" - "please enter a commit message in the commitizen format.\n" - f"{displayed_msgs_content}" - ) - @pytest.fixture def use_cz_custom_validator(mocker): diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 87c7aca42..89a9224b8 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -363,5 +363,5 @@ def test_commit_command_with_config_message_length_limit( success_mock.assert_called_once() success_mock.reset_mock() - commands.Commit(config, {"message_length_limit": None})() + commands.Commit(config, {"message_length_limit": 0})() success_mock.assert_called_once() diff --git a/tests/test_conf.py b/tests/test_conf.py index 6e4256f16..ee5eba5b3 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -106,7 +106,7 @@ "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, - "message_length_limit": None, + "message_length_limit": 0, } _new_settings: dict[str, Any] = { @@ -146,7 +146,7 @@ "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, - "message_length_limit": None, + "message_length_limit": 0, }