Coverage for src / python_commitlint / rules / type_rules.py: 100%
71 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-04-28 02:54 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-04-28 02:54 +0000
1"""Rules that validate the commit ``type`` (the token before the colon)."""
3from python_commitlint.core.enums import CaseType, RuleCondition
4from python_commitlint.core.exceptions import ConfigurationError
5from python_commitlint.core.models import (
6 CommitMessage,
7 RuleConfig,
8 ValidationError,
9)
10from python_commitlint.rules.base import BaseRule, config_value_or
11from python_commitlint.rules.case_validators import CaseValidator
14class TypeEmptyRule(BaseRule):
15 """Require or forbid an empty type. Rule name: ``type-empty``."""
17 @property
18 def name(self) -> str:
19 return "type-empty"
21 def validate(
22 self, commit: CommitMessage, config: RuleConfig
23 ) -> ValidationError | None:
24 is_empty = not commit.type
25 should_be_empty = config.condition == RuleCondition.ALWAYS
27 if is_empty != should_be_empty:
28 msg = (
29 "type may not be empty"
30 if not should_be_empty
31 else "type must be empty"
32 )
33 return self._create_error(config, msg)
34 return None
37class TypeCaseRule(BaseRule):
38 """Enforce a single case style on the type. Rule name: ``type-case``."""
40 @property
41 def name(self) -> str:
42 return "type-case"
44 def validate(
45 self, commit: CommitMessage, config: RuleConfig
46 ) -> ValidationError | None:
47 if not commit.type or config.value is None:
48 return None
50 case_type = CaseType(config.value)
51 is_valid = CaseValidator.validate(commit.type, case_type)
52 should_match = config.condition == RuleCondition.ALWAYS
54 if is_valid != should_match:
55 return self._create_error(config, f"type must be {config.value}")
56 return None
59class TypeEnumRule(BaseRule):
60 """Restrict the type to (or away from) an allowed list. Rule name: ``type-enum``."""
62 @property
63 def name(self) -> str:
64 return "type-enum"
66 def validate(
67 self, commit: CommitMessage, config: RuleConfig
68 ) -> ValidationError | None:
69 if not commit.type:
70 return None
72 if config.value is None:
73 raise ConfigurationError(
74 f"{self.name}: requires a 'value' (list of allowed types)"
75 )
76 allowed_types = config.value
77 is_in_enum = commit.type in allowed_types
78 should_be_in_enum = config.condition == RuleCondition.ALWAYS
80 if is_in_enum != should_be_in_enum:
81 msg = (
82 f"type must be one of {allowed_types}"
83 if should_be_in_enum
84 else f"type must not be one of {allowed_types}"
85 )
86 return self._create_error(config, msg)
87 return None
90class TypeMinLengthRule(BaseRule):
91 """Enforce a minimum character length on the type. Rule name: ``type-min-length``."""
93 @property
94 def name(self) -> str:
95 return "type-min-length"
97 def validate(
98 self, commit: CommitMessage, config: RuleConfig
99 ) -> ValidationError | None:
100 if not commit.type:
101 return None
103 min_length = config_value_or(config, 0)
104 is_valid = len(commit.type) >= min_length
105 should_be_valid = config.condition == RuleCondition.ALWAYS
107 if is_valid != should_be_valid:
108 return self._create_error(
109 config, f"type must be at least {min_length} characters"
110 )
111 return None
114class TypeMaxLengthRule(BaseRule):
115 """Enforce a maximum character length on the type. Rule name: ``type-max-length``."""
117 @property
118 def name(self) -> str:
119 return "type-max-length"
121 def validate(
122 self, commit: CommitMessage, config: RuleConfig
123 ) -> ValidationError | None:
124 if not commit.type:
125 return None
127 max_length = config_value_or(config, float("inf"))
128 is_valid = len(commit.type) <= max_length
129 should_be_valid = config.condition == RuleCondition.ALWAYS
131 if is_valid != should_be_valid:
132 return self._create_error(
133 config, f"type must be at most {max_length} characters"
134 )
135 return None