Skip to content

Commit f83118a

Browse files
committed
Update
1 parent eda396f commit f83118a

2 files changed

Lines changed: 23 additions & 11 deletions

File tree

src/community_of_python_flake8_plugin/checks/final_class.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import ast
33
import typing
44

5+
from community_of_python_flake8_plugin.constants import FINAL_CLASS_EXCLUDED_BASES
6+
from community_of_python_flake8_plugin.utils import check_inherits_from_bases
57
from community_of_python_flake8_plugin.violation_codes import ViolationCodes
68
from community_of_python_flake8_plugin.violations import Violation
79

@@ -28,6 +30,12 @@ def is_protocol_class(class_node: ast.ClassDef) -> bool:
2830
return False
2931

3032

33+
def is_model_factory_class(class_node: ast.ClassDef) -> bool:
34+
"""Check if the class inherits from ModelFactory or SQLAlchemyFactory."""
35+
model_factory_bases: typing.Final = {"ModelFactory", "SQLAlchemyFactory"}
36+
return check_inherits_from_bases(class_node, model_factory_bases)
37+
38+
3139
@typing.final
3240
class FinalClassCheck(ast.NodeVisitor):
3341
def __init__(self, syntax_tree: ast.AST) -> None: # noqa: ARG002
@@ -38,8 +46,12 @@ def visit_ClassDef(self, ast_node: ast.ClassDef) -> None:
3846
self.generic_visit(ast_node)
3947

4048
def _check_final_decorator(self, ast_node: ast.ClassDef) -> None:
41-
# Skip Protocol classes and test classes
42-
if is_protocol_class(ast_node) or ast_node.name.startswith("Test"):
49+
# Skip Protocol classes, test classes, and ModelFactory classes
50+
if (
51+
is_protocol_class(ast_node)
52+
or ast_node.name.startswith("Test")
53+
or is_model_factory_class(ast_node)
54+
):
4355
return
4456

4557
if not contains_final_decorator(ast_node):

tests/test_plugin.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,25 +195,25 @@ def test_type_annotation_validations(input_source: str, expected_output: list[st
195195
(
196196
"from polyfactory.factories.pydantic_factory import ModelFactory\n"
197197
"class MyFactory(ModelFactory):\n def calculator(self): pass",
198-
["COP012"],
198+
[],
199199
),
200200
# No violation: ModelFactory generic methods should be exempt from COP009
201201
(
202202
"from polyfactory.factories.pydantic_factory import ModelFactory\nimport some_module\n"
203203
"class MyFactory(ModelFactory[some_module.SomeClass]):\n def calculator(self): pass",
204-
["COP012"],
204+
[],
205205
),
206206
# No violation: ModelFactory classmethod should be exempt from COP009
207207
(
208208
"from polyfactory.factories.pydantic_factory import ModelFactory\n"
209209
"class MyFactory(ModelFactory):\n @classmethod\n def create(cls): pass",
210-
["COP012"],
210+
[],
211211
),
212212
# No violation: ModelFactory generic classmethod should be exempt from COP009
213213
(
214214
"from polyfactory.factories.pydantic_factory import ModelFactory\nimport some_module\n"
215215
"class MyFactory(ModelFactory[some_module.SomeClass]):\n @classmethod\n def create(cls): pass",
216-
["COP012"],
216+
[],
217217
),
218218
# No violation: cached_property imported directly should exempt function from COP009
219219
# (but triggers COP002 for import style)
@@ -362,10 +362,10 @@ def test_variable_usage_validations(input_source: str, expected_output: list[str
362362
("import pydantic\nclass MyBaseModel(pydantic.BaseModel): ...", ["COP012"]),
363363
# COP012: Classes inheriting from RootModel now require final decorator
364364
("from pydantic import RootModel\nclass MyRootModel(RootModel): ...", ["COP012"]),
365-
# COP012: Classes inheriting from ModelFactory now require final decorator
365+
# No violation: Classes inheriting from ModelFactory are exempt from final decorator requirement
366366
(
367367
"from polyfactory.factories.pydantic_factory import ModelFactory\nclass MyModelFactory(ModelFactory): ...",
368-
["COP012"],
368+
[],
369369
),
370370
# COP012: Exception classes now require final decorator
371371
(
@@ -377,13 +377,13 @@ def test_variable_usage_validations(input_source: str, expected_output: list[str
377377
"import dataclasses\n\n@dataclasses.dataclass\nclass ExampleChild(Example):\n value: int\n",
378378
["COP012", "COP014"],
379379
),
380-
# COP012: Classes inheriting from ModelFactory now require final decorator (with methods)
380+
# No violation: Classes inheriting from ModelFactory are exempt from final decorator requirement (with methods)
381381
(
382382
"from polyfactory.factories.pydantic_factory import ModelFactory\n"
383383
"class MyModelFactory(ModelFactory):\n"
384384
" def fn():\n"
385385
" pass",
386-
["COP012"],
386+
[],
387387
),
388388
],
389389
)
@@ -465,7 +465,7 @@ def test_module_level_validations(input_source: str, expected_output: list[str])
465465
"import polyfactory.factories.pydantic_factory\n"
466466
"class MyFactoryClass(polyfactory.factories.pydantic_factory.ModelFactory):\n"
467467
" pass",
468-
["COP012"],
468+
[],
469469
),
470470
# COP014: Dataclass with init=False still needs slots and frozen
471471
(

0 commit comments

Comments
 (0)