Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/brain/brain_attrs.py: 29%
35 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""
6Astroid hook for the attrs library
8Without this hook pylint reports unsupported-assignment-operation
9for attrs classes
10"""
11from astroid.helpers import safe_infer
12from astroid.manager import AstroidManager
13from astroid.nodes.node_classes import AnnAssign, Assign, AssignName, Call, Unknown
14from astroid.nodes.scoped_nodes import ClassDef
16ATTRIB_NAMES = frozenset(
17 (
18 "attr.Factory",
19 "attr.ib",
20 "attrib",
21 "attr.attrib",
22 "attr.field",
23 "attrs.field",
24 "field",
25 )
26)
27ATTRS_NAMES = frozenset(
28 (
29 "attr.s",
30 "attrs",
31 "attr.attrs",
32 "attr.attributes",
33 "attr.define",
34 "attr.mutable",
35 "attr.frozen",
36 "attrs.define",
37 "attrs.mutable",
38 "attrs.frozen",
39 )
40)
43def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
44 """Return whether a decorated node has an attr decorator applied."""
45 if not node.decorators:
46 return False
47 for decorator_attribute in node.decorators.nodes:
48 if isinstance(decorator_attribute, Call): # decorator with arguments
49 decorator_attribute = decorator_attribute.func
50 if decorator_attribute.as_string() in decorator_names:
51 return True
53 inferred = safe_infer(decorator_attribute)
54 if inferred and inferred.root().name == "attr._next_gen":
55 return True
56 return False
59def attr_attributes_transform(node: ClassDef) -> None:
60 """Given that the ClassNode has an attr decorator,
61 rewrite class attributes as instance attributes
62 """
63 # Astroid can't infer this attribute properly
64 # Prevents https://github.com/pylint-dev/pylint/issues/1884
65 node.locals["__attrs_attrs__"] = [Unknown(parent=node)]
67 for cdef_body_node in node.body:
68 if not isinstance(cdef_body_node, (Assign, AnnAssign)):
69 continue
70 if isinstance(cdef_body_node.value, Call):
71 if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES:
72 continue
73 else:
74 continue
75 targets = (
76 cdef_body_node.targets
77 if hasattr(cdef_body_node, "targets")
78 else [cdef_body_node.target]
79 )
80 for target in targets:
81 rhs_node = Unknown(
82 lineno=cdef_body_node.lineno,
83 col_offset=cdef_body_node.col_offset,
84 parent=cdef_body_node,
85 )
86 if isinstance(target, AssignName):
87 # Could be a subscript if the code analysed is
88 # i = Optional[str] = ""
89 # See https://github.com/pylint-dev/pylint/issues/4439
90 node.locals[target.name] = [rhs_node]
91 node.instance_attrs[target.name] = [rhs_node]
94AstroidManager().register_transform(
95 ClassDef, attr_attributes_transform, is_decorated_with_attrs
96)