Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/brain/brain_collections.py: 59%

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 

5from __future__ import annotations 

6 

7from typing import TYPE_CHECKING 

8 

9from astroid.brain.helpers import register_module_extender 

10from astroid.builder import AstroidBuilder, extract_node, parse 

11from astroid.const import PY313_PLUS 

12from astroid.context import InferenceContext 

13from astroid.exceptions import AttributeInferenceError 

14from astroid.manager import AstroidManager 

15from astroid.nodes.scoped_nodes import ClassDef 

16 

17if TYPE_CHECKING: 

18 from astroid import nodes 

19 

20 

21def _collections_transform(): 

22 return parse(""" 

23 class defaultdict(dict): 

24 default_factory = None 

25 def __missing__(self, key): pass 

26 def __getitem__(self, key): return default_factory 

27 

28 """ + _deque_mock() + _ordered_dict_mock()) 

29 

30 

31def _collections_abc_313_transform() -> nodes.Module: 

32 """See https://github.com/python/cpython/pull/124735""" 

33 return AstroidBuilder(AstroidManager()).string_build( 

34 "from _collections_abc import *" 

35 ) 

36 

37 

38def _deque_mock(): 

39 base_deque_class = """ 

40 class deque(object): 

41 maxlen = 0 

42 def __init__(self, iterable=None, maxlen=None): 

43 self.iterable = iterable or [] 

44 def append(self, x): pass 

45 def appendleft(self, x): pass 

46 def clear(self): pass 

47 def count(self, x): return 0 

48 def extend(self, iterable): pass 

49 def extendleft(self, iterable): pass 

50 def pop(self): return self.iterable[0] 

51 def popleft(self): return self.iterable[0] 

52 def remove(self, value): pass 

53 def reverse(self): return reversed(self.iterable) 

54 def rotate(self, n=1): return self 

55 def __iter__(self): return self 

56 def __reversed__(self): return self.iterable[::-1] 

57 def __getitem__(self, index): return self.iterable[index] 

58 def __setitem__(self, index, value): pass 

59 def __delitem__(self, index): pass 

60 def __bool__(self): return bool(self.iterable) 

61 def __nonzero__(self): return bool(self.iterable) 

62 def __contains__(self, o): return o in self.iterable 

63 def __len__(self): return len(self.iterable) 

64 def __copy__(self): return deque(self.iterable) 

65 def copy(self): return deque(self.iterable) 

66 def index(self, x, start=0, end=0): return 0 

67 def insert(self, i, x): pass 

68 def __add__(self, other): pass 

69 def __iadd__(self, other): pass 

70 def __mul__(self, other): pass 

71 def __imul__(self, other): pass 

72 def __rmul__(self, other): pass 

73 @classmethod 

74 def __class_getitem__(self, item): return cls""" 

75 return base_deque_class 

76 

77 

78def _ordered_dict_mock(): 

79 base_ordered_dict_class = """ 

80 class OrderedDict(dict): 

81 def __reversed__(self): return self[::-1] 

82 def move_to_end(self, key, last=False): pass 

83 @classmethod 

84 def __class_getitem__(cls, item): return cls""" 

85 return base_ordered_dict_class 

86 

87 

88def _looks_like_subscriptable(node: ClassDef) -> bool: 

89 """ 

90 Returns True if the node corresponds to a ClassDef of the Collections.abc module 

91 that supports subscripting. 

92 

93 :param node: ClassDef node 

94 """ 

95 if node.qname().startswith("_collections") or node.qname().startswith( 

96 "collections" 

97 ): 

98 try: 

99 node.getattr("__class_getitem__") 

100 return True 

101 except AttributeInferenceError: 

102 pass 

103 return False 

104 

105 

106CLASS_GET_ITEM_TEMPLATE = """ 

107@classmethod 

108def __class_getitem__(cls, item): 

109 return cls 

110""" 

111 

112 

113def easy_class_getitem_inference(node, context: InferenceContext | None = None): 

114 # Here __class_getitem__ exists but is quite a mess to infer thus 

115 # put an easy inference tip 

116 func_to_add = extract_node(CLASS_GET_ITEM_TEMPLATE) 

117 node.locals["__class_getitem__"] = [func_to_add] 

118 

119 

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

121 register_module_extender(manager, "collections", _collections_transform) 

122 

123 # Starting with Python39 some objects of the collection module are subscriptable 

124 # thanks to the __class_getitem__ method but the way it is implemented in 

125 # _collection_abc makes it difficult to infer. (We would have to handle AssignName inference in the 

126 # getitem method of the ClassDef class) Instead we put here a mock of the __class_getitem__ method 

127 manager.register_transform( 

128 ClassDef, easy_class_getitem_inference, _looks_like_subscriptable 

129 ) 

130 

131 if PY313_PLUS: 

132 register_module_extender( 

133 manager, "collections.abc", _collections_abc_313_transform 

134 )