Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/interpreter/objectmodel.py: 46%

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

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

6Data object model, as per https://docs.python.org/3/reference/datamodel.html. 

7 

8This module describes, at least partially, a data object model for some 

9of astroid's nodes. The model contains special attributes that nodes such 

10as functions, classes, modules etc have, such as __doc__, __class__, 

11__module__ etc, being used when doing attribute lookups over nodes. 

12 

13For instance, inferring `obj.__class__` will first trigger an inference 

14of the `obj` variable. If it was successfully inferred, then an attribute 

15`__class__ will be looked for in the inferred object. This is the part 

16where the data model occurs. The model is attached to those nodes 

17and the lookup mechanism will try to see if attributes such as 

18`__class__` are defined by the model or not. If they are defined, 

19the model will be requested to return the corresponding value of that 

20attribute. Thus the model can be viewed as a special part of the lookup 

21mechanism. 

22""" 

23 

24from __future__ import annotations 

25 

26import itertools 

27import os 

28import types 

29from collections.abc import Iterator 

30from functools import lru_cache 

31from typing import TYPE_CHECKING, Any, Literal 

32 

33import astroid 

34from astroid import bases, nodes, util 

35from astroid.context import InferenceContext, copy_context 

36from astroid.exceptions import AttributeInferenceError, InferenceError, NoDefault 

37from astroid.manager import AstroidManager 

38from astroid.nodes import node_classes 

39from astroid.typing import InferenceResult, SuccessfulInferenceResult 

40 

41if TYPE_CHECKING: 

42 from astroid.objects import Property 

43 

44IMPL_PREFIX = "attr_" 

45LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX) 

46 

47 

48def _dunder_dict(instance, attributes): 

49 obj = node_classes.Dict( 

50 parent=instance, 

51 lineno=instance.lineno, 

52 col_offset=instance.col_offset, 

53 end_lineno=instance.end_lineno, 

54 end_col_offset=instance.end_col_offset, 

55 ) 

56 

57 # Convert the keys to node strings 

58 keys = [ 

59 node_classes.Const(value=value, parent=obj) for value in list(attributes.keys()) 

60 ] 

61 

62 # The original attribute has a list of elements for each key, 

63 # but that is not useful for retrieving the special attribute's value. 

64 # In this case, we're picking the last value from each list. 

65 values = [elem[-1] for elem in attributes.values()] 

66 

67 obj.postinit(list(zip(keys, values))) 

68 return obj 

69 

70 

71def _get_bound_node(model: ObjectModel) -> Any: 

72 # TODO: Use isinstance instead of try ... except after _instance has typing 

73 try: 

74 return model._instance._proxied 

75 except AttributeError: 

76 return model._instance 

77 

78 

79class ObjectModel: 

80 def __init__(self): 

81 self._instance = None 

82 

83 def __repr__(self): 

84 import pprint # pylint: disable=import-outside-toplevel 

85 

86 result = [] 

87 cname = type(self).__name__ 

88 string = "%(cname)s(%(fields)s)" 

89 alignment = len(cname) + 1 

90 for field in sorted(self.attributes()): 

91 width = max(80 - len(field) - alignment, 1) 

92 try: 

93 lines = pprint.pformat(field, indent=2, width=width).splitlines(True) 

94 except ValueError: 

95 lines = [f"<{type(field).__name__}>"] 

96 

97 inner = [lines[0]] 

98 for line in lines[1:]: 

99 inner.append(" " * alignment + line) 

100 result.append(field) 

101 

102 return string % { 

103 "cname": cname, 

104 "fields": (",\n" + " " * alignment).join(result), 

105 } 

106 

107 def __call__(self, instance): 

108 self._instance = instance 

109 return self 

110 

111 def __get__(self, instance, cls=None): 

112 # ObjectModel needs to be a descriptor so that just doing 

113 # `special_attributes = SomeObjectModel` should be enough in the body of a node. 

114 # But at the same time, node.special_attributes should return an object 

115 # which can be used for manipulating the special attributes. That's the reason 

116 # we pass the instance through which it got accessed to ObjectModel.__call__, 

117 # returning itself afterwards, so we can still have access to the 

118 # underlying data model and to the instance for which it got accessed. 

119 return self(instance) 

120 

121 def __contains__(self, name) -> bool: 

122 return name in self.attributes() 

123 

124 @lru_cache # noqa 

125 def attributes(self) -> list[str]: 

126 """Get the attributes which are exported by this object model.""" 

127 return [o[LEN_OF_IMPL_PREFIX:] for o in dir(self) if o.startswith(IMPL_PREFIX)] 

128 

129 def lookup(self, name): 

130 """Look up the given *name* in the current model. 

131 

132 It should return an AST or an interpreter object, 

133 but if the name is not found, then an AttributeInferenceError will be raised. 

134 """ 

135 if name in self.attributes(): 

136 return getattr(self, IMPL_PREFIX + name) 

137 raise AttributeInferenceError(target=self._instance, attribute=name) 

138 

139 @property 

140 def attr___new__(self) -> bases.BoundMethod: 

141 """Calling cls.__new__(type) on an object returns an instance of 'type'.""" 

142 from astroid import builder # pylint: disable=import-outside-toplevel 

143 

144 node: nodes.FunctionDef = builder.extract_node( 

145 """def __new__(self, cls): return cls()""" 

146 ) 

147 # We set the parent as being the ClassDef of 'object' as that 

148 # triggers correct inference as a call to __new__ in bases.py 

149 node.parent = AstroidManager().builtins_module["object"] 

150 

151 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) 

152 

153 @property 

154 def attr___init__(self) -> bases.BoundMethod: 

155 """Calling cls.__init__() normally returns None.""" 

156 from astroid import builder # pylint: disable=import-outside-toplevel 

157 

158 # The *args and **kwargs are necessary not to trigger warnings about missing 

159 # or extra parameters for '__init__' methods we don't infer correctly. 

160 # This BoundMethod is the fallback value for those. 

161 node: nodes.FunctionDef = builder.extract_node( 

162 """def __init__(self, *args, **kwargs): return None""" 

163 ) 

164 # We set the parent as being the ClassDef of 'object' as that 

165 # is where this method originally comes from 

166 node.parent = AstroidManager().builtins_module["object"] 

167 

168 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) 

169 

170 # Base object attributes that return Unknown as fallback placeholders. 

171 @property 

172 def attr___ne__(self): 

173 return node_classes.Unknown(parent=self._instance) 

174 

175 attr___class__ = attr___ne__ 

176 attr___delattr__ = attr___ne__ 

177 attr___dir__ = attr___ne__ 

178 attr___doc__ = attr___ne__ 

179 attr___eq__ = attr___ne__ 

180 attr___format__ = attr___ne__ 

181 attr___ge__ = attr___ne__ 

182 attr___getattribute__ = attr___ne__ 

183 attr___getstate__ = attr___ne__ 

184 attr___gt__ = attr___ne__ 

185 attr___hash__ = attr___ne__ 

186 attr___init_subclass__ = attr___ne__ 

187 attr___le__ = attr___ne__ 

188 attr___lt__ = attr___ne__ 

189 attr___reduce__ = attr___ne__ 

190 attr___reduce_ex__ = attr___ne__ 

191 attr___repr__ = attr___ne__ 

192 attr___setattr__ = attr___ne__ 

193 attr___sizeof__ = attr___ne__ 

194 attr___str__ = attr___ne__ 

195 attr___subclasshook__ = attr___ne__ 

196 

197 

198class ModuleModel(ObjectModel): 

199 def _builtins(self): 

200 builtins_ast_module = AstroidManager().builtins_module 

201 return builtins_ast_module.special_attributes.lookup("__dict__") 

202 

203 @property 

204 def attr_builtins(self): 

205 return self._builtins() 

206 

207 @property 

208 def attr___path__(self): 

209 if not self._instance.package: 

210 raise AttributeInferenceError(target=self._instance, attribute="__path__") 

211 

212 path_objs = [ 

213 node_classes.Const( 

214 value=( 

215 path if not path.endswith("__init__.py") else os.path.dirname(path) 

216 ), 

217 parent=self._instance, 

218 ) 

219 for path in self._instance.path 

220 ] 

221 

222 container = node_classes.List(parent=self._instance) 

223 container.postinit(path_objs) 

224 

225 return container 

226 

227 @property 

228 def attr___name__(self): 

229 return node_classes.Const(value=self._instance.name, parent=self._instance) 

230 

231 @property 

232 def attr___doc__(self): 

233 return node_classes.Const( 

234 value=getattr(self._instance.doc_node, "value", None), 

235 parent=self._instance, 

236 ) 

237 

238 @property 

239 def attr___file__(self): 

240 return node_classes.Const(value=self._instance.file, parent=self._instance) 

241 

242 @property 

243 def attr___dict__(self): 

244 return _dunder_dict(self._instance, self._instance.globals) 

245 

246 @property 

247 def attr___package__(self): 

248 if not self._instance.package: 

249 value = "" 

250 else: 

251 value = self._instance.name 

252 

253 return node_classes.Const(value=value, parent=self._instance) 

254 

255 # These are related to the Python 3 implementation of the 

256 # import system, 

257 # https://docs.python.org/3/reference/import.html#import-related-module-attributes 

258 

259 @property 

260 def attr___spec__(self): 

261 # No handling for now. 

262 return node_classes.Unknown(parent=self._instance) 

263 

264 @property 

265 def attr___loader__(self): 

266 # No handling for now. 

267 return node_classes.Unknown(parent=self._instance) 

268 

269 @property 

270 def attr___cached__(self): 

271 # No handling for now. 

272 return node_classes.Unknown(parent=self._instance) 

273 

274 

275class FunctionModel(ObjectModel): 

276 def _is_builtin_func(self) -> bool: 

277 func = self._instance 

278 return isinstance(func.parent, nodes.Module) and not func.root().pure_python 

279 

280 @property 

281 def attr___name__(self): 

282 return node_classes.Const(value=self._instance.name, parent=self._instance) 

283 

284 @property 

285 def attr___doc__(self): 

286 return node_classes.Const( 

287 value=getattr(self._instance.doc_node, "value", None), 

288 parent=self._instance, 

289 ) 

290 

291 @property 

292 def attr___qualname__(self): 

293 return node_classes.Const(value=self._instance.qname(), parent=self._instance) 

294 

295 @property 

296 def attr___defaults__(self): 

297 func = self._instance 

298 if self._is_builtin_func(): 

299 raise AttributeInferenceError(target=func, attribute="__defaults__") 

300 if not func.args.defaults: 

301 return node_classes.Const(value=None, parent=func) 

302 

303 defaults_obj = node_classes.Tuple(parent=func) 

304 defaults_obj.postinit(func.args.defaults) 

305 return defaults_obj 

306 

307 @property 

308 def attr___annotations__(self): 

309 if self._is_builtin_func(): 

310 raise AttributeInferenceError( 

311 target=self._instance, attribute="__annotations__" 

312 ) 

313 obj = node_classes.Dict( 

314 parent=self._instance, 

315 lineno=self._instance.lineno, 

316 col_offset=self._instance.col_offset, 

317 end_lineno=self._instance.end_lineno, 

318 end_col_offset=self._instance.end_col_offset, 

319 ) 

320 

321 if not self._instance.returns: 

322 returns = None 

323 else: 

324 returns = self._instance.returns 

325 

326 args = self._instance.args 

327 pair_annotations = itertools.chain( 

328 zip(args.args or [], args.annotations), 

329 zip(args.kwonlyargs, args.kwonlyargs_annotations), 

330 zip(args.posonlyargs or [], args.posonlyargs_annotations), 

331 ) 

332 

333 annotations = { 

334 arg.name: annotation for (arg, annotation) in pair_annotations if annotation 

335 } 

336 if args.varargannotation: 

337 annotations[args.vararg] = args.varargannotation 

338 if args.kwargannotation: 

339 annotations[args.kwarg] = args.kwargannotation 

340 if returns: 

341 annotations["return"] = returns 

342 

343 items = [ 

344 (node_classes.Const(key, parent=obj), value) 

345 for (key, value) in annotations.items() 

346 ] 

347 

348 obj.postinit(items) 

349 return obj 

350 

351 @property 

352 def attr___dict__(self): 

353 if self._is_builtin_func(): 

354 raise AttributeInferenceError(target=self._instance, attribute="__dict__") 

355 return node_classes.Dict( 

356 parent=self._instance, 

357 lineno=self._instance.lineno, 

358 col_offset=self._instance.col_offset, 

359 end_lineno=self._instance.end_lineno, 

360 end_col_offset=self._instance.end_col_offset, 

361 ) 

362 

363 @property 

364 def attr___globals__(self): 

365 if self._is_builtin_func(): 

366 raise AttributeInferenceError( 

367 target=self._instance, attribute="__globals__" 

368 ) 

369 return node_classes.Dict( 

370 parent=self._instance, 

371 lineno=self._instance.lineno, 

372 col_offset=self._instance.col_offset, 

373 end_lineno=self._instance.end_lineno, 

374 end_col_offset=self._instance.end_col_offset, 

375 ) 

376 

377 @property 

378 def attr___kwdefaults__(self): 

379 if self._is_builtin_func(): 

380 raise AttributeInferenceError( 

381 target=self._instance, attribute="__kwdefaults__" 

382 ) 

383 

384 def _default_args(args, parent): 

385 for arg in args.kwonlyargs: 

386 try: 

387 default = args.default_value(arg.name) 

388 except NoDefault: 

389 continue 

390 

391 name = node_classes.Const(arg.name, parent=parent) 

392 yield name, default 

393 

394 args = self._instance.args 

395 obj = node_classes.Dict( 

396 parent=self._instance, 

397 lineno=self._instance.lineno, 

398 col_offset=self._instance.col_offset, 

399 end_lineno=self._instance.end_lineno, 

400 end_col_offset=self._instance.end_col_offset, 

401 ) 

402 defaults = dict(_default_args(args, obj)) 

403 

404 obj.postinit(list(defaults.items())) 

405 return obj 

406 

407 @property 

408 def attr___module__(self): 

409 return node_classes.Const(self._instance.root().qname()) 

410 

411 @property 

412 def attr___get__(self): 

413 func = self._instance 

414 

415 if self._is_builtin_func(): 

416 raise AttributeInferenceError(target=func, attribute="__get__") 

417 

418 class DescriptorBoundMethod(bases.BoundMethod): 

419 """Bound method which knows how to understand calling descriptor 

420 binding. 

421 """ 

422 

423 def implicit_parameters(self) -> Literal[0]: 

424 # Different than BoundMethod since the signature 

425 # is different. 

426 return 0 

427 

428 def infer_call_result( 

429 self, 

430 caller: SuccessfulInferenceResult | None, 

431 context: InferenceContext | None = None, 

432 ) -> Iterator[bases.BoundMethod]: 

433 if len(caller.args) > 2 or len(caller.args) < 1: 

434 raise InferenceError( 

435 "Invalid arguments for descriptor binding", 

436 target=self, 

437 context=context, 

438 ) 

439 

440 context = copy_context(context) 

441 try: 

442 cls = next(caller.args[0].infer(context=context)) 

443 except StopIteration as e: 

444 raise InferenceError(context=context, node=caller.args[0]) from e 

445 

446 if isinstance(cls, util.UninferableBase): 

447 raise InferenceError( 

448 "Invalid class inferred", target=self, context=context 

449 ) 

450 

451 # The `func` can already be a Unbound or BoundMethod. If the former, make sure to 

452 # wrap as a BoundMethod like we do below when constructing the function from scratch. 

453 if isinstance(func, bases.UnboundMethod): 

454 yield bases.BoundMethod(proxy=func, bound=cls) 

455 return 

456 

457 if isinstance(func, bases.BoundMethod): 

458 yield func 

459 return 

460 

461 # Rebuild the original value, but with the parent set as the 

462 # class where it will be bound. 

463 new_func_kwargs = { 

464 "name": func.name, 

465 "lineno": func.lineno, 

466 "col_offset": func.col_offset, 

467 "parent": func.parent, 

468 "end_lineno": func.end_lineno, 

469 "end_col_offset": func.end_col_offset, 

470 } 

471 if isinstance(func, astroid.objects.PartialFunction): 

472 new_func_kwargs["filled_args"] = func.filled_args 

473 new_func_kwargs["filled_keywords"] = func.filled_keywords 

474 

475 new_func = func.__class__(**new_func_kwargs) 

476 # pylint: disable=no-member 

477 new_func.postinit( 

478 func.args, 

479 func.body, 

480 func.decorators, 

481 func.returns, 

482 doc_node=func.doc_node, 

483 ) 

484 

485 # Build a proper bound method that points to our newly built function. 

486 proxy = bases.UnboundMethod(new_func) 

487 yield bases.BoundMethod(proxy=proxy, bound=cls) 

488 

489 @property 

490 def args(self): 

491 """Overwrite the underlying args to match those of the underlying func. 

492 

493 Usually the underlying *func* is a function/method, as in: 

494 

495 def test(self): 

496 pass 

497 

498 This has only the *self* parameter but when we access test.__get__ 

499 we get a new object which has two parameters, *self* and *type*. 

500 """ 

501 nonlocal func 

502 arguments = nodes.Arguments( 

503 parent=func.args.parent, vararg=None, kwarg=None 

504 ) 

505 

506 positional_or_keyword_params = func.args.args.copy() 

507 positional_or_keyword_params.append( 

508 nodes.AssignName( 

509 name="type", 

510 lineno=0, 

511 col_offset=0, 

512 parent=arguments, 

513 end_lineno=None, 

514 end_col_offset=None, 

515 ) 

516 ) 

517 

518 positional_only_params = func.args.posonlyargs.copy() 

519 

520 arguments.postinit( 

521 args=positional_or_keyword_params, 

522 posonlyargs=positional_only_params, 

523 defaults=[], 

524 kwonlyargs=[], 

525 kw_defaults=[], 

526 annotations=[], 

527 kwonlyargs_annotations=[], 

528 posonlyargs_annotations=[], 

529 ) 

530 return arguments 

531 

532 return DescriptorBoundMethod(proxy=self._instance, bound=self._instance) 

533 

534 # Function-specific attributes. 

535 @property 

536 def attr___call__(self): 

537 return node_classes.Unknown(parent=self._instance) 

538 

539 attr___builtins__ = attr___call__ 

540 attr___closure__ = attr___call__ 

541 attr___code__ = attr___call__ 

542 attr___type_params__ = attr___call__ 

543 

544 

545class ClassModel(ObjectModel): 

546 def __init__(self): 

547 # Add a context so that inferences called from an instance don't recurse endlessly 

548 self.context = InferenceContext() 

549 

550 super().__init__() 

551 

552 @property 

553 def attr___annotations__(self) -> node_classes.Unknown: 

554 return node_classes.Unknown(parent=self._instance) 

555 

556 @property 

557 def attr___module__(self): 

558 return node_classes.Const(self._instance.root().qname()) 

559 

560 @property 

561 def attr___name__(self): 

562 return node_classes.Const(self._instance.name) 

563 

564 @property 

565 def attr___qualname__(self): 

566 return node_classes.Const(self._instance.qname()) 

567 

568 @property 

569 def attr___doc__(self): 

570 return node_classes.Const(getattr(self._instance.doc_node, "value", None)) 

571 

572 @property 

573 def attr___mro__(self): 

574 mro = self._instance.mro() 

575 obj = node_classes.Tuple(parent=self._instance) 

576 obj.postinit(mro) 

577 return obj 

578 

579 @property 

580 def attr_mro(self): 

581 other_self = self 

582 

583 # Cls.mro is a method and we need to return one in order to have a proper inference. 

584 # The method we're returning is capable of inferring the underlying MRO though. 

585 class MroBoundMethod(bases.BoundMethod): 

586 def infer_call_result( 

587 self, 

588 caller: SuccessfulInferenceResult | None, 

589 context: InferenceContext | None = None, 

590 ) -> Iterator[node_classes.Tuple]: 

591 yield other_self.attr___mro__ 

592 

593 implicit_metaclass = self._instance.implicit_metaclass() 

594 mro_method = implicit_metaclass.locals["mro"][0] 

595 return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass) 

596 

597 @property 

598 def attr___bases__(self): 

599 obj = node_classes.Tuple() 

600 context = InferenceContext() 

601 elts = list(self._instance._inferred_bases(context)) 

602 obj.postinit(elts=elts) 

603 return obj 

604 

605 @property 

606 def attr___class__(self): 

607 # pylint: disable=import-outside-toplevel; circular import 

608 from astroid import helpers 

609 

610 return helpers.object_type(self._instance) 

611 

612 @property 

613 def attr___subclasses__(self): 

614 """Get the subclasses of the underlying class. 

615 

616 This looks only in the current module for retrieving the subclasses, 

617 thus it might miss a couple of them. 

618 """ 

619 

620 qname = self._instance.qname() 

621 root = self._instance.root() 

622 classes = [ 

623 cls 

624 for cls in root.nodes_of_class(nodes.ClassDef) 

625 if cls != self._instance and cls.is_subtype_of(qname, context=self.context) 

626 ] 

627 

628 obj = node_classes.List(parent=self._instance) 

629 obj.postinit(classes) 

630 

631 class SubclassesBoundMethod(bases.BoundMethod): 

632 def infer_call_result( 

633 self, 

634 caller: SuccessfulInferenceResult | None, 

635 context: InferenceContext | None = None, 

636 ) -> Iterator[node_classes.List]: 

637 yield obj 

638 

639 implicit_metaclass = self._instance.implicit_metaclass() 

640 subclasses_method = implicit_metaclass.locals["__subclasses__"][0] 

641 return SubclassesBoundMethod(proxy=subclasses_method, bound=implicit_metaclass) 

642 

643 @property 

644 def attr___dict__(self): 

645 return node_classes.Dict( 

646 parent=self._instance, 

647 lineno=self._instance.lineno, 

648 col_offset=self._instance.col_offset, 

649 end_lineno=self._instance.end_lineno, 

650 end_col_offset=self._instance.end_col_offset, 

651 ) 

652 

653 @property 

654 def attr___call__(self): 

655 """Calling a class A() returns an instance of A.""" 

656 return self._instance.instantiate_class() 

657 

658 

659class SuperModel(ObjectModel): 

660 @property 

661 def attr___thisclass__(self): 

662 return self._instance.mro_pointer 

663 

664 @property 

665 def attr___self_class__(self): 

666 return self._instance._self_class 

667 

668 @property 

669 def attr___self__(self): 

670 return self._instance.type 

671 

672 @property 

673 def attr___class__(self): 

674 return self._instance._proxied 

675 

676 

677class UnboundMethodModel(FunctionModel): 

678 @property 

679 def attr___class__(self): 

680 # pylint: disable=import-outside-toplevel; circular import 

681 from astroid import helpers 

682 

683 return helpers.object_type(self._instance) 

684 

685 @property 

686 def attr___func__(self): 

687 return self._instance._proxied 

688 

689 @property 

690 def attr___self__(self): 

691 return node_classes.Const(value=None, parent=self._instance) 

692 

693 attr_im_func = attr___func__ 

694 attr_im_class = attr___class__ 

695 attr_im_self = attr___self__ 

696 

697 

698class ContextManagerModel(ObjectModel): 

699 """Model for context managers. 

700 

701 Based on 3.3.9 of the Data Model documentation: 

702 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers 

703 """ 

704 

705 @property 

706 def attr___enter__(self) -> bases.BoundMethod: 

707 """Representation of the base implementation of __enter__. 

708 

709 As per Python documentation: 

710 Enter the runtime context related to this object. The with statement 

711 will bind this method's return value to the target(s) specified in the 

712 as clause of the statement, if any. 

713 """ 

714 from astroid import builder # pylint: disable=import-outside-toplevel 

715 

716 node: nodes.FunctionDef = builder.extract_node("""def __enter__(self): ...""") 

717 # We set the parent as being the ClassDef of 'object' as that 

718 # is where this method originally comes from 

719 node.parent = AstroidManager().builtins_module["object"] 

720 

721 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) 

722 

723 @property 

724 def attr___exit__(self) -> bases.BoundMethod: 

725 """Representation of the base implementation of __exit__. 

726 

727 As per Python documentation: 

728 Exit the runtime context related to this object. The parameters describe the 

729 exception that caused the context to be exited. If the context was exited 

730 without an exception, all three arguments will be None. 

731 """ 

732 from astroid import builder # pylint: disable=import-outside-toplevel 

733 

734 node: nodes.FunctionDef = builder.extract_node( 

735 """def __exit__(self, exc_type, exc_value, traceback): ...""" 

736 ) 

737 # We set the parent as being the ClassDef of 'object' as that 

738 # is where this method originally comes from 

739 node.parent = AstroidManager().builtins_module["object"] 

740 

741 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) 

742 

743 

744class BoundMethodModel(FunctionModel): 

745 @property 

746 def attr___func__(self): 

747 return self._instance._proxied._proxied 

748 

749 @property 

750 def attr___self__(self): 

751 return self._instance.bound 

752 

753 

754class GeneratorBaseModel(FunctionModel, ContextManagerModel): 

755 def __init__(self, gen_module: nodes.Module): 

756 super().__init__() 

757 for name, values in gen_module.locals.items(): 

758 method = values[0] 

759 if isinstance(method, nodes.FunctionDef): 

760 method = bases.BoundMethod(method, _get_bound_node(self)) 

761 

762 def patched(cls, meth=method): 

763 return meth 

764 

765 setattr(type(self), IMPL_PREFIX + name, property(patched)) 

766 

767 @property 

768 def attr___name__(self): 

769 return node_classes.Const( 

770 value=self._instance.parent.name, parent=self._instance 

771 ) 

772 

773 @property 

774 def attr___doc__(self): 

775 return node_classes.Const( 

776 value=getattr(self._instance.parent.doc_node, "value", None), 

777 parent=self._instance, 

778 ) 

779 

780 

781class GeneratorModel(GeneratorBaseModel): 

782 def __init__(self): 

783 super().__init__(AstroidManager().builtins_module["generator"]) 

784 

785 

786class AsyncGeneratorModel(GeneratorBaseModel): 

787 def __init__(self): 

788 super().__init__(AstroidManager().builtins_module["async_generator"]) 

789 

790 

791class InstanceModel(ObjectModel): 

792 @property 

793 def attr___class__(self): 

794 return self._instance._proxied 

795 

796 @property 

797 def attr___module__(self): 

798 return node_classes.Const(self._instance.root().qname()) 

799 

800 @property 

801 def attr___doc__(self): 

802 return node_classes.Const(getattr(self._instance.doc_node, "value", None)) 

803 

804 @property 

805 def attr___dict__(self): 

806 return _dunder_dict(self._instance, self._instance.instance_attrs) 

807 

808 

809# Exception instances 

810 

811 

812class ExceptionInstanceModel(InstanceModel): 

813 @property 

814 def attr_args(self) -> nodes.Tuple: 

815 return nodes.Tuple(parent=self._instance) 

816 

817 @property 

818 def attr___traceback__(self): 

819 builtins_ast_module = AstroidManager().builtins_module 

820 traceback_type = builtins_ast_module[types.TracebackType.__name__] 

821 return traceback_type.instantiate_class() 

822 

823 

824class SyntaxErrorInstanceModel(ExceptionInstanceModel): 

825 @property 

826 def attr_text(self): 

827 return node_classes.Const("") 

828 

829 

830class GroupExceptionInstanceModel(ExceptionInstanceModel): 

831 @property 

832 def attr_exceptions(self) -> nodes.Tuple: 

833 return node_classes.Tuple(parent=self._instance) 

834 

835 

836class OSErrorInstanceModel(ExceptionInstanceModel): 

837 @property 

838 def attr_filename(self): 

839 return node_classes.Const("") 

840 

841 @property 

842 def attr_errno(self): 

843 return node_classes.Const(0) 

844 

845 @property 

846 def attr_strerror(self): 

847 return node_classes.Const("") 

848 

849 attr_filename2 = attr_filename 

850 

851 

852class ImportErrorInstanceModel(ExceptionInstanceModel): 

853 @property 

854 def attr_name(self): 

855 return node_classes.Const("") 

856 

857 @property 

858 def attr_path(self): 

859 return node_classes.Const("") 

860 

861 

862class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel): 

863 @property 

864 def attr_object(self): 

865 return node_classes.Const(b"") 

866 

867 

868BUILTIN_EXCEPTIONS = { 

869 "builtins.SyntaxError": SyntaxErrorInstanceModel, 

870 "builtins.ExceptionGroup": GroupExceptionInstanceModel, 

871 "builtins.ImportError": ImportErrorInstanceModel, 

872 "builtins.UnicodeDecodeError": UnicodeDecodeErrorInstanceModel, 

873 # These are all similar to OSError in terms of attributes 

874 "builtins.OSError": OSErrorInstanceModel, 

875 "builtins.BlockingIOError": OSErrorInstanceModel, 

876 "builtins.BrokenPipeError": OSErrorInstanceModel, 

877 "builtins.ChildProcessError": OSErrorInstanceModel, 

878 "builtins.ConnectionAbortedError": OSErrorInstanceModel, 

879 "builtins.ConnectionError": OSErrorInstanceModel, 

880 "builtins.ConnectionRefusedError": OSErrorInstanceModel, 

881 "builtins.ConnectionResetError": OSErrorInstanceModel, 

882 "builtins.FileExistsError": OSErrorInstanceModel, 

883 "builtins.FileNotFoundError": OSErrorInstanceModel, 

884 "builtins.InterruptedError": OSErrorInstanceModel, 

885 "builtins.IsADirectoryError": OSErrorInstanceModel, 

886 "builtins.NotADirectoryError": OSErrorInstanceModel, 

887 "builtins.PermissionError": OSErrorInstanceModel, 

888 "builtins.ProcessLookupError": OSErrorInstanceModel, 

889 "builtins.TimeoutError": OSErrorInstanceModel, 

890} 

891 

892 

893class DictModel(ObjectModel): 

894 @property 

895 def attr___class__(self): 

896 return self._instance._proxied 

897 

898 def _generic_dict_attribute(self, obj, name): 

899 """Generate a bound method that can infer the given *obj*.""" 

900 

901 class DictMethodBoundMethod(astroid.BoundMethod): 

902 def infer_call_result( 

903 self, 

904 caller: SuccessfulInferenceResult | None, 

905 context: InferenceContext | None = None, 

906 ) -> Iterator[InferenceResult]: 

907 yield obj 

908 

909 meth = next(self._instance._proxied.igetattr(name), None) 

910 return DictMethodBoundMethod(proxy=meth, bound=self._instance) 

911 

912 @property 

913 def attr_items(self): 

914 from astroid import objects # pylint: disable=import-outside-toplevel 

915 

916 elems = [] 

917 obj = node_classes.List(parent=self._instance) 

918 for key, value in self._instance.items: 

919 elem = node_classes.Tuple(parent=obj) 

920 elem.postinit((key, value)) 

921 elems.append(elem) 

922 obj.postinit(elts=elems) 

923 

924 items_obj = objects.DictItems(obj) 

925 return self._generic_dict_attribute(items_obj, "items") 

926 

927 @property 

928 def attr_keys(self): 

929 from astroid import objects # pylint: disable=import-outside-toplevel 

930 

931 keys = [key for (key, _) in self._instance.items] 

932 obj = node_classes.List(parent=self._instance) 

933 obj.postinit(elts=keys) 

934 

935 keys_obj = objects.DictKeys(obj) 

936 return self._generic_dict_attribute(keys_obj, "keys") 

937 

938 @property 

939 def attr_values(self): 

940 from astroid import objects # pylint: disable=import-outside-toplevel 

941 

942 values = [value for (_, value) in self._instance.items] 

943 obj = node_classes.List(parent=self._instance) 

944 obj.postinit(values) 

945 

946 values_obj = objects.DictValues(obj) 

947 return self._generic_dict_attribute(values_obj, "values") 

948 

949 

950class PropertyModel(ObjectModel): 

951 """Model for a builtin property.""" 

952 

953 def _init_function(self, name): 

954 function = nodes.FunctionDef( 

955 name=name, 

956 parent=self._instance, 

957 lineno=self._instance.lineno, 

958 col_offset=self._instance.col_offset, 

959 end_lineno=self._instance.end_lineno, 

960 end_col_offset=self._instance.end_col_offset, 

961 ) 

962 

963 args = nodes.Arguments(parent=function, vararg=None, kwarg=None) 

964 args.postinit( 

965 args=[], 

966 defaults=[], 

967 kwonlyargs=[], 

968 kw_defaults=[], 

969 annotations=[], 

970 posonlyargs=[], 

971 posonlyargs_annotations=[], 

972 kwonlyargs_annotations=[], 

973 ) 

974 

975 function.postinit(args=args, body=[]) 

976 return function 

977 

978 @property 

979 def attr_fget(self): 

980 func = self._instance 

981 

982 class PropertyFuncAccessor(nodes.FunctionDef): 

983 def infer_call_result( 

984 self, 

985 caller: SuccessfulInferenceResult | None, 

986 context: InferenceContext | None = None, 

987 ) -> Iterator[InferenceResult]: 

988 nonlocal func 

989 if caller and len(caller.args) != 1: 

990 raise InferenceError( 

991 "fget() needs a single argument", target=self, context=context 

992 ) 

993 

994 yield from func.function.infer_call_result( 

995 caller=caller, context=context 

996 ) 

997 

998 property_accessor = PropertyFuncAccessor( 

999 name="fget", 

1000 parent=self._instance, 

1001 lineno=self._instance.lineno, 

1002 col_offset=self._instance.col_offset, 

1003 end_lineno=self._instance.end_lineno, 

1004 end_col_offset=self._instance.end_col_offset, 

1005 ) 

1006 property_accessor.postinit(args=func.args, body=func.body) 

1007 return property_accessor 

1008 

1009 @property 

1010 def attr_fset(self): 

1011 func = self._instance 

1012 

1013 def find_setter(func: Property) -> nodes.FunctionDef | None: 

1014 """ 

1015 Given a property, find the corresponding setter function and returns it. 

1016 

1017 :param func: property for which the setter has to be found 

1018 :return: the setter function or None 

1019 """ 

1020 for node in func.parent.body: 

1021 if isinstance(node, (nodes.FunctionDef, nodes.AsyncFunctionDef)): 

1022 for dec_name in node.decoratornames(): 

1023 if dec_name.endswith(func.function.name + ".setter"): 

1024 return node 

1025 return None 

1026 

1027 func_setter = find_setter(func) 

1028 if not func_setter: 

1029 raise InferenceError( 

1030 f"Unable to find the setter of property {func.function.name}" 

1031 ) 

1032 

1033 class PropertyFuncAccessor(nodes.FunctionDef): 

1034 def infer_call_result( 

1035 self, 

1036 caller: SuccessfulInferenceResult | None, 

1037 context: InferenceContext | None = None, 

1038 ) -> Iterator[InferenceResult]: 

1039 nonlocal func_setter 

1040 if caller and len(caller.args) != 2: 

1041 raise InferenceError( 

1042 "fset() needs two arguments", target=self, context=context 

1043 ) 

1044 yield from func_setter.infer_call_result(caller=caller, context=context) 

1045 

1046 property_accessor = PropertyFuncAccessor( 

1047 name="fset", 

1048 parent=self._instance, 

1049 lineno=self._instance.lineno, 

1050 col_offset=self._instance.col_offset, 

1051 end_lineno=self._instance.end_lineno, 

1052 end_col_offset=self._instance.end_col_offset, 

1053 ) 

1054 property_accessor.postinit(args=func_setter.args, body=func_setter.body) 

1055 return property_accessor 

1056 

1057 @property 

1058 def attr_setter(self): 

1059 return self._init_function("setter") 

1060 

1061 @property 

1062 def attr_deleter(self): 

1063 return self._init_function("deleter") 

1064 

1065 @property 

1066 def attr_getter(self): 

1067 return self._init_function("getter") 

1068 

1069 # pylint: enable=import-outside-toplevel