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

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.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 

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) 

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) 

41 

42 

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 

52 

53 inferred = safe_infer(decorator_attribute) 

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

55 return True 

56 return False 

57 

58 

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)] 

66 

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] 

92 

93 

94AstroidManager().register_transform( 

95 ClassDef, attr_attributes_transform, is_decorated_with_attrs 

96)