Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/brain/brain_attrs.py: 49%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

39 statements  

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 

4 

5""" 

6Astroid hook for the attrs library 

7 

8Without this hook pylint reports unsupported-assignment-operation 

9for attrs classes 

10""" 

11from astroid.manager import AstroidManager 

12from astroid.nodes.node_classes import AnnAssign, Assign, AssignName, Call, Unknown 

13from astroid.nodes.scoped_nodes import ClassDef 

14from astroid.util import safe_infer 

15 

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) 

27NEW_ATTRS_NAMES = frozenset( 

28 ( 

29 "attrs.define", 

30 "attrs.mutable", 

31 "attrs.frozen", 

32 ) 

33) 

34ATTRS_NAMES = frozenset( 

35 ( 

36 "attr.s", 

37 "attrs", 

38 "attr.attrs", 

39 "attr.attributes", 

40 "attr.define", 

41 "attr.mutable", 

42 "attr.frozen", 

43 *NEW_ATTRS_NAMES, 

44 ) 

45) 

46 

47 

48def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool: 

49 """Return whether a decorated node has an attr decorator applied.""" 

50 if not node.decorators: 

51 return False 

52 for decorator_attribute in node.decorators.nodes: 

53 if isinstance(decorator_attribute, Call): # decorator with arguments 

54 decorator_attribute = decorator_attribute.func 

55 if decorator_attribute.as_string() in decorator_names: 

56 return True 

57 

58 inferred = safe_infer(decorator_attribute) 

59 if inferred and inferred.root().name == "attr._next_gen": 

60 return True 

61 return False 

62 

63 

64def attr_attributes_transform(node: ClassDef) -> None: 

65 """Given that the ClassNode has an attr decorator, 

66 rewrite class attributes as instance attributes 

67 """ 

68 # Astroid can't infer this attribute properly 

69 # Prevents https://github.com/pylint-dev/pylint/issues/1884 

70 node.locals["__attrs_attrs__"] = [Unknown(parent=node)] 

71 

72 use_bare_annotations = is_decorated_with_attrs(node, NEW_ATTRS_NAMES) 

73 for cdef_body_node in node.body: 

74 if not isinstance(cdef_body_node, (Assign, AnnAssign)): 

75 continue 

76 if isinstance(cdef_body_node.value, Call): 

77 if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES: 

78 continue 

79 elif not use_bare_annotations: 

80 continue 

81 targets = ( 

82 cdef_body_node.targets 

83 if hasattr(cdef_body_node, "targets") 

84 else [cdef_body_node.target] 

85 ) 

86 for target in targets: 

87 rhs_node = Unknown( 

88 lineno=cdef_body_node.lineno, 

89 col_offset=cdef_body_node.col_offset, 

90 parent=cdef_body_node, 

91 ) 

92 if isinstance(target, AssignName): 

93 # Could be a subscript if the code analysed is 

94 # i = Optional[str] = "" 

95 # See https://github.com/pylint-dev/pylint/issues/4439 

96 node.locals[target.name] = [rhs_node] 

97 node.instance_attrs[target.name] = [rhs_node] 

98 

99 

100def register(manager: AstroidManager) -> None: 

101 manager.register_transform( 

102 ClassDef, attr_attributes_transform, is_decorated_with_attrs 

103 )