Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/util.py: 68%

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

82 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 

6from __future__ import annotations 

7 

8import re 

9import warnings 

10from typing import TYPE_CHECKING, Any, Final, Literal 

11 

12from astroid.exceptions import InferenceError 

13 

14if TYPE_CHECKING: 

15 from astroid import bases, nodes 

16 from astroid.context import InferenceContext 

17 from astroid.typing import InferenceResult 

18 

19 

20class UninferableBase: 

21 """Special inference object, which is returned when inference fails. 

22 

23 This is meant to be used as a singleton. Use astroid.util.Uninferable to access it. 

24 """ 

25 

26 def __repr__(self) -> Literal["Uninferable"]: 

27 return "Uninferable" 

28 

29 __str__ = __repr__ 

30 

31 def __getattribute__(self, name: str) -> Any: 

32 if name == "next": 

33 raise AttributeError("next method should not be called") 

34 if name.startswith("__") and name.endswith("__"): 

35 return object.__getattribute__(self, name) 

36 if name == "accept": 

37 return object.__getattribute__(self, name) 

38 return self 

39 

40 def __call__(self, *args: Any, **kwargs: Any) -> UninferableBase: 

41 return self 

42 

43 def __bool__(self) -> Literal[False]: 

44 return False 

45 

46 def accept(self, visitor): 

47 return visitor.visit_uninferable(self) 

48 

49 

50Uninferable: Final = UninferableBase() 

51 

52# Width/precision in a format spec drive how large a formatted string gets. 

53# Match the leading width and the optional ``.precision`` from the start of a 

54# format spec (PEP 3101 / format mini-language). 

55_FORMAT_SPEC_SIZE = re.compile( 

56 r"(?:.?[<>=^])?[-+ ]?z?#?0?(?P<width>\d+)?(?:[,_])?(?:\.(?P<precision>\d+))?" 

57) 

58# Mirrors the sequence/repetition caps in astroid.protocols. 

59MAX_FORMATTED_SIZE = 10**8 

60 

61 

62def format_spec_too_large(format_spec: str) -> bool: 

63 """Whether a format spec asks for an oversized width or precision. 

64 

65 Used to avoid materializing a multi-gigabyte string while inferring a tiny 

66 literal such as ``"{:>2000000000}".format("x")`` or ``f"{1.5:.2e9f}"``. 

67 """ 

68 # Every group in _FORMAT_SPEC_SIZE is optional, so match is never None. 

69 match = _FORMAT_SPEC_SIZE.match(format_spec) 

70 return any( 

71 size is not None and int(size) > MAX_FORMATTED_SIZE 

72 for size in match.group("width", "precision") 

73 ) 

74 

75 

76class BadOperationMessage: 

77 """Object which describes a TypeError occurred somewhere in the inference chain. 

78 

79 This is not an exception, but a container object which holds the types and 

80 the error which occurred. 

81 """ 

82 

83 

84class BadUnaryOperationMessage(BadOperationMessage): 

85 """Object which describes operational failures on UnaryOps.""" 

86 

87 def __init__(self, operand, op, error): 

88 self.operand = operand 

89 self.op = op 

90 self.error = error 

91 

92 @property 

93 def _object_type_helper(self): 

94 from astroid import helpers # pylint: disable=import-outside-toplevel 

95 

96 return helpers.object_type 

97 

98 def _object_type(self, obj): 

99 objtype = self._object_type_helper(obj) 

100 if isinstance(objtype, UninferableBase): 

101 return None 

102 

103 return objtype 

104 

105 def __str__(self) -> str: 

106 if hasattr(self.operand, "name"): 

107 operand_type = self.operand.name 

108 else: 

109 object_type = self._object_type(self.operand) 

110 if hasattr(object_type, "name"): 

111 operand_type = object_type.name 

112 else: 

113 # Just fallback to as_string 

114 operand_type = object_type.as_string() 

115 

116 msg = "bad operand type for unary {}: {}" 

117 return msg.format(self.op, operand_type) 

118 

119 

120class BadBinaryOperationMessage(BadOperationMessage): 

121 """Object which describes type errors for BinOps.""" 

122 

123 def __init__(self, left_type, op, right_type): 

124 self.left_type = left_type 

125 self.right_type = right_type 

126 self.op = op 

127 

128 def __str__(self) -> str: 

129 return ( 

130 f"unsupported operand type(s) for {self.op}: {self.left_type.name!r} " 

131 f"and {self.right_type.name!r}" 

132 ) 

133 

134 

135def check_warnings_filter() -> bool: 

136 """Return True if any other than the default DeprecationWarning filter is enabled. 

137 

138 https://docs.python.org/3/library/warnings.html#default-warning-filter 

139 """ 

140 return any( 

141 issubclass(DeprecationWarning, filter[2]) 

142 and filter[0] != "ignore" 

143 and filter[3] != "__main__" 

144 for filter in warnings.filters 

145 ) 

146 

147 

148def safe_infer( 

149 node: nodes.NodeNG | bases.Proxy | UninferableBase, 

150 context: InferenceContext | None = None, 

151) -> InferenceResult | None: 

152 """Return the inferred value for the given node. 

153 

154 Return None if inference failed or if there is some ambiguity (more than 

155 one node has been inferred). 

156 """ 

157 if isinstance(node, UninferableBase): 

158 return node 

159 try: 

160 inferit = node.infer(context=context) 

161 value = next(inferit) 

162 except (InferenceError, StopIteration): 

163 return None 

164 try: 

165 next(inferit) 

166 return None # None if there is ambiguity on the inferred node 

167 except InferenceError: 

168 return None # there is some kind of ambiguity 

169 except StopIteration: 

170 return value