summaryrefslogtreecommitdiff
path: root/pylint/checkers/design_analysis.py
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2019-07-06 18:38:19 +0300
committerClaudiu Popa <pcmanticore@gmail.com>2019-07-06 18:38:19 +0300
commit02297960ac482c66c89b208b331f863ae8a2d09e (patch)
tree26cb019d265475356cd720560aa722d9290970d8 /pylint/checkers/design_analysis.py
parent0eeb67749b6aa016db61d87e55a1f7a7e459b352 (diff)
downloadpylint-git-02297960ac482c66c89b208b331f863ae8a2d09e.tar.gz
Excluded `attrs` from `too-few-public-methods` check. Close #2988.
Diffstat (limited to 'pylint/checkers/design_analysis.py')
-rw-r--r--pylint/checkers/design_analysis.py21
1 files changed, 14 insertions, 7 deletions
diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py
index b6fd9913c..62a718098 100644
--- a/pylint/checkers/design_analysis.py
+++ b/pylint/checkers/design_analysis.py
@@ -88,7 +88,7 @@ MSGS = {
),
}
SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
-DATACLASS_DECORATOR = "dataclass"
+DATACLASSES_DECORATORS = frozenset({"dataclass", "attrs"})
DATACLASS_IMPORT = "dataclasses"
TYPING_NAMEDTUPLE = "typing.NamedTuple"
@@ -126,19 +126,24 @@ def _is_enum_class(node: astroid.ClassDef) -> bool:
return False
-def _is_dataclass(node: astroid.ClassDef) -> bool:
- """Check if a class definition defines a Python 3.7+ dataclass
+def _is_dataclass_like(node: astroid.ClassDef) -> bool:
+ """Check if a class definition defines a Python data class
+
+ A list of decorator names are introspected, such as the builtin
+ `dataclass` decorator, as well as the popular `attrs` one from
+ the `attrs` library.
:param node: The class node to check.
:type node: astroid.ClassDef
- :returns: True if the given node represents a dataclass class. False otherwise.
+ :returns:
+ `True` if the given node represents a dataclass class, `False` otherwise.
:rtype: bool
"""
if not node.decorators:
return False
- root_locals = node.root().locals
+ root_locals = set(node.root().locals)
for decorator in node.decorators.nodes:
if isinstance(decorator, astroid.Call):
decorator = decorator.func
@@ -148,7 +153,9 @@ def _is_dataclass(node: astroid.ClassDef) -> bool:
name = decorator.name
else:
name = decorator.attrname
- if name == DATACLASS_DECORATOR and DATACLASS_DECORATOR in root_locals:
+ if name in DATACLASSES_DECORATORS and root_locals.intersection(
+ DATACLASSES_DECORATORS
+ ):
return True
return False
@@ -361,7 +368,7 @@ class MisdesignChecker(BaseChecker):
if (
node.type != "class"
or _is_enum_class(node)
- or _is_dataclass(node)
+ or _is_dataclass_like(node)
or _is_typing_namedtuple(node)
):
return