33import typing
44
55from community_of_python_flake8_plugin .constants import FINAL_CLASS_EXCLUDED_BASES
6+ from community_of_python_flake8_plugin .utils import check_inherits_from_bases
67from community_of_python_flake8_plugin .violation_codes import ViolationCodes
78from community_of_python_flake8_plugin .violations import Violation
89
@@ -23,33 +24,23 @@ def has_required_dataclass_params(decorator: ast.expr) -> bool:
2324 if not isinstance (decorator , ast .Call ):
2425 return False
2526
26- # Check if all required parameters are present
27+ keywords : typing .Final = {kw .arg : kw .value for kw in decorator .keywords if isinstance (kw .value , ast .Constant )}
28+ kw_only_param : typing .Final = keywords .get ("kw_only" )
29+ slots_param : typing .Final = keywords .get ("slots" )
30+ frozen_param : typing .Final = keywords .get ("frozen" )
2731 return (
28- any (
29- keyword .arg == "kw_only" and isinstance (keyword .value , ast .Constant ) and keyword .value .value is True
30- for keyword in decorator .keywords
31- )
32- and any (
33- keyword .arg == "slots" and isinstance (keyword .value , ast .Constant ) and keyword .value .value is True
34- for keyword in decorator .keywords
35- )
36- and any (
37- keyword .arg == "frozen" and isinstance (keyword .value , ast .Constant ) and keyword .value .value is True
38- for keyword in decorator .keywords
39- )
32+ kw_only_param is not None
33+ and isinstance (kw_only_param , ast .Constant )
34+ and kw_only_param .value is True
35+ and slots_param is not None
36+ and isinstance (slots_param , ast .Constant )
37+ and slots_param .value is True
38+ and frozen_param is not None
39+ and isinstance (frozen_param , ast .Constant )
40+ and frozen_param .value is True
4041 )
4142
4243
43- def is_inherited_from_whitelisted_class (class_node : ast .ClassDef ) -> bool :
44- """Check if class inherits from whitelisted base classes."""
45- for base_class in class_node .bases :
46- if isinstance (base_class , ast .Name ) and base_class .id in FINAL_CLASS_EXCLUDED_BASES :
47- return True
48- if isinstance (base_class , ast .Attribute ) and base_class .attr in FINAL_CLASS_EXCLUDED_BASES :
49- return True
50- return False
51-
52-
5344def is_pydantic_model (class_node : ast .ClassDef ) -> bool :
5445 """Check if class inherits from Pydantic BaseModel or RootModel."""
5546 for base_class in class_node .bases :
@@ -78,7 +69,7 @@ def __init__(self, syntax_tree: ast.AST) -> None: # noqa: ARG002
7869 def visit_ClassDef (self , ast_node : ast .ClassDef ) -> None :
7970 # Skip whitelisted classes and classes that inherit from Exception or other special classes
8071 if (
81- is_inherited_from_whitelisted_class (ast_node )
72+ check_inherits_from_bases (ast_node , FINAL_CLASS_EXCLUDED_BASES )
8273 or is_pydantic_model (ast_node )
8374 or is_model_factory (ast_node )
8475 or self ._inherits_from_exception (ast_node )
@@ -104,8 +95,8 @@ def visit_ClassDef(self, ast_node: ast.ClassDef) -> None:
10495 def _inherits_from_exception (self , ast_node : ast .ClassDef ) -> bool :
10596 """Check if class inherits from Exception or its subclasses."""
10697 for base in ast_node .bases :
107- if (isinstance (base , ast .Name ) and ("Error" in base .id or "Exception" in base .id )) or (
108- isinstance (base , ast .Attribute ) and ("Error" in base .attr or "Exception" in base .attr )
109- ):
98+ if isinstance (base , ast .Name ) and ("Error" in base .id or "Exception" in base .id ):
11099 return True
111- return len (ast_node .bases ) > 0 # Skip all classes that inherit from anything
100+ if isinstance (base , ast .Attribute ) and ("Error" in base .attr or "Exception" in base .attr ):
101+ return True
102+ return False
0 commit comments