Coverage for src / python_commitlint / rules / registry.py: 93%

27 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-04-28 02:54 +0000

1"""Registry that holds rules by name and a factory for the default rule set.""" 

2 

3from python_commitlint.core.protocols import RuleProtocol 

4from python_commitlint.rules.body_rules import ( 

5 BodyCaseRule, 

6 BodyEmptyRule, 

7 BodyFullStopRule, 

8 BodyLeadingBlankRule, 

9 BodyMaxLengthRule, 

10 BodyMaxLineLengthRule, 

11 BodyMinLengthRule, 

12) 

13from python_commitlint.rules.footer_rules import ( 

14 FooterEmptyRule, 

15 FooterLeadingBlankRule, 

16 FooterMaxLengthRule, 

17 FooterMaxLineLengthRule, 

18 FooterMinLengthRule, 

19) 

20from python_commitlint.rules.header_rules import ( 

21 HeaderCaseRule, 

22 HeaderFullStopRule, 

23 HeaderMaxLengthRule, 

24 HeaderMinLengthRule, 

25 HeaderTrimRule, 

26) 

27from python_commitlint.rules.scope_rules import ( 

28 ScopeCaseRule, 

29 ScopeEmptyRule, 

30 ScopeEnumRule, 

31 ScopeMaxLengthRule, 

32 ScopeMinLengthRule, 

33) 

34from python_commitlint.rules.subject_rules import ( 

35 SubjectCaseRule, 

36 SubjectEmptyRule, 

37 SubjectFullStopRule, 

38 SubjectMaxLengthRule, 

39 SubjectMinLengthRule, 

40) 

41from python_commitlint.rules.type_rules import ( 

42 TypeCaseRule, 

43 TypeEmptyRule, 

44 TypeEnumRule, 

45 TypeMaxLengthRule, 

46 TypeMinLengthRule, 

47) 

48 

49 

50class RuleRegistry: 

51 """Maps rule names to :class:`RuleProtocol` implementations. 

52 

53 The linter consults the registry to resolve a configuration entry's 

54 rule name to the rule object that knows how to validate it. Unknown 

55 rule names are silently skipped by the linter, so registries can be 

56 populated with any subset of the built-ins or with user-defined rules. 

57 """ 

58 

59 def __init__(self) -> None: 

60 """Create an empty registry.""" 

61 self._rules: dict[str, RuleProtocol] = {} 

62 

63 def register(self, rule: RuleProtocol) -> None: 

64 """Register ``rule`` under its own ``name``. 

65 

66 Args: 

67 rule: A rule instance whose ``name`` will be used as the key. 

68 Re-registering an existing name overwrites the prior entry. 

69 """ 

70 self._rules[rule.name] = rule 

71 

72 def get(self, name: str) -> RuleProtocol | None: 

73 """Return the rule registered under ``name`` or ``None``.""" 

74 return self._rules.get(name) 

75 

76 def get_all(self) -> dict[str, RuleProtocol]: 

77 """Return a shallow copy of the registry contents.""" 

78 return self._rules.copy() 

79 

80 

81class RuleRegistryFactory: 

82 """Constructs :class:`RuleRegistry` instances.""" 

83 

84 @staticmethod 

85 def create() -> RuleRegistry: 

86 """Return an empty :class:`RuleRegistry`.""" 

87 return RuleRegistry() 

88 

89 @staticmethod 

90 def create_with_default_rules() -> RuleRegistry: 

91 """Return a registry populated with every built-in rule.""" 

92 registry = RuleRegistry() 

93 

94 rules: list[RuleProtocol] = [ 

95 TypeEmptyRule(), 

96 TypeCaseRule(), 

97 TypeEnumRule(), 

98 TypeMinLengthRule(), 

99 TypeMaxLengthRule(), 

100 SubjectEmptyRule(), 

101 SubjectCaseRule(), 

102 SubjectFullStopRule(), 

103 SubjectMinLengthRule(), 

104 SubjectMaxLengthRule(), 

105 ScopeEmptyRule(), 

106 ScopeCaseRule(), 

107 ScopeEnumRule(), 

108 ScopeMinLengthRule(), 

109 ScopeMaxLengthRule(), 

110 HeaderMaxLengthRule(), 

111 HeaderMinLengthRule(), 

112 HeaderTrimRule(), 

113 HeaderFullStopRule(), 

114 HeaderCaseRule(), 

115 BodyEmptyRule(), 

116 BodyLeadingBlankRule(), 

117 BodyMaxLengthRule(), 

118 BodyMaxLineLengthRule(), 

119 BodyMinLengthRule(), 

120 BodyFullStopRule(), 

121 BodyCaseRule(), 

122 FooterEmptyRule(), 

123 FooterLeadingBlankRule(), 

124 FooterMaxLengthRule(), 

125 FooterMaxLineLengthRule(), 

126 FooterMinLengthRule(), 

127 ] 

128 

129 for rule in rules: 

130 registry.register(rule) 

131 

132 return registry