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

504 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:53 +0000

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 

167class ModuleModel(ObjectModel): 

168 def _builtins(self): 

169 builtins_ast_module = AstroidManager().builtins_module 

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

171 

172 @property 

173 def attr_builtins(self): 

174 return self._builtins() 

175 

176 @property 

177 def attr___path__(self): 

178 if not self._instance.package: 

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

180 

181 path_objs = [ 

182 node_classes.Const( 

183 value=path 

184 if not path.endswith("__init__.py") 

185 else os.path.dirname(path), 

186 parent=self._instance, 

187 ) 

188 for path in self._instance.path 

189 ] 

190 

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

192 container.postinit(path_objs) 

193 

194 return container 

195 

196 @property 

197 def attr___name__(self): 

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

199 

200 @property 

201 def attr___doc__(self): 

202 return node_classes.Const( 

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

204 parent=self._instance, 

205 ) 

206 

207 @property 

208 def attr___file__(self): 

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

210 

211 @property 

212 def attr___dict__(self): 

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

214 

215 @property 

216 def attr___package__(self): 

217 if not self._instance.package: 

218 value = "" 

219 else: 

220 value = self._instance.name 

221 

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

223 

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

225 # import system, 

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

227 

228 @property 

229 def attr___spec__(self): 

230 # No handling for now. 

231 return node_classes.Unknown() 

232 

233 @property 

234 def attr___loader__(self): 

235 # No handling for now. 

236 return node_classes.Unknown() 

237 

238 @property 

239 def attr___cached__(self): 

240 # No handling for now. 

241 return node_classes.Unknown() 

242 

243 

244class FunctionModel(ObjectModel): 

245 @property 

246 def attr___name__(self): 

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

248 

249 @property 

250 def attr___doc__(self): 

251 return node_classes.Const( 

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

253 parent=self._instance, 

254 ) 

255 

256 @property 

257 def attr___qualname__(self): 

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

259 

260 @property 

261 def attr___defaults__(self): 

262 func = self._instance 

263 if not func.args.defaults: 

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

265 

266 defaults_obj = node_classes.Tuple(parent=func) 

267 defaults_obj.postinit(func.args.defaults) 

268 return defaults_obj 

269 

270 @property 

271 def attr___annotations__(self): 

272 obj = node_classes.Dict( 

273 parent=self._instance, 

274 lineno=self._instance.lineno, 

275 col_offset=self._instance.col_offset, 

276 end_lineno=self._instance.end_lineno, 

277 end_col_offset=self._instance.end_col_offset, 

278 ) 

279 

280 if not self._instance.returns: 

281 returns = None 

282 else: 

283 returns = self._instance.returns 

284 

285 args = self._instance.args 

286 pair_annotations = itertools.chain( 

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

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

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

290 ) 

291 

292 annotations = { 

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

294 } 

295 if args.varargannotation: 

296 annotations[args.vararg] = args.varargannotation 

297 if args.kwargannotation: 

298 annotations[args.kwarg] = args.kwargannotation 

299 if returns: 

300 annotations["return"] = returns 

301 

302 items = [ 

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

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

305 ] 

306 

307 obj.postinit(items) 

308 return obj 

309 

310 @property 

311 def attr___dict__(self): 

312 return node_classes.Dict( 

313 parent=self._instance, 

314 lineno=self._instance.lineno, 

315 col_offset=self._instance.col_offset, 

316 end_lineno=self._instance.end_lineno, 

317 end_col_offset=self._instance.end_col_offset, 

318 ) 

319 

320 attr___globals__ = attr___dict__ 

321 

322 @property 

323 def attr___kwdefaults__(self): 

324 def _default_args(args, parent): 

325 for arg in args.kwonlyargs: 

326 try: 

327 default = args.default_value(arg.name) 

328 except NoDefault: 

329 continue 

330 

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

332 yield name, default 

333 

334 args = self._instance.args 

335 obj = node_classes.Dict( 

336 parent=self._instance, 

337 lineno=self._instance.lineno, 

338 col_offset=self._instance.col_offset, 

339 end_lineno=self._instance.end_lineno, 

340 end_col_offset=self._instance.end_col_offset, 

341 ) 

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

343 

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

345 return obj 

346 

347 @property 

348 def attr___module__(self): 

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

350 

351 @property 

352 def attr___get__(self): 

353 func = self._instance 

354 

355 class DescriptorBoundMethod(bases.BoundMethod): 

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

357 binding. 

358 """ 

359 

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

361 # Different than BoundMethod since the signature 

362 # is different. 

363 return 0 

364 

365 def infer_call_result( 

366 self, 

367 caller: SuccessfulInferenceResult | None, 

368 context: InferenceContext | None = None, 

369 ) -> Iterator[bases.BoundMethod]: 

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

371 raise InferenceError( 

372 "Invalid arguments for descriptor binding", 

373 target=self, 

374 context=context, 

375 ) 

376 

377 context = copy_context(context) 

378 try: 

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

380 except StopIteration as e: 

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

382 

383 if isinstance(cls, util.UninferableBase): 

384 raise InferenceError( 

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

386 ) 

387 

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

389 # code is not expecting 

390 if isinstance(func, bases.BoundMethod): 

391 yield func 

392 return 

393 

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

395 # class where it will be bound. 

396 new_func = func.__class__( 

397 name=func.name, 

398 lineno=func.lineno, 

399 col_offset=func.col_offset, 

400 parent=func.parent, 

401 end_lineno=func.end_lineno, 

402 end_col_offset=func.end_col_offset, 

403 ) 

404 # pylint: disable=no-member 

405 new_func.postinit( 

406 func.args, 

407 func.body, 

408 func.decorators, 

409 func.returns, 

410 doc_node=func.doc_node, 

411 ) 

412 

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

414 proxy = bases.UnboundMethod(new_func) 

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

416 

417 @property 

418 def args(self): 

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

420 

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

422 

423 def test(self): 

424 pass 

425 

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

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

428 """ 

429 nonlocal func 

430 arguments = astroid.Arguments( 

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

432 ) 

433 

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

435 positional_or_keyword_params.append( 

436 astroid.AssignName( 

437 name="type", 

438 lineno=0, 

439 col_offset=0, 

440 parent=arguments, 

441 end_lineno=None, 

442 end_col_offset=None, 

443 ) 

444 ) 

445 

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

447 

448 arguments.postinit( 

449 args=positional_or_keyword_params, 

450 posonlyargs=positional_only_params, 

451 defaults=[], 

452 kwonlyargs=[], 

453 kw_defaults=[], 

454 annotations=[], 

455 kwonlyargs_annotations=[], 

456 posonlyargs_annotations=[], 

457 ) 

458 return arguments 

459 

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

461 

462 # These are here just for completion. 

463 @property 

464 def attr___ne__(self): 

465 return node_classes.Unknown() 

466 

467 attr___subclasshook__ = attr___ne__ 

468 attr___str__ = attr___ne__ 

469 attr___sizeof__ = attr___ne__ 

470 attr___setattr___ = attr___ne__ 

471 attr___repr__ = attr___ne__ 

472 attr___reduce__ = attr___ne__ 

473 attr___reduce_ex__ = attr___ne__ 

474 attr___lt__ = attr___ne__ 

475 attr___eq__ = attr___ne__ 

476 attr___gt__ = attr___ne__ 

477 attr___format__ = attr___ne__ 

478 attr___delattr___ = attr___ne__ 

479 attr___getattribute__ = attr___ne__ 

480 attr___hash__ = attr___ne__ 

481 attr___dir__ = attr___ne__ 

482 attr___call__ = attr___ne__ 

483 attr___class__ = attr___ne__ 

484 attr___closure__ = attr___ne__ 

485 attr___code__ = attr___ne__ 

486 

487 

488class ClassModel(ObjectModel): 

489 def __init__(self): 

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

491 self.context = InferenceContext() 

492 

493 super().__init__() 

494 

495 @property 

496 def attr___module__(self): 

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

498 

499 @property 

500 def attr___name__(self): 

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

502 

503 @property 

504 def attr___qualname__(self): 

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

506 

507 @property 

508 def attr___doc__(self): 

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

510 

511 @property 

512 def attr___mro__(self): 

513 if not self._instance.newstyle: 

514 raise AttributeInferenceError(target=self._instance, attribute="__mro__") 

515 

516 mro = self._instance.mro() 

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

518 obj.postinit(mro) 

519 return obj 

520 

521 @property 

522 def attr_mro(self): 

523 if not self._instance.newstyle: 

524 raise AttributeInferenceError(target=self._instance, attribute="mro") 

525 

526 other_self = self 

527 

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

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

530 class MroBoundMethod(bases.BoundMethod): 

531 def infer_call_result( 

532 self, 

533 caller: SuccessfulInferenceResult | None, 

534 context: InferenceContext | None = None, 

535 ) -> Iterator[node_classes.Tuple]: 

536 yield other_self.attr___mro__ 

537 

538 implicit_metaclass = self._instance.implicit_metaclass() 

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

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

541 

542 @property 

543 def attr___bases__(self): 

544 obj = node_classes.Tuple() 

545 context = InferenceContext() 

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

547 obj.postinit(elts=elts) 

548 return obj 

549 

550 @property 

551 def attr___class__(self): 

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

553 from astroid import helpers 

554 

555 return helpers.object_type(self._instance) 

556 

557 @property 

558 def attr___subclasses__(self): 

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

560 

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

562 thus it might miss a couple of them. 

563 """ 

564 if not self._instance.newstyle: 

565 raise AttributeInferenceError( 

566 target=self._instance, attribute="__subclasses__" 

567 ) 

568 

569 qname = self._instance.qname() 

570 root = self._instance.root() 

571 classes = [ 

572 cls 

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

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

575 ] 

576 

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

578 obj.postinit(classes) 

579 

580 class SubclassesBoundMethod(bases.BoundMethod): 

581 def infer_call_result( 

582 self, 

583 caller: SuccessfulInferenceResult | None, 

584 context: InferenceContext | None = None, 

585 ) -> Iterator[node_classes.List]: 

586 yield obj 

587 

588 implicit_metaclass = self._instance.implicit_metaclass() 

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

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

591 

592 @property 

593 def attr___dict__(self): 

594 return node_classes.Dict( 

595 parent=self._instance, 

596 lineno=self._instance.lineno, 

597 col_offset=self._instance.col_offset, 

598 end_lineno=self._instance.end_lineno, 

599 end_col_offset=self._instance.end_col_offset, 

600 ) 

601 

602 @property 

603 def attr___call__(self): 

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

605 return self._instance.instantiate_class() 

606 

607 

608class SuperModel(ObjectModel): 

609 @property 

610 def attr___thisclass__(self): 

611 return self._instance.mro_pointer 

612 

613 @property 

614 def attr___self_class__(self): 

615 return self._instance._self_class 

616 

617 @property 

618 def attr___self__(self): 

619 return self._instance.type 

620 

621 @property 

622 def attr___class__(self): 

623 return self._instance._proxied 

624 

625 

626class UnboundMethodModel(ObjectModel): 

627 @property 

628 def attr___class__(self): 

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

630 from astroid import helpers 

631 

632 return helpers.object_type(self._instance) 

633 

634 @property 

635 def attr___func__(self): 

636 return self._instance._proxied 

637 

638 @property 

639 def attr___self__(self): 

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

641 

642 attr_im_func = attr___func__ 

643 attr_im_class = attr___class__ 

644 attr_im_self = attr___self__ 

645 

646 

647class ContextManagerModel(ObjectModel): 

648 """Model for context managers. 

649 

650 Based on 3.3.9 of the Data Model documentation: 

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

652 """ 

653 

654 @property 

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

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

657 

658 As per Python documentation: 

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

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

661 as clause of the statement, if any. 

662 """ 

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

664 

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

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

667 # is where this method originally comes from 

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

669 

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

671 

672 @property 

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

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

675 

676 As per Python documentation: 

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

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

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

680 """ 

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

682 

683 node: nodes.FunctionDef = builder.extract_node( 

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

685 ) 

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

687 # is where this method originally comes from 

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

689 

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

691 

692 

693class BoundMethodModel(FunctionModel): 

694 @property 

695 def attr___func__(self): 

696 return self._instance._proxied._proxied 

697 

698 @property 

699 def attr___self__(self): 

700 return self._instance.bound 

701 

702 

703class GeneratorModel(FunctionModel, ContextManagerModel): 

704 def __new__(cls, *args, **kwargs): 

705 # Append the values from the GeneratorType unto this object. 

706 ret = super().__new__(cls, *args, **kwargs) 

707 generator = AstroidManager().builtins_module["generator"] 

708 for name, values in generator.locals.items(): 

709 method = values[0] 

710 

711 def patched(cls, meth=method): 

712 return meth 

713 

714 setattr(type(ret), IMPL_PREFIX + name, property(patched)) 

715 

716 return ret 

717 

718 @property 

719 def attr___name__(self): 

720 return node_classes.Const( 

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

722 ) 

723 

724 @property 

725 def attr___doc__(self): 

726 return node_classes.Const( 

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

728 parent=self._instance, 

729 ) 

730 

731 

732class AsyncGeneratorModel(GeneratorModel): 

733 def __new__(cls, *args, **kwargs): 

734 # Append the values from the AGeneratorType unto this object. 

735 ret = super().__new__(cls, *args, **kwargs) 

736 astroid_builtins = AstroidManager().builtins_module 

737 generator = astroid_builtins.get("async_generator") 

738 if generator is None: 

739 # Make it backward compatible. 

740 generator = astroid_builtins.get("generator") 

741 

742 for name, values in generator.locals.items(): 

743 method = values[0] 

744 

745 def patched(cls, meth=method): 

746 return meth 

747 

748 setattr(type(ret), IMPL_PREFIX + name, property(patched)) 

749 

750 return ret 

751 

752 

753class InstanceModel(ObjectModel): 

754 @property 

755 def attr___class__(self): 

756 return self._instance._proxied 

757 

758 @property 

759 def attr___module__(self): 

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

761 

762 @property 

763 def attr___doc__(self): 

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

765 

766 @property 

767 def attr___dict__(self): 

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

769 

770 

771# Exception instances 

772 

773 

774class ExceptionInstanceModel(InstanceModel): 

775 @property 

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

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

778 

779 @property 

780 def attr___traceback__(self): 

781 builtins_ast_module = AstroidManager().builtins_module 

782 traceback_type = builtins_ast_module[types.TracebackType.__name__] 

783 return traceback_type.instantiate_class() 

784 

785 

786class SyntaxErrorInstanceModel(ExceptionInstanceModel): 

787 @property 

788 def attr_text(self): 

789 return node_classes.Const("") 

790 

791 

792class OSErrorInstanceModel(ExceptionInstanceModel): 

793 @property 

794 def attr_filename(self): 

795 return node_classes.Const("") 

796 

797 @property 

798 def attr_errno(self): 

799 return node_classes.Const(0) 

800 

801 @property 

802 def attr_strerror(self): 

803 return node_classes.Const("") 

804 

805 attr_filename2 = attr_filename 

806 

807 

808class ImportErrorInstanceModel(ExceptionInstanceModel): 

809 @property 

810 def attr_name(self): 

811 return node_classes.Const("") 

812 

813 @property 

814 def attr_path(self): 

815 return node_classes.Const("") 

816 

817 

818class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel): 

819 @property 

820 def attr_object(self): 

821 return node_classes.Const("") 

822 

823 

824BUILTIN_EXCEPTIONS = { 

825 "builtins.SyntaxError": SyntaxErrorInstanceModel, 

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) -> astroid.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