Skip to content

Commit 67ba9f3

Browse files
committed
Implement disable comment for drf serializers
1 parent 5b25537 commit 67ba9f3

File tree

4 files changed

+69
-3
lines changed

4 files changed

+69
-3
lines changed

src/extra_checks/check_id.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,10 @@ def find_check(cls, value: Any) -> Optional["CheckId"]:
4545
"no-index-together",
4646
)
4747
)
48+
DRF_META_CHECKS_NAMES = frozenset(
49+
(
50+
"drf-model-serializer-extra-kwargs",
51+
"drf-model-serializer-meta-attribute",
52+
)
53+
)
4854
ALL_CHECKS_NAMES = frozenset(CheckId._value2member_map_.keys()) # type: ignore

src/extra_checks/checks/drf_serializer_checks.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import site
33
from abc import abstractmethod
44
from typing import (
5+
TYPE_CHECKING,
56
Any,
67
Iterable,
78
Iterator,
@@ -17,11 +18,46 @@
1718
import django.core.checks
1819
from rest_framework.serializers import ModelSerializer, Serializer
1920

20-
from .. import CheckId
21+
from ..ast.protocols import DisableCommentProtocol
22+
from ..ast.source_provider import SourceProvider
23+
from ..check_id import DRF_META_CHECKS_NAMES, CheckId
2124
from ..forms import AttrsForm
2225
from ..registry import ChecksConfig, registry
2326
from .base_checks import BaseCheck
2427

28+
if TYPE_CHECKING:
29+
cached_property = property
30+
else:
31+
from django.utils.functional import cached_property
32+
33+
34+
class DisableCommentProvider(DisableCommentProtocol):
35+
def __init__(self, serializer_class: Type[Serializer]):
36+
self.serializer_class = serializer_class
37+
38+
@cached_property
39+
def _source_provider(self) -> SourceProvider:
40+
return SourceProvider(self.serializer_class)
41+
42+
def is_disabled_by_comment(self, check_id: str) -> bool:
43+
check = CheckId.find_check(check_id)
44+
if check in DRF_META_CHECKS_NAMES:
45+
lines = (self._source_provider.source or "").splitlines()
46+
# find line starting with `class Meta` and lowest indent
47+
try:
48+
lineno, _ = sorted(
49+
[
50+
(i, line)
51+
for i, line in enumerate(lines, 1)
52+
if line.strip().startswith(("class Meta(", "class Meta:"))
53+
],
54+
key=lambda a: a[1].find("class Meta"),
55+
)[0]
56+
except StopIteration:
57+
return False
58+
return check in self._source_provider.get_disabled_checks_for_line(lineno)
59+
return check in self._source_provider.get_disabled_checks_for_line(1)
60+
2561

2662
def _collect_serializers(
2763
serializers: Iterable[Type[Serializer]],
@@ -90,10 +126,10 @@ def check_drf_serializers(
90126
s_classes, m_classes = _get_serializers_to_check(config.include_apps)
91127
for s in s_classes:
92128
for check in serializer_checks:
93-
yield from check(s, None)
129+
yield from check(s, DisableCommentProvider(s))
94130
for s in m_classes:
95131
for check in model_serializer_checks:
96-
yield from check(s, None)
132+
yield from check(s, DisableCommentProvider(s))
97133

98134

99135
class CheckDRFSerializer(BaseCheck):

tests/example/serializers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ class ModernAuthorSerializer(AuthorSerializer):
2323
class Meta:
2424
model = Author
2525
extra_kwargs = {"first_name": {"read_only": True}}
26+
27+
28+
class DisableCheckSerializer(serializers.ModelSerializer):
29+
first_name = serializers.CharField()
30+
31+
def method_with_meta_class(self):
32+
class Meta:
33+
...
34+
35+
# extra-checks-disable-next-line drf-model-serializer-extra-kwargs
36+
class Meta:
37+
model = Author
38+
extra_kwargs = {"first_name": {"read_only": True}}

tests/test_drf_serializer_checks.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from tests.example.serializers import (
1616
ArticleSerializer,
1717
AuthorSerializer,
18+
DisableCheckSerializer,
1819
ModernAuthorSerializer,
1920
)
2021

@@ -88,3 +89,13 @@ def test_get_serializers_to_check_include_apps(settings):
8889
# include_apps so it's included too
8990
assert len(ss) == 1
9091
assert ss[0] is rest_framework.authtoken.serializers.AuthTokenSerializer
92+
93+
94+
def test_drf_ignore_meta_checks(test_case):
95+
messages = (
96+
test_case.settings({"checks": [CheckDRFSerializerExtraKwargs.Id.value]})
97+
.check(CheckDRFSerializerExtraKwargs)
98+
.serializers(DisableCheckSerializer)
99+
.run()
100+
)
101+
assert not messages

0 commit comments

Comments
 (0)