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

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

166 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""" 

6Inference objects are a way to represent composite AST nodes, 

7which are used only as inference results, so they can't be found in the 

8original AST tree. For instance, inferring the following frozenset use, 

9leads to an inferred FrozenSet: 

10 

11 Call(func=Name('frozenset'), args=Tuple(...)) 

12""" 

13 

14from __future__ import annotations 

15 

16import sys 

17from collections.abc import Generator, Iterator 

18from functools import cached_property 

19from typing import Any, Literal, NoReturn 

20 

21from astroid import bases, util 

22from astroid.context import InferenceContext 

23from astroid.exceptions import ( 

24 AttributeInferenceError, 

25 InferenceError, 

26 MroError, 

27 SuperError, 

28) 

29from astroid.interpreter import objectmodel 

30from astroid.manager import AstroidManager 

31from astroid.nodes import node_classes, scoped_nodes 

32from astroid.typing import InferenceResult, SuccessfulInferenceResult 

33 

34if sys.version_info >= (3, 11): 

35 from typing import Self 

36else: 

37 from typing_extensions import Self 

38 

39 

40class FrozenSet(node_classes.BaseContainer): 

41 """Class representing a FrozenSet composite node.""" 

42 

43 def pytype(self) -> Literal["builtins.frozenset"]: 

44 return "builtins.frozenset" 

45 

46 def _infer(self, context: InferenceContext | None = None, **kwargs: Any): 

47 yield self 

48 

49 @cached_property 

50 def _proxied(self): # pylint: disable=method-hidden 

51 ast_builtins = AstroidManager().builtins_module 

52 return ast_builtins.getattr("frozenset")[0] 

53 

54 

55class Super(node_classes.NodeNG): 

56 """Proxy class over a super call. 

57 

58 This class offers almost the same behaviour as Python's super, 

59 which is MRO lookups for retrieving attributes from the parents. 

60 

61 The *mro_pointer* is the place in the MRO from where we should 

62 start looking, not counting it. *mro_type* is the object which 

63 provides the MRO, it can be both a type or an instance. 

64 *self_class* is the class where the super call is, while 

65 *scope* is the function where the super call is. 

66 """ 

67 

68 special_attributes = objectmodel.SuperModel() 

69 

70 def __init__( 

71 self, 

72 mro_pointer: SuccessfulInferenceResult, 

73 mro_type: SuccessfulInferenceResult, 

74 self_class: scoped_nodes.ClassDef, 

75 scope: scoped_nodes.FunctionDef, 

76 call: node_classes.Call, 

77 ) -> None: 

78 self.type = mro_type 

79 self.mro_pointer = mro_pointer 

80 self._class_based = False 

81 self._self_class = self_class 

82 self._scope = scope 

83 super().__init__( 

84 parent=scope, 

85 lineno=scope.lineno, 

86 col_offset=scope.col_offset, 

87 end_lineno=scope.end_lineno, 

88 end_col_offset=scope.end_col_offset, 

89 ) 

90 

91 def _infer(self, context: InferenceContext | None = None, **kwargs: Any): 

92 yield self 

93 

94 def super_mro(self): 

95 """Get the MRO which will be used to lookup attributes in this super.""" 

96 if not isinstance(self.mro_pointer, scoped_nodes.ClassDef): 

97 raise SuperError( 

98 "The first argument to super must be a subtype of " 

99 "type, not {mro_pointer}.", 

100 super_=self, 

101 ) 

102 

103 if isinstance(self.type, scoped_nodes.ClassDef): 

104 # `super(type, type)`, most likely in a class method. 

105 self._class_based = True 

106 mro_type = self.type 

107 else: 

108 mro_type = getattr(self.type, "_proxied", None) 

109 if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)): 

110 raise SuperError( 

111 "The second argument to super must be an " 

112 "instance or subtype of type, not {type}.", 

113 super_=self, 

114 ) 

115 

116 mro = mro_type.mro() 

117 if self.mro_pointer not in mro: 

118 raise SuperError( 

119 "The second argument to super must be an " 

120 "instance or subtype of type, not {type}.", 

121 super_=self, 

122 ) 

123 

124 index = mro.index(self.mro_pointer) 

125 return mro[index + 1 :] 

126 

127 @cached_property 

128 def _proxied(self): 

129 ast_builtins = AstroidManager().builtins_module 

130 return ast_builtins.getattr("super")[0] 

131 

132 def pytype(self) -> Literal["builtins.super"]: 

133 return "builtins.super" 

134 

135 def display_type(self) -> str: 

136 return "Super of" 

137 

138 @property 

139 def name(self): 

140 """Get the name of the MRO pointer.""" 

141 return self.mro_pointer.name 

142 

143 def qname(self) -> Literal["super"]: 

144 return "super" 

145 

146 def igetattr( # noqa: C901 

147 self, name: str, context: InferenceContext | None = None 

148 ) -> Iterator[InferenceResult]: 

149 """Retrieve the inferred values of the given attribute name.""" 

150 # '__class__' is a special attribute that should be taken directly 

151 # from the special attributes dict 

152 if name == "__class__": 

153 yield self.special_attributes.lookup(name) 

154 return 

155 

156 try: 

157 mro = self.super_mro() 

158 # Don't let invalid MROs or invalid super calls 

159 # leak out as is from this function. 

160 except SuperError as exc: 

161 raise AttributeInferenceError( 

162 ( 

163 "Lookup for {name} on {target!r} because super call {super!r} " 

164 "is invalid." 

165 ), 

166 target=self, 

167 attribute=name, 

168 context=context, 

169 super_=exc.super_, 

170 ) from exc 

171 except MroError as exc: 

172 raise AttributeInferenceError( 

173 ( 

174 "Lookup for {name} on {target!r} failed because {cls!r} has an " 

175 "invalid MRO." 

176 ), 

177 target=self, 

178 attribute=name, 

179 context=context, 

180 mros=exc.mros, 

181 cls=exc.cls, 

182 ) from exc 

183 found = False 

184 for cls in mro: 

185 if name not in cls.locals: 

186 continue 

187 

188 found = True 

189 for inferred in bases._infer_stmts([cls[name]], context, frame=self): 

190 if not isinstance(inferred, scoped_nodes.FunctionDef): 

191 yield inferred 

192 continue 

193 

194 # We can obtain different descriptors from a super depending 

195 # on what we are accessing and where the super call is. 

196 if inferred.type == "classmethod": 

197 # Pass original caller for classmethod too 

198 yield bases.BoundMethod(inferred, cls, original_caller=self.type) 

199 elif self._scope.type == "classmethod" and inferred.type == "method": 

200 yield inferred 

201 elif self._class_based or inferred.type == "staticmethod": 

202 yield inferred 

203 elif isinstance(inferred, Property): 

204 function = inferred.function 

205 try: 

206 yield from function.infer_call_result( 

207 caller=self, context=context 

208 ) 

209 except InferenceError: 

210 yield util.Uninferable 

211 elif bases._is_property(inferred): 

212 # TODO: support other descriptors as well. 

213 try: 

214 yield from inferred.infer_call_result(self, context) 

215 except InferenceError: 

216 yield util.Uninferable 

217 else: 

218 # Pass original caller (self.type) so infer_call_result can 

219 # correctly resolve Self return types to the actual caller type 

220 yield bases.BoundMethod(inferred, cls, original_caller=self.type) 

221 

222 # Only if we haven't found any explicit overwrites for the 

223 # attribute we look it up in the special attributes 

224 if not found and name in self.special_attributes: 

225 special_attr = self.special_attributes.lookup(name) 

226 if not isinstance(special_attr, node_classes.Unknown): 

227 yield special_attr 

228 return 

229 

230 if not found: 

231 raise AttributeInferenceError(target=self, attribute=name, context=context) 

232 

233 def getattr(self, name, context: InferenceContext | None = None): 

234 return list(self.igetattr(name, context=context)) 

235 

236 

237class ExceptionInstance(bases.Instance): 

238 """Class for instances of exceptions. 

239 

240 It has special treatment for some of the exceptions's attributes, 

241 which are transformed at runtime into certain concrete objects, such as 

242 the case of .args. 

243 """ 

244 

245 @cached_property 

246 def special_attributes(self): 

247 qname = self.qname() 

248 instance = objectmodel.BUILTIN_EXCEPTIONS.get( 

249 qname, objectmodel.ExceptionInstanceModel 

250 ) 

251 return instance()(self) 

252 

253 

254class DictInstance(bases.Instance): 

255 """Special kind of instances for dictionaries. 

256 

257 This instance knows the underlying object model of the dictionaries, which means 

258 that methods such as .values or .items can be properly inferred. 

259 """ 

260 

261 special_attributes = objectmodel.DictModel() 

262 

263 

264# Custom objects tailored for dictionaries, which are used to 

265# disambiguate between the types of Python 2 dict's method returns 

266# and Python 3 (where they return set like objects). 

267class DictItems(bases.Proxy): 

268 __str__ = node_classes.NodeNG.__str__ 

269 __repr__ = node_classes.NodeNG.__repr__ 

270 

271 

272class DictKeys(bases.Proxy): 

273 __str__ = node_classes.NodeNG.__str__ 

274 __repr__ = node_classes.NodeNG.__repr__ 

275 

276 

277class DictValues(bases.Proxy): 

278 __str__ = node_classes.NodeNG.__str__ 

279 __repr__ = node_classes.NodeNG.__repr__ 

280 

281 

282class PartialFunction(scoped_nodes.FunctionDef): 

283 """A class representing partial function obtained via functools.partial.""" 

284 

285 def __init__( 

286 self, 

287 call=None, 

288 name=None, 

289 lineno=None, 

290 col_offset=None, 

291 parent=None, 

292 *, 

293 end_lineno=None, 

294 end_col_offset=None, 

295 filled_args=None, 

296 filled_keywords=None, 

297 ): 

298 super().__init__( 

299 name, 

300 lineno=lineno, 

301 col_offset=col_offset, 

302 end_col_offset=end_col_offset, 

303 end_lineno=end_lineno, 

304 parent=parent, 

305 ) 

306 if call is None: 

307 self.filled_args = list(filled_args or []) 

308 self.filled_keywords = dict(filled_keywords or {}) 

309 else: 

310 self.filled_args = call.positional_arguments[1:] 

311 self.filled_keywords = call.keyword_arguments 

312 

313 wrapped_function = call.positional_arguments[0] 

314 inferred_wrapped_function = next(wrapped_function.infer()) 

315 if isinstance(inferred_wrapped_function, PartialFunction): 

316 self.filled_args = ( 

317 inferred_wrapped_function.filled_args + self.filled_args 

318 ) 

319 self.filled_keywords = { 

320 **inferred_wrapped_function.filled_keywords, 

321 **self.filled_keywords, 

322 } 

323 

324 self.filled_positionals = len(self.filled_args) 

325 

326 def infer_call_result( 

327 self, 

328 caller: SuccessfulInferenceResult | None, 

329 context: InferenceContext | None = None, 

330 ) -> Iterator[InferenceResult]: 

331 if context: 

332 assert ( 

333 context.callcontext 

334 ), "CallContext should be set before inferring call result" 

335 current_passed_keywords = { 

336 keyword for (keyword, _) in context.callcontext.keywords 

337 } 

338 for keyword, value in self.filled_keywords.items(): 

339 if keyword not in current_passed_keywords: 

340 context.callcontext.keywords.append((keyword, value)) 

341 

342 call_context_args = context.callcontext.args or [] 

343 context.callcontext.args = self.filled_args + call_context_args 

344 

345 return super().infer_call_result(caller=caller, context=context) 

346 

347 def qname(self) -> str: 

348 return self.__class__.__name__ 

349 

350 

351# TODO: Hack to solve the circular import problem between node_classes and objects 

352# This is not needed in 2.0, which has a cleaner design overall 

353node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance) 

354 

355 

356class Property(scoped_nodes.FunctionDef): 

357 """Class representing a Python property.""" 

358 

359 def __init__(self, function, name=None, lineno=None, col_offset=None, parent=None): 

360 self.function = function 

361 super().__init__( 

362 name, 

363 lineno=lineno, 

364 col_offset=col_offset, 

365 parent=parent, 

366 end_col_offset=function.end_col_offset, 

367 end_lineno=function.end_lineno, 

368 ) 

369 

370 special_attributes = objectmodel.PropertyModel() 

371 type = "property" 

372 

373 def pytype(self) -> Literal["builtins.property"]: 

374 return "builtins.property" 

375 

376 def infer_call_result( 

377 self, 

378 caller: SuccessfulInferenceResult | None, 

379 context: InferenceContext | None = None, 

380 ) -> NoReturn: 

381 raise InferenceError("Properties are not callable") 

382 

383 def _infer( 

384 self, context: InferenceContext | None = None, **kwargs: Any 

385 ) -> Generator[Self]: 

386 yield self