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

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

508 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 pprint 

29import types 

30from collections.abc import Iterator 

31from functools import lru_cache 

32from typing import TYPE_CHECKING, Any, Literal 

33 

34import astroid 

35from astroid import bases, nodes, util 

36from astroid.context import InferenceContext, copy_context 

37from astroid.exceptions import AttributeInferenceError, InferenceError, NoDefault 

38from astroid.manager import AstroidManager 

39from astroid.nodes import node_classes 

40from astroid.typing import InferenceResult, SuccessfulInferenceResult 

41 

42if TYPE_CHECKING: 

43 from astroid.objects import Property 

44 

45IMPL_PREFIX = "attr_" 

46LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX) 

47 

48 

49def _dunder_dict(instance, attributes): 

50 obj = node_classes.Dict( 

51 parent=instance, 

52 lineno=instance.lineno, 

53 col_offset=instance.col_offset, 

54 end_lineno=instance.end_lineno, 

55 end_col_offset=instance.end_col_offset, 

56 ) 

57 

58 # Convert the keys to node strings 

59 keys = [ 

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

61 ] 

62 

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

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

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

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

67 

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

69 return obj 

70 

71 

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

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

74 try: 

75 return model._instance._proxied 

76 except AttributeError: 

77 return model._instance 

78 

79 

80class ObjectModel: 

81 def __init__(self): 

82 self._instance = None 

83 

84 def __repr__(self): 

85 result = [] 

86 cname = type(self).__name__ 

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

88 alignment = len(cname) + 1 

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

90 width = 80 - len(field) - alignment 

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

92 

93 inner = [lines[0]] 

94 for line in lines[1:]: 

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

96 result.append(field) 

97 

98 return string % { 

99 "cname": cname, 

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

101 } 

102 

103 def __call__(self, instance): 

104 self._instance = instance 

105 return self 

106 

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

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

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

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

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

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

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

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

115 return self(instance) 

116 

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

118 return name in self.attributes() 

119 

120 @lru_cache # noqa 

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

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

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

124 

125 def lookup(self, name): 

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

127 

128 It should return an AST or an interpreter object, 

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

130 """ 

131 if name in self.attributes(): 

132 return getattr(self, IMPL_PREFIX + name) 

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

134 

135 @property 

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

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

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

139 

140 node: nodes.FunctionDef = builder.extract_node( 

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

142 ) 

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

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

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

146 

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

148 

149 @property 

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

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

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

153 

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

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

156 # This BoundMethod is the fallback value for those. 

157 node: nodes.FunctionDef = builder.extract_node( 

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

159 ) 

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

161 # is where this method originally comes from 

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

163 

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

165 

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

167 @property 

168 def attr___ne__(self): 

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

170 

171 attr___class__ = attr___ne__ 

172 attr___delattr__ = attr___ne__ 

173 attr___dir__ = attr___ne__ 

174 attr___doc__ = attr___ne__ 

175 attr___eq__ = attr___ne__ 

176 attr___format__ = attr___ne__ 

177 attr___ge__ = attr___ne__ 

178 attr___getattribute__ = attr___ne__ 

179 attr___getstate__ = attr___ne__ 

180 attr___gt__ = attr___ne__ 

181 attr___hash__ = attr___ne__ 

182 attr___init_subclass__ = attr___ne__ 

183 attr___le__ = attr___ne__ 

184 attr___lt__ = attr___ne__ 

185 attr___reduce__ = attr___ne__ 

186 attr___reduce_ex__ = attr___ne__ 

187 attr___repr__ = attr___ne__ 

188 attr___setattr__ = attr___ne__ 

189 attr___sizeof__ = attr___ne__ 

190 attr___str__ = attr___ne__ 

191 attr___subclasshook__ = attr___ne__ 

192 

193 

194class ModuleModel(ObjectModel): 

195 def _builtins(self): 

196 builtins_ast_module = AstroidManager().builtins_module 

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

198 

199 @property 

200 def attr_builtins(self): 

201 return self._builtins() 

202 

203 @property 

204 def attr___path__(self): 

205 if not self._instance.package: 

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

207 

208 path_objs = [ 

209 node_classes.Const( 

210 value=( 

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

212 ), 

213 parent=self._instance, 

214 ) 

215 for path in self._instance.path 

216 ] 

217 

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

219 container.postinit(path_objs) 

220 

221 return container 

222 

223 @property 

224 def attr___name__(self): 

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

226 

227 @property 

228 def attr___doc__(self): 

229 return node_classes.Const( 

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

231 parent=self._instance, 

232 ) 

233 

234 @property 

235 def attr___file__(self): 

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

237 

238 @property 

239 def attr___dict__(self): 

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

241 

242 @property 

243 def attr___package__(self): 

244 if not self._instance.package: 

245 value = "" 

246 else: 

247 value = self._instance.name 

248 

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

250 

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

252 # import system, 

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

254 

255 @property 

256 def attr___spec__(self): 

257 # No handling for now. 

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

259 

260 @property 

261 def attr___loader__(self): 

262 # No handling for now. 

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

264 

265 @property 

266 def attr___cached__(self): 

267 # No handling for now. 

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

269 

270 

271class FunctionModel(ObjectModel): 

272 @property 

273 def attr___name__(self): 

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

275 

276 @property 

277 def attr___doc__(self): 

278 return node_classes.Const( 

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

280 parent=self._instance, 

281 ) 

282 

283 @property 

284 def attr___qualname__(self): 

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

286 

287 @property 

288 def attr___defaults__(self): 

289 func = self._instance 

290 if not func.args.defaults: 

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

292 

293 defaults_obj = node_classes.Tuple(parent=func) 

294 defaults_obj.postinit(func.args.defaults) 

295 return defaults_obj 

296 

297 @property 

298 def attr___annotations__(self): 

299 obj = node_classes.Dict( 

300 parent=self._instance, 

301 lineno=self._instance.lineno, 

302 col_offset=self._instance.col_offset, 

303 end_lineno=self._instance.end_lineno, 

304 end_col_offset=self._instance.end_col_offset, 

305 ) 

306 

307 if not self._instance.returns: 

308 returns = None 

309 else: 

310 returns = self._instance.returns 

311 

312 args = self._instance.args 

313 pair_annotations = itertools.chain( 

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

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

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

317 ) 

318 

319 annotations = { 

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

321 } 

322 if args.varargannotation: 

323 annotations[args.vararg] = args.varargannotation 

324 if args.kwargannotation: 

325 annotations[args.kwarg] = args.kwargannotation 

326 if returns: 

327 annotations["return"] = returns 

328 

329 items = [ 

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

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

332 ] 

333 

334 obj.postinit(items) 

335 return obj 

336 

337 @property 

338 def attr___dict__(self): 

339 return node_classes.Dict( 

340 parent=self._instance, 

341 lineno=self._instance.lineno, 

342 col_offset=self._instance.col_offset, 

343 end_lineno=self._instance.end_lineno, 

344 end_col_offset=self._instance.end_col_offset, 

345 ) 

346 

347 attr___globals__ = attr___dict__ 

348 

349 @property 

350 def attr___kwdefaults__(self): 

351 def _default_args(args, parent): 

352 for arg in args.kwonlyargs: 

353 try: 

354 default = args.default_value(arg.name) 

355 except NoDefault: 

356 continue 

357 

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

359 yield name, default 

360 

361 args = self._instance.args 

362 obj = node_classes.Dict( 

363 parent=self._instance, 

364 lineno=self._instance.lineno, 

365 col_offset=self._instance.col_offset, 

366 end_lineno=self._instance.end_lineno, 

367 end_col_offset=self._instance.end_col_offset, 

368 ) 

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

370 

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

372 return obj 

373 

374 @property 

375 def attr___module__(self): 

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

377 

378 @property 

379 def attr___get__(self): 

380 func = self._instance 

381 

382 class DescriptorBoundMethod(bases.BoundMethod): 

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

384 binding. 

385 """ 

386 

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

388 # Different than BoundMethod since the signature 

389 # is different. 

390 return 0 

391 

392 def infer_call_result( 

393 self, 

394 caller: SuccessfulInferenceResult | None, 

395 context: InferenceContext | None = None, 

396 ) -> Iterator[bases.BoundMethod]: 

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

398 raise InferenceError( 

399 "Invalid arguments for descriptor binding", 

400 target=self, 

401 context=context, 

402 ) 

403 

404 context = copy_context(context) 

405 try: 

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

407 except StopIteration as e: 

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

409 

410 if isinstance(cls, util.UninferableBase): 

411 raise InferenceError( 

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

413 ) 

414 

415 # For some reason func is a Node that the below 

416 # code is not expecting 

417 if isinstance(func, bases.BoundMethod): 

418 yield func 

419 return 

420 

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

422 # class where it will be bound. 

423 new_func = func.__class__( 

424 name=func.name, 

425 lineno=func.lineno, 

426 col_offset=func.col_offset, 

427 parent=func.parent, 

428 end_lineno=func.end_lineno, 

429 end_col_offset=func.end_col_offset, 

430 ) 

431 # pylint: disable=no-member 

432 new_func.postinit( 

433 func.args, 

434 func.body, 

435 func.decorators, 

436 func.returns, 

437 doc_node=func.doc_node, 

438 ) 

439 

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

441 proxy = bases.UnboundMethod(new_func) 

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

443 

444 @property 

445 def args(self): 

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

447 

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

449 

450 def test(self): 

451 pass 

452 

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

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

455 """ 

456 nonlocal func 

457 arguments = nodes.Arguments( 

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

459 ) 

460 

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

462 positional_or_keyword_params.append( 

463 nodes.AssignName( 

464 name="type", 

465 lineno=0, 

466 col_offset=0, 

467 parent=arguments, 

468 end_lineno=None, 

469 end_col_offset=None, 

470 ) 

471 ) 

472 

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

474 

475 arguments.postinit( 

476 args=positional_or_keyword_params, 

477 posonlyargs=positional_only_params, 

478 defaults=[], 

479 kwonlyargs=[], 

480 kw_defaults=[], 

481 annotations=[], 

482 kwonlyargs_annotations=[], 

483 posonlyargs_annotations=[], 

484 ) 

485 return arguments 

486 

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

488 

489 # Function-specific attributes. 

490 @property 

491 def attr___call__(self): 

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

493 

494 attr___builtins__ = attr___call__ 

495 attr___closure__ = attr___call__ 

496 attr___code__ = attr___call__ 

497 attr___type_params__ = attr___call__ 

498 

499 

500class ClassModel(ObjectModel): 

501 def __init__(self): 

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

503 self.context = InferenceContext() 

504 

505 super().__init__() 

506 

507 @property 

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

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

510 

511 @property 

512 def attr___module__(self): 

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

514 

515 @property 

516 def attr___name__(self): 

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

518 

519 @property 

520 def attr___qualname__(self): 

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

522 

523 @property 

524 def attr___doc__(self): 

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

526 

527 @property 

528 def attr___mro__(self): 

529 mro = self._instance.mro() 

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

531 obj.postinit(mro) 

532 return obj 

533 

534 @property 

535 def attr_mro(self): 

536 other_self = self 

537 

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

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

540 class MroBoundMethod(bases.BoundMethod): 

541 def infer_call_result( 

542 self, 

543 caller: SuccessfulInferenceResult | None, 

544 context: InferenceContext | None = None, 

545 ) -> Iterator[node_classes.Tuple]: 

546 yield other_self.attr___mro__ 

547 

548 implicit_metaclass = self._instance.implicit_metaclass() 

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

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

551 

552 @property 

553 def attr___bases__(self): 

554 obj = node_classes.Tuple() 

555 context = InferenceContext() 

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

557 obj.postinit(elts=elts) 

558 return obj 

559 

560 @property 

561 def attr___class__(self): 

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

563 from astroid import helpers 

564 

565 return helpers.object_type(self._instance) 

566 

567 @property 

568 def attr___subclasses__(self): 

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

570 

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

572 thus it might miss a couple of them. 

573 """ 

574 

575 qname = self._instance.qname() 

576 root = self._instance.root() 

577 classes = [ 

578 cls 

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

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

581 ] 

582 

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

584 obj.postinit(classes) 

585 

586 class SubclassesBoundMethod(bases.BoundMethod): 

587 def infer_call_result( 

588 self, 

589 caller: SuccessfulInferenceResult | None, 

590 context: InferenceContext | None = None, 

591 ) -> Iterator[node_classes.List]: 

592 yield obj 

593 

594 implicit_metaclass = self._instance.implicit_metaclass() 

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

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

597 

598 @property 

599 def attr___dict__(self): 

600 return node_classes.Dict( 

601 parent=self._instance, 

602 lineno=self._instance.lineno, 

603 col_offset=self._instance.col_offset, 

604 end_lineno=self._instance.end_lineno, 

605 end_col_offset=self._instance.end_col_offset, 

606 ) 

607 

608 @property 

609 def attr___call__(self): 

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

611 return self._instance.instantiate_class() 

612 

613 

614class SuperModel(ObjectModel): 

615 @property 

616 def attr___thisclass__(self): 

617 return self._instance.mro_pointer 

618 

619 @property 

620 def attr___self_class__(self): 

621 return self._instance._self_class 

622 

623 @property 

624 def attr___self__(self): 

625 return self._instance.type 

626 

627 @property 

628 def attr___class__(self): 

629 return self._instance._proxied 

630 

631 

632class UnboundMethodModel(ObjectModel): 

633 @property 

634 def attr___class__(self): 

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

636 from astroid import helpers 

637 

638 return helpers.object_type(self._instance) 

639 

640 @property 

641 def attr___func__(self): 

642 return self._instance._proxied 

643 

644 @property 

645 def attr___self__(self): 

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

647 

648 attr_im_func = attr___func__ 

649 attr_im_class = attr___class__ 

650 attr_im_self = attr___self__ 

651 

652 

653class ContextManagerModel(ObjectModel): 

654 """Model for context managers. 

655 

656 Based on 3.3.9 of the Data Model documentation: 

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

658 """ 

659 

660 @property 

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

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

663 

664 As per Python documentation: 

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

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

667 as clause of the statement, if any. 

668 """ 

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

670 

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

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

673 # is where this method originally comes from 

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

675 

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

677 

678 @property 

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

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

681 

682 As per Python documentation: 

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

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

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

686 """ 

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

688 

689 node: nodes.FunctionDef = builder.extract_node( 

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

691 ) 

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

693 # is where this method originally comes from 

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

695 

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

697 

698 

699class BoundMethodModel(FunctionModel): 

700 @property 

701 def attr___func__(self): 

702 return self._instance._proxied._proxied 

703 

704 @property 

705 def attr___self__(self): 

706 return self._instance.bound 

707 

708 

709class GeneratorBaseModel(FunctionModel, ContextManagerModel): 

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

711 super().__init__() 

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

713 method = values[0] 

714 if isinstance(method, nodes.FunctionDef): 

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

716 

717 def patched(cls, meth=method): 

718 return meth 

719 

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

721 

722 @property 

723 def attr___name__(self): 

724 return node_classes.Const( 

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

726 ) 

727 

728 @property 

729 def attr___doc__(self): 

730 return node_classes.Const( 

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

732 parent=self._instance, 

733 ) 

734 

735 

736class GeneratorModel(GeneratorBaseModel): 

737 def __init__(self): 

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

739 

740 

741class AsyncGeneratorModel(GeneratorBaseModel): 

742 def __init__(self): 

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

744 

745 

746class InstanceModel(ObjectModel): 

747 @property 

748 def attr___class__(self): 

749 return self._instance._proxied 

750 

751 @property 

752 def attr___module__(self): 

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

754 

755 @property 

756 def attr___doc__(self): 

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

758 

759 @property 

760 def attr___dict__(self): 

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

762 

763 

764# Exception instances 

765 

766 

767class ExceptionInstanceModel(InstanceModel): 

768 @property 

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

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

771 

772 @property 

773 def attr___traceback__(self): 

774 builtins_ast_module = AstroidManager().builtins_module 

775 traceback_type = builtins_ast_module[types.TracebackType.__name__] 

776 return traceback_type.instantiate_class() 

777 

778 

779class SyntaxErrorInstanceModel(ExceptionInstanceModel): 

780 @property 

781 def attr_text(self): 

782 return node_classes.Const("") 

783 

784 

785class GroupExceptionInstanceModel(ExceptionInstanceModel): 

786 @property 

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

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

789 

790 

791class OSErrorInstanceModel(ExceptionInstanceModel): 

792 @property 

793 def attr_filename(self): 

794 return node_classes.Const("") 

795 

796 @property 

797 def attr_errno(self): 

798 return node_classes.Const(0) 

799 

800 @property 

801 def attr_strerror(self): 

802 return node_classes.Const("") 

803 

804 attr_filename2 = attr_filename 

805 

806 

807class ImportErrorInstanceModel(ExceptionInstanceModel): 

808 @property 

809 def attr_name(self): 

810 return node_classes.Const("") 

811 

812 @property 

813 def attr_path(self): 

814 return node_classes.Const("") 

815 

816 

817class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel): 

818 @property 

819 def attr_object(self): 

820 return node_classes.Const(b"") 

821 

822 

823BUILTIN_EXCEPTIONS = { 

824 "builtins.SyntaxError": SyntaxErrorInstanceModel, 

825 "builtins.ExceptionGroup": GroupExceptionInstanceModel, 

826 "builtins.ImportError": ImportErrorInstanceModel, 

827 "builtins.UnicodeDecodeError": UnicodeDecodeErrorInstanceModel, 

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

829 "builtins.OSError": OSErrorInstanceModel, 

830 "builtins.BlockingIOError": OSErrorInstanceModel, 

831 "builtins.BrokenPipeError": OSErrorInstanceModel, 

832 "builtins.ChildProcessError": OSErrorInstanceModel, 

833 "builtins.ConnectionAbortedError": OSErrorInstanceModel, 

834 "builtins.ConnectionError": OSErrorInstanceModel, 

835 "builtins.ConnectionRefusedError": OSErrorInstanceModel, 

836 "builtins.ConnectionResetError": OSErrorInstanceModel, 

837 "builtins.FileExistsError": OSErrorInstanceModel, 

838 "builtins.FileNotFoundError": OSErrorInstanceModel, 

839 "builtins.InterruptedError": OSErrorInstanceModel, 

840 "builtins.IsADirectoryError": OSErrorInstanceModel, 

841 "builtins.NotADirectoryError": OSErrorInstanceModel, 

842 "builtins.PermissionError": OSErrorInstanceModel, 

843 "builtins.ProcessLookupError": OSErrorInstanceModel, 

844 "builtins.TimeoutError": OSErrorInstanceModel, 

845} 

846 

847 

848class DictModel(ObjectModel): 

849 @property 

850 def attr___class__(self): 

851 return self._instance._proxied 

852 

853 def _generic_dict_attribute(self, obj, name): 

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

855 

856 class DictMethodBoundMethod(astroid.BoundMethod): 

857 def infer_call_result( 

858 self, 

859 caller: SuccessfulInferenceResult | None, 

860 context: InferenceContext | None = None, 

861 ) -> Iterator[InferenceResult]: 

862 yield obj 

863 

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

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

866 

867 @property 

868 def attr_items(self): 

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

870 

871 elems = [] 

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

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

874 elem = node_classes.Tuple(parent=obj) 

875 elem.postinit((key, value)) 

876 elems.append(elem) 

877 obj.postinit(elts=elems) 

878 

879 items_obj = objects.DictItems(obj) 

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

881 

882 @property 

883 def attr_keys(self): 

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

885 

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

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

888 obj.postinit(elts=keys) 

889 

890 keys_obj = objects.DictKeys(obj) 

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

892 

893 @property 

894 def attr_values(self): 

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

896 

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

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

899 obj.postinit(values) 

900 

901 values_obj = objects.DictValues(obj) 

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

903 

904 

905class PropertyModel(ObjectModel): 

906 """Model for a builtin property.""" 

907 

908 def _init_function(self, name): 

909 function = nodes.FunctionDef( 

910 name=name, 

911 parent=self._instance, 

912 lineno=self._instance.lineno, 

913 col_offset=self._instance.col_offset, 

914 end_lineno=self._instance.end_lineno, 

915 end_col_offset=self._instance.end_col_offset, 

916 ) 

917 

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

919 args.postinit( 

920 args=[], 

921 defaults=[], 

922 kwonlyargs=[], 

923 kw_defaults=[], 

924 annotations=[], 

925 posonlyargs=[], 

926 posonlyargs_annotations=[], 

927 kwonlyargs_annotations=[], 

928 ) 

929 

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

931 return function 

932 

933 @property 

934 def attr_fget(self): 

935 func = self._instance 

936 

937 class PropertyFuncAccessor(nodes.FunctionDef): 

938 def infer_call_result( 

939 self, 

940 caller: SuccessfulInferenceResult | None, 

941 context: InferenceContext | None = None, 

942 ) -> Iterator[InferenceResult]: 

943 nonlocal func 

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

945 raise InferenceError( 

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

947 ) 

948 

949 yield from func.function.infer_call_result( 

950 caller=caller, context=context 

951 ) 

952 

953 property_accessor = PropertyFuncAccessor( 

954 name="fget", 

955 parent=self._instance, 

956 lineno=self._instance.lineno, 

957 col_offset=self._instance.col_offset, 

958 end_lineno=self._instance.end_lineno, 

959 end_col_offset=self._instance.end_col_offset, 

960 ) 

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

962 return property_accessor 

963 

964 @property 

965 def attr_fset(self): 

966 func = self._instance 

967 

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

969 """ 

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

971 

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

973 :return: the setter function or None 

974 """ 

975 for target in [ 

976 t for t in func.parent.get_children() if t.name == func.function.name 

977 ]: 

978 for dec_name in target.decoratornames(): 

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

980 return target 

981 return None 

982 

983 func_setter = find_setter(func) 

984 if not func_setter: 

985 raise InferenceError( 

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

987 ) 

988 

989 class PropertyFuncAccessor(nodes.FunctionDef): 

990 def infer_call_result( 

991 self, 

992 caller: SuccessfulInferenceResult | None, 

993 context: InferenceContext | None = None, 

994 ) -> Iterator[InferenceResult]: 

995 nonlocal func_setter 

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

997 raise InferenceError( 

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

999 ) 

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

1001 

1002 property_accessor = PropertyFuncAccessor( 

1003 name="fset", 

1004 parent=self._instance, 

1005 lineno=self._instance.lineno, 

1006 col_offset=self._instance.col_offset, 

1007 end_lineno=self._instance.end_lineno, 

1008 end_col_offset=self._instance.end_col_offset, 

1009 ) 

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

1011 return property_accessor 

1012 

1013 @property 

1014 def attr_setter(self): 

1015 return self._init_function("setter") 

1016 

1017 @property 

1018 def attr_deleter(self): 

1019 return self._init_function("deleter") 

1020 

1021 @property 

1022 def attr_getter(self): 

1023 return self._init_function("getter") 

1024 

1025 # pylint: enable=import-outside-toplevel