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 __contains__(self, o): return o in self.iterable 

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

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

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

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

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

67 def __add__(self, other): pass 

68 def __iadd__(self, other): pass 

69 def __mul__(self, other): pass 

70 def __imul__(self, other): pass 

71 def __rmul__(self, other): pass 

72 @classmethod 

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

74 return base_deque_class 

75 

76 

77def _ordered_dict_mock(): 

78 base_ordered_dict_class = """ 

79 class OrderedDict(dict): 

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

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

82 @classmethod 

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

84 return base_ordered_dict_class 

85 

86 

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

88 """ 

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

90 that supports subscripting. 

91 

92 :param node: ClassDef node 

93 """ 

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

95 "collections" 

96 ): 

97 try: 

98 node.getattr("__class_getitem__") 

99 return True 

100 except AttributeInferenceError: 

101 pass 

102 return False 

103 

104 

105CLASS_GET_ITEM_TEMPLATE = """ 

106@classmethod 

107def __class_getitem__(cls, item): 

108 return cls 

109""" 

110 

111 

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

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

114 # put an easy inference tip 

115 func_to_add = extract_node(CLASS_GET_ITEM_TEMPLATE) 

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

117 

118 

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

120 register_module_extender(manager, "collections", _collections_transform) 

121 

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

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

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

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

126 manager.register_transform( 

127 ClassDef, easy_class_getitem_inference, _looks_like_subscriptable 

128 ) 

129 

130 if PY313_PLUS: 

131 register_module_extender( 

132 manager, "collections.abc", _collections_abc_313_transform 

133 )