Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py: 58%

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

480 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"""Astroid hooks for various builtins.""" 

6 

7from __future__ import annotations 

8 

9import itertools 

10from collections.abc import Callable, Iterable, Iterator 

11from functools import partial 

12from typing import TYPE_CHECKING, Any, NoReturn, Union, cast 

13 

14from astroid import arguments, helpers, nodes, objects, util 

15from astroid.builder import AstroidBuilder 

16from astroid.context import InferenceContext 

17from astroid.exceptions import ( 

18 AstroidTypeError, 

19 AttributeInferenceError, 

20 InferenceError, 

21 MroError, 

22 UseInferenceDefault, 

23) 

24from astroid.inference_tip import inference_tip 

25from astroid.manager import AstroidManager 

26from astroid.nodes import scoped_nodes 

27from astroid.typing import ( 

28 ConstFactoryResult, 

29 InferenceResult, 

30 SuccessfulInferenceResult, 

31) 

32 

33if TYPE_CHECKING: 

34 from astroid.bases import Instance 

35 

36ContainerObjects = Union[ 

37 objects.FrozenSet, 

38 objects.DictItems, 

39 objects.DictKeys, 

40 objects.DictValues, 

41] 

42 

43BuiltContainers = Union[ 

44 type[tuple], 

45 type[list], 

46 type[set], 

47 type[frozenset], 

48] 

49 

50CopyResult = Union[ 

51 nodes.Dict, 

52 nodes.List, 

53 nodes.Set, 

54 objects.FrozenSet, 

55] 

56 

57OBJECT_DUNDER_NEW = "object.__new__" 

58 

59STR_CLASS = """ 

60class whatever(object): 

61 def join(self, iterable): 

62 return {rvalue} 

63 def replace(self, old, new, count=None): 

64 return {rvalue} 

65 def format(self, *args, **kwargs): 

66 return {rvalue} 

67 def encode(self, encoding='ascii', errors=None): 

68 return b'' 

69 def decode(self, encoding='ascii', errors=None): 

70 return u'' 

71 def capitalize(self): 

72 return {rvalue} 

73 def title(self): 

74 return {rvalue} 

75 def lower(self): 

76 return {rvalue} 

77 def upper(self): 

78 return {rvalue} 

79 def swapcase(self): 

80 return {rvalue} 

81 def index(self, sub, start=None, end=None): 

82 return 0 

83 def find(self, sub, start=None, end=None): 

84 return 0 

85 def count(self, sub, start=None, end=None): 

86 return 0 

87 def strip(self, chars=None): 

88 return {rvalue} 

89 def lstrip(self, chars=None): 

90 return {rvalue} 

91 def rstrip(self, chars=None): 

92 return {rvalue} 

93 def rjust(self, width, fillchar=None): 

94 return {rvalue} 

95 def center(self, width, fillchar=None): 

96 return {rvalue} 

97 def ljust(self, width, fillchar=None): 

98 return {rvalue} 

99""" 

100 

101 

102BYTES_CLASS = """ 

103class whatever(object): 

104 def join(self, iterable): 

105 return {rvalue} 

106 def replace(self, old, new, count=None): 

107 return {rvalue} 

108 def decode(self, encoding='ascii', errors=None): 

109 return u'' 

110 def capitalize(self): 

111 return {rvalue} 

112 def title(self): 

113 return {rvalue} 

114 def lower(self): 

115 return {rvalue} 

116 def upper(self): 

117 return {rvalue} 

118 def swapcase(self): 

119 return {rvalue} 

120 def index(self, sub, start=None, end=None): 

121 return 0 

122 def find(self, sub, start=None, end=None): 

123 return 0 

124 def count(self, sub, start=None, end=None): 

125 return 0 

126 def strip(self, chars=None): 

127 return {rvalue} 

128 def lstrip(self, chars=None): 

129 return {rvalue} 

130 def rstrip(self, chars=None): 

131 return {rvalue} 

132 def rjust(self, width, fillchar=None): 

133 return {rvalue} 

134 def center(self, width, fillchar=None): 

135 return {rvalue} 

136 def ljust(self, width, fillchar=None): 

137 return {rvalue} 

138""" 

139 

140 

141def _use_default() -> NoReturn: # pragma: no cover 

142 raise UseInferenceDefault() 

143 

144 

145def _extend_string_class(class_node, code, rvalue): 

146 """Function to extend builtin str/unicode class.""" 

147 code = code.format(rvalue=rvalue) 

148 fake = AstroidBuilder(AstroidManager()).string_build(code)["whatever"] 

149 for method in fake.mymethods(): 

150 method.parent = class_node 

151 method.lineno = None 

152 method.col_offset = None 

153 if "__class__" in method.locals: 

154 method.locals["__class__"] = [class_node] 

155 class_node.locals[method.name] = [method] 

156 method.parent = class_node 

157 

158 

159def _extend_builtins(class_transforms): 

160 builtin_ast = AstroidManager().builtins_module 

161 for class_name, transform in class_transforms.items(): 

162 transform(builtin_ast[class_name]) 

163 

164 

165def on_bootstrap(): 

166 """Called by astroid_bootstrapping().""" 

167 _extend_builtins( 

168 { 

169 "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"), 

170 "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"), 

171 } 

172 ) 

173 

174 

175def _builtin_filter_predicate(node, builtin_name) -> bool: 

176 # pylint: disable = too-many-boolean-expressions 

177 if ( 

178 builtin_name == "type" 

179 and node.root().name == "re" 

180 and isinstance(node.func, nodes.Name) 

181 and node.func.name == "type" 

182 and isinstance(node.parent, nodes.Assign) 

183 and len(node.parent.targets) == 1 

184 and isinstance(node.parent.targets[0], nodes.AssignName) 

185 and node.parent.targets[0].name in {"Pattern", "Match"} 

186 ): 

187 # Handle re.Pattern and re.Match in brain_re 

188 # Match these patterns from stdlib/re.py 

189 # ```py 

190 # Pattern = type(...) 

191 # Match = type(...) 

192 # ``` 

193 return False 

194 if isinstance(node.func, nodes.Name): 

195 return node.func.name == builtin_name 

196 if isinstance(node.func, nodes.Attribute): 

197 return ( 

198 node.func.attrname == "fromkeys" 

199 and isinstance(node.func.expr, nodes.Name) 

200 and node.func.expr.name == "dict" 

201 ) 

202 return False 

203 

204 

205def register_builtin_transform( 

206 manager: AstroidManager, transform, builtin_name 

207) -> None: 

208 """Register a new transform function for the given *builtin_name*. 

209 

210 The transform function must accept two parameters, a node and 

211 an optional context. 

212 """ 

213 

214 def _transform_wrapper( 

215 node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any 

216 ) -> Iterator: 

217 result = transform(node, context=context) 

218 if result: 

219 if not result.parent: 

220 # Let the transformation function determine 

221 # the parent for its result. Otherwise, 

222 # we set it to be the node we transformed from. 

223 result.parent = node 

224 

225 if result.lineno is None: 

226 result.lineno = node.lineno 

227 # Can be a 'Module' see https://github.com/pylint-dev/pylint/issues/4671 

228 # We don't have a regression test on this one: tread carefully 

229 if hasattr(result, "col_offset") and result.col_offset is None: 

230 result.col_offset = node.col_offset 

231 return iter([result]) 

232 

233 manager.register_transform( 

234 nodes.Call, 

235 inference_tip(_transform_wrapper), 

236 partial(_builtin_filter_predicate, builtin_name=builtin_name), 

237 ) 

238 

239 

240def _container_generic_inference( 

241 node: nodes.Call, 

242 context: InferenceContext | None, 

243 node_type: type[nodes.BaseContainer], 

244 transform: Callable[[SuccessfulInferenceResult], nodes.BaseContainer | None], 

245) -> nodes.BaseContainer: 

246 args = node.args 

247 if not args: 

248 return node_type( 

249 lineno=node.lineno, 

250 col_offset=node.col_offset, 

251 parent=node.parent, 

252 end_lineno=node.end_lineno, 

253 end_col_offset=node.end_col_offset, 

254 ) 

255 if len(node.args) > 1: 

256 raise UseInferenceDefault() 

257 

258 (arg,) = args 

259 transformed = transform(arg) 

260 if not transformed: 

261 try: 

262 inferred = next(arg.infer(context=context)) 

263 except (InferenceError, StopIteration) as exc: 

264 raise UseInferenceDefault from exc 

265 if isinstance(inferred, util.UninferableBase): 

266 raise UseInferenceDefault 

267 transformed = transform(inferred) 

268 if not transformed or isinstance(transformed, util.UninferableBase): 

269 raise UseInferenceDefault 

270 return transformed 

271 

272 

273def _container_generic_transform( 

274 arg: SuccessfulInferenceResult, 

275 context: InferenceContext | None, 

276 klass: type[nodes.BaseContainer], 

277 iterables: tuple[type[nodes.BaseContainer] | type[ContainerObjects], ...], 

278 build_elts: BuiltContainers, 

279) -> nodes.BaseContainer | None: 

280 elts: Iterable | str | bytes 

281 

282 if isinstance(arg, klass): 

283 return arg 

284 if isinstance(arg, iterables): 

285 arg = cast(Union[nodes.BaseContainer, ContainerObjects], arg) 

286 if all(isinstance(elt, nodes.Const) for elt in arg.elts): 

287 elts = [cast(nodes.Const, elt).value for elt in arg.elts] 

288 else: 

289 # TODO: Does not handle deduplication for sets. 

290 elts = [] 

291 for element in arg.elts: 

292 if not element: 

293 continue 

294 inferred = util.safe_infer(element, context=context) 

295 if inferred: 

296 evaluated_object = nodes.EvaluatedObject( 

297 original=element, value=inferred 

298 ) 

299 elts.append(evaluated_object) 

300 elif isinstance(arg, nodes.Dict): 

301 # Dicts need to have consts as strings already. 

302 elts = [ 

303 item[0].value if isinstance(item[0], nodes.Const) else _use_default() 

304 for item in arg.items 

305 ] 

306 elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)): 

307 elts = arg.value 

308 else: 

309 return None 

310 return klass.from_elements(elts=build_elts(elts)) 

311 

312 

313def _infer_builtin_container( 

314 node: nodes.Call, 

315 context: InferenceContext | None, 

316 klass: type[nodes.BaseContainer], 

317 iterables: tuple[type[nodes.NodeNG] | type[ContainerObjects], ...], 

318 build_elts: BuiltContainers, 

319) -> nodes.BaseContainer: 

320 transform_func = partial( 

321 _container_generic_transform, 

322 context=context, 

323 klass=klass, 

324 iterables=iterables, 

325 build_elts=build_elts, 

326 ) 

327 

328 return _container_generic_inference(node, context, klass, transform_func) 

329 

330 

331# pylint: disable=invalid-name 

332infer_tuple = partial( 

333 _infer_builtin_container, 

334 klass=nodes.Tuple, 

335 iterables=( 

336 nodes.List, 

337 nodes.Set, 

338 objects.FrozenSet, 

339 objects.DictItems, 

340 objects.DictKeys, 

341 objects.DictValues, 

342 ), 

343 build_elts=tuple, 

344) 

345 

346infer_list = partial( 

347 _infer_builtin_container, 

348 klass=nodes.List, 

349 iterables=( 

350 nodes.Tuple, 

351 nodes.Set, 

352 objects.FrozenSet, 

353 objects.DictItems, 

354 objects.DictKeys, 

355 objects.DictValues, 

356 ), 

357 build_elts=list, 

358) 

359 

360infer_set = partial( 

361 _infer_builtin_container, 

362 klass=nodes.Set, 

363 iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys), 

364 build_elts=set, 

365) 

366 

367infer_frozenset = partial( 

368 _infer_builtin_container, 

369 klass=objects.FrozenSet, 

370 iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys), 

371 build_elts=frozenset, 

372) 

373 

374 

375def _get_elts(arg, context): 

376 def is_iterable(n) -> bool: 

377 return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set)) 

378 

379 try: 

380 inferred = next(arg.infer(context)) 

381 except (InferenceError, StopIteration) as exc: 

382 raise UseInferenceDefault from exc 

383 if isinstance(inferred, nodes.Dict): 

384 items = inferred.items 

385 elif is_iterable(inferred): 

386 items = [] 

387 for elt in inferred.elts: 

388 # If an item is not a pair of two items, 

389 # then fallback to the default inference. 

390 # Also, take in consideration only hashable items, 

391 # tuples and consts. We are choosing Names as well. 

392 if not is_iterable(elt): 

393 raise UseInferenceDefault() 

394 if len(elt.elts) != 2: 

395 raise UseInferenceDefault() 

396 if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)): 

397 raise UseInferenceDefault() 

398 items.append(tuple(elt.elts)) 

399 else: 

400 raise UseInferenceDefault() 

401 return items 

402 

403 

404def infer_dict(node: nodes.Call, context: InferenceContext | None = None) -> nodes.Dict: 

405 """Try to infer a dict call to a Dict node. 

406 

407 The function treats the following cases: 

408 

409 * dict() 

410 * dict(mapping) 

411 * dict(iterable) 

412 * dict(iterable, **kwargs) 

413 * dict(mapping, **kwargs) 

414 * dict(**kwargs) 

415 

416 If a case can't be inferred, we'll fallback to default inference. 

417 """ 

418 call = arguments.CallSite.from_call(node, context=context) 

419 if call.has_invalid_arguments() or call.has_invalid_keywords(): 

420 raise UseInferenceDefault 

421 

422 args = call.positional_arguments 

423 kwargs = list(call.keyword_arguments.items()) 

424 

425 items: list[tuple[InferenceResult, InferenceResult]] 

426 if not args and not kwargs: 

427 # dict() 

428 return nodes.Dict( 

429 lineno=node.lineno, 

430 col_offset=node.col_offset, 

431 parent=node.parent, 

432 end_lineno=node.end_lineno, 

433 end_col_offset=node.end_col_offset, 

434 ) 

435 if kwargs and not args: 

436 # dict(a=1, b=2, c=4) 

437 items = [(nodes.Const(key), value) for key, value in kwargs] 

438 elif len(args) == 1 and kwargs: 

439 # dict(some_iterable, b=2, c=4) 

440 elts = _get_elts(args[0], context) 

441 keys = [(nodes.Const(key), value) for key, value in kwargs] 

442 items = elts + keys 

443 elif len(args) == 1: 

444 items = _get_elts(args[0], context) 

445 else: 

446 raise UseInferenceDefault() 

447 value = nodes.Dict( 

448 col_offset=node.col_offset, 

449 lineno=node.lineno, 

450 parent=node.parent, 

451 end_lineno=node.end_lineno, 

452 end_col_offset=node.end_col_offset, 

453 ) 

454 value.postinit(items) 

455 return value 

456 

457 

458def infer_super( 

459 node: nodes.Call, context: InferenceContext | None = None 

460) -> objects.Super: 

461 """Understand super calls. 

462 

463 There are some restrictions for what can be understood: 

464 

465 * unbounded super (one argument form) is not understood. 

466 

467 * if the super call is not inside a function (classmethod or method), 

468 then the default inference will be used. 

469 

470 * if the super arguments can't be inferred, the default inference 

471 will be used. 

472 """ 

473 if len(node.args) == 1: 

474 # Ignore unbounded super. 

475 raise UseInferenceDefault 

476 

477 scope = node.scope() 

478 if not isinstance(scope, nodes.FunctionDef): 

479 # Ignore non-method uses of super. 

480 raise UseInferenceDefault 

481 if scope.type not in ("classmethod", "method"): 

482 # Not interested in staticmethods. 

483 raise UseInferenceDefault 

484 

485 cls = scoped_nodes.get_wrapping_class(scope) 

486 assert cls is not None 

487 if not node.args: 

488 mro_pointer = cls 

489 # In we are in a classmethod, the interpreter will fill 

490 # automatically the class as the second argument, not an instance. 

491 if scope.type == "classmethod": 

492 mro_type = cls 

493 else: 

494 mro_type = cls.instantiate_class() 

495 else: 

496 try: 

497 mro_pointer = next(node.args[0].infer(context=context)) 

498 except (InferenceError, StopIteration) as exc: 

499 raise UseInferenceDefault from exc 

500 try: 

501 mro_type = next(node.args[1].infer(context=context)) 

502 except (InferenceError, StopIteration) as exc: 

503 raise UseInferenceDefault from exc 

504 

505 if isinstance(mro_pointer, util.UninferableBase) or isinstance( 

506 mro_type, util.UninferableBase 

507 ): 

508 # No way we could understand this. 

509 raise UseInferenceDefault 

510 

511 super_obj = objects.Super( 

512 mro_pointer=mro_pointer, 

513 mro_type=mro_type, 

514 self_class=cls, 

515 scope=scope, 

516 call=node, 

517 ) 

518 super_obj.parent = node 

519 return super_obj 

520 

521 

522def _infer_getattr_args(node, context): 

523 if len(node.args) not in (2, 3): 

524 # Not a valid getattr call. 

525 raise UseInferenceDefault 

526 

527 try: 

528 obj = next(node.args[0].infer(context=context)) 

529 attr = next(node.args[1].infer(context=context)) 

530 except (InferenceError, StopIteration) as exc: 

531 raise UseInferenceDefault from exc 

532 

533 if isinstance(obj, util.UninferableBase) or isinstance(attr, util.UninferableBase): 

534 # If one of the arguments is something we can't infer, 

535 # then also make the result of the getattr call something 

536 # which is unknown. 

537 return util.Uninferable, util.Uninferable 

538 

539 is_string = isinstance(attr, nodes.Const) and isinstance(attr.value, str) 

540 if not is_string: 

541 raise UseInferenceDefault 

542 

543 return obj, attr.value 

544 

545 

546def infer_getattr(node, context: InferenceContext | None = None): 

547 """Understand getattr calls. 

548 

549 If one of the arguments is an Uninferable object, then the 

550 result will be an Uninferable object. Otherwise, the normal attribute 

551 lookup will be done. 

552 """ 

553 obj, attr = _infer_getattr_args(node, context) 

554 if ( 

555 isinstance(obj, util.UninferableBase) 

556 or isinstance(attr, util.UninferableBase) 

557 or not hasattr(obj, "igetattr") 

558 ): 

559 return util.Uninferable 

560 

561 try: 

562 return next(obj.igetattr(attr, context=context)) 

563 except (StopIteration, InferenceError, AttributeInferenceError): 

564 if len(node.args) == 3: 

565 # Try to infer the default and return it instead. 

566 try: 

567 return next(node.args[2].infer(context=context)) 

568 except (StopIteration, InferenceError) as exc: 

569 raise UseInferenceDefault from exc 

570 

571 raise UseInferenceDefault 

572 

573 

574def infer_hasattr(node, context: InferenceContext | None = None): 

575 """Understand hasattr calls. 

576 

577 This always guarantees three possible outcomes for calling 

578 hasattr: Const(False) when we are sure that the object 

579 doesn't have the intended attribute, Const(True) when 

580 we know that the object has the attribute and Uninferable 

581 when we are unsure of the outcome of the function call. 

582 """ 

583 try: 

584 obj, attr = _infer_getattr_args(node, context) 

585 if ( 

586 isinstance(obj, util.UninferableBase) 

587 or isinstance(attr, util.UninferableBase) 

588 or not hasattr(obj, "getattr") 

589 ): 

590 return util.Uninferable 

591 obj.getattr(attr, context=context) 

592 except UseInferenceDefault: 

593 # Can't infer something from this function call. 

594 return util.Uninferable 

595 except AttributeInferenceError: 

596 # Doesn't have it. 

597 return nodes.Const(False) 

598 return nodes.Const(True) 

599 

600 

601def infer_callable(node, context: InferenceContext | None = None): 

602 """Understand callable calls. 

603 

604 This follows Python's semantics, where an object 

605 is callable if it provides an attribute __call__, 

606 even though that attribute is something which can't be 

607 called. 

608 """ 

609 if len(node.args) != 1: 

610 # Invalid callable call. 

611 raise UseInferenceDefault 

612 

613 argument = node.args[0] 

614 try: 

615 inferred = next(argument.infer(context=context)) 

616 except (InferenceError, StopIteration): 

617 return util.Uninferable 

618 if isinstance(inferred, util.UninferableBase): 

619 return util.Uninferable 

620 return nodes.Const(inferred.callable()) 

621 

622 

623def infer_property( 

624 node: nodes.Call, context: InferenceContext | None = None 

625) -> objects.Property: 

626 """Understand `property` class. 

627 

628 This only infers the output of `property` 

629 call, not the arguments themselves. 

630 """ 

631 if len(node.args) < 1: 

632 # Invalid property call. 

633 raise UseInferenceDefault 

634 

635 getter = node.args[0] 

636 try: 

637 inferred = next(getter.infer(context=context)) 

638 except (InferenceError, StopIteration) as exc: 

639 raise UseInferenceDefault from exc 

640 

641 if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)): 

642 raise UseInferenceDefault 

643 

644 prop_func = objects.Property( 

645 function=inferred, 

646 name="<property>", 

647 lineno=node.lineno, 

648 col_offset=node.col_offset, 

649 # ↓ semantically, the definition of the class of property isn't within 

650 # node.frame. It's somewhere in the builtins module, but we are special 

651 # casing it for each "property()" call, so we are making up the 

652 # definition on the spot, ad-hoc. 

653 parent=scoped_nodes.SYNTHETIC_ROOT, 

654 ) 

655 prop_func.postinit( 

656 body=[], 

657 args=inferred.args, 

658 doc_node=getattr(inferred, "doc_node", None), 

659 ) 

660 return prop_func 

661 

662 

663def infer_bool(node, context: InferenceContext | None = None): 

664 """Understand bool calls.""" 

665 if len(node.args) > 1: 

666 # Invalid bool call. 

667 raise UseInferenceDefault 

668 

669 if not node.args: 

670 return nodes.Const(False) 

671 

672 argument = node.args[0] 

673 try: 

674 inferred = next(argument.infer(context=context)) 

675 except (InferenceError, StopIteration): 

676 return util.Uninferable 

677 if isinstance(inferred, util.UninferableBase): 

678 return util.Uninferable 

679 

680 bool_value = inferred.bool_value(context=context) 

681 if isinstance(bool_value, util.UninferableBase): 

682 return util.Uninferable 

683 return nodes.Const(bool_value) 

684 

685 

686def infer_type(node, context: InferenceContext | None = None): 

687 """Understand the one-argument form of *type*.""" 

688 if len(node.args) != 1: 

689 raise UseInferenceDefault 

690 

691 return helpers.object_type(node.args[0], context) 

692 

693 

694def infer_slice(node, context: InferenceContext | None = None): 

695 """Understand `slice` calls.""" 

696 args = node.args 

697 if not 0 < len(args) <= 3: 

698 raise UseInferenceDefault 

699 

700 infer_func = partial(util.safe_infer, context=context) 

701 args = [infer_func(arg) for arg in args] 

702 for arg in args: 

703 if not arg or isinstance(arg, util.UninferableBase): 

704 raise UseInferenceDefault 

705 if not isinstance(arg, nodes.Const): 

706 raise UseInferenceDefault 

707 if not isinstance(arg.value, (type(None), int)): 

708 raise UseInferenceDefault 

709 

710 if len(args) < 3: 

711 # Make sure we have 3 arguments. 

712 args.extend([None] * (3 - len(args))) 

713 

714 slice_node = nodes.Slice( 

715 lineno=node.lineno, 

716 col_offset=node.col_offset, 

717 parent=node.parent, 

718 end_lineno=node.end_lineno, 

719 end_col_offset=node.end_col_offset, 

720 ) 

721 slice_node.postinit(*args) 

722 return slice_node 

723 

724 

725def _infer_object__new__decorator( 

726 node: nodes.ClassDef, context: InferenceContext | None = None, **kwargs: Any 

727) -> Iterator[Instance]: 

728 # Instantiate class immediately 

729 # since that's what @object.__new__ does 

730 return iter((node.instantiate_class(),)) 

731 

732 

733def _infer_object__new__decorator_check(node) -> bool: 

734 """Predicate before inference_tip. 

735 

736 Check if the given ClassDef has an @object.__new__ decorator 

737 """ 

738 if not node.decorators: 

739 return False 

740 

741 for decorator in node.decorators.nodes: 

742 if isinstance(decorator, nodes.Attribute): 

743 if decorator.as_string() == OBJECT_DUNDER_NEW: 

744 return True 

745 return False 

746 

747 

748def infer_issubclass(callnode, context: InferenceContext | None = None): 

749 """Infer issubclass() calls. 

750 

751 :param nodes.Call callnode: an `issubclass` call 

752 :param InferenceContext context: the context for the inference 

753 :rtype nodes.Const: Boolean Const value of the `issubclass` call 

754 :raises UseInferenceDefault: If the node cannot be inferred 

755 """ 

756 call = arguments.CallSite.from_call(callnode, context=context) 

757 if call.keyword_arguments: 

758 # issubclass doesn't support keyword arguments 

759 raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments") 

760 if len(call.positional_arguments) != 2: 

761 raise UseInferenceDefault( 

762 f"Expected two arguments, got {len(call.positional_arguments)}" 

763 ) 

764 # The left hand argument is the obj to be checked 

765 obj_node, class_or_tuple_node = call.positional_arguments 

766 

767 try: 

768 obj_type = next(obj_node.infer(context=context)) 

769 except (InferenceError, StopIteration) as exc: 

770 raise UseInferenceDefault from exc 

771 if not isinstance(obj_type, nodes.ClassDef): 

772 raise UseInferenceDefault("TypeError: arg 1 must be class") 

773 

774 # The right hand argument is the class(es) that the given 

775 # object is to be checked against. 

776 try: 

777 class_container = _class_or_tuple_to_container( 

778 class_or_tuple_node, context=context 

779 ) 

780 except InferenceError as exc: 

781 raise UseInferenceDefault from exc 

782 try: 

783 issubclass_bool = helpers.object_issubclass(obj_type, class_container, context) 

784 except AstroidTypeError as exc: 

785 raise UseInferenceDefault("TypeError: " + str(exc)) from exc 

786 except MroError as exc: 

787 raise UseInferenceDefault from exc 

788 return nodes.Const(issubclass_bool) 

789 

790 

791def infer_isinstance( 

792 callnode: nodes.Call, context: InferenceContext | None = None 

793) -> nodes.Const: 

794 """Infer isinstance calls. 

795 

796 :param nodes.Call callnode: an isinstance call 

797 :raises UseInferenceDefault: If the node cannot be inferred 

798 """ 

799 call = arguments.CallSite.from_call(callnode, context=context) 

800 if call.keyword_arguments: 

801 # isinstance doesn't support keyword arguments 

802 raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments") 

803 if len(call.positional_arguments) != 2: 

804 raise UseInferenceDefault( 

805 f"Expected two arguments, got {len(call.positional_arguments)}" 

806 ) 

807 # The left hand argument is the obj to be checked 

808 obj_node, class_or_tuple_node = call.positional_arguments 

809 # The right hand argument is the class(es) that the given 

810 # obj is to be check is an instance of 

811 try: 

812 class_container = _class_or_tuple_to_container( 

813 class_or_tuple_node, context=context 

814 ) 

815 except InferenceError as exc: 

816 raise UseInferenceDefault from exc 

817 try: 

818 isinstance_bool = helpers.object_isinstance(obj_node, class_container, context) 

819 except AstroidTypeError as exc: 

820 raise UseInferenceDefault("TypeError: " + str(exc)) from exc 

821 except MroError as exc: 

822 raise UseInferenceDefault from exc 

823 if isinstance(isinstance_bool, util.UninferableBase): 

824 raise UseInferenceDefault 

825 return nodes.Const(isinstance_bool) 

826 

827 

828def _class_or_tuple_to_container( 

829 node: InferenceResult, context: InferenceContext | None = None 

830) -> list[InferenceResult]: 

831 # Move inferences results into container 

832 # to simplify later logic 

833 # raises InferenceError if any of the inferences fall through 

834 try: 

835 node_infer = next(node.infer(context=context)) 

836 except StopIteration as e: 

837 raise InferenceError(node=node, context=context) from e 

838 # arg2 MUST be a type or a TUPLE of types 

839 # for isinstance 

840 if isinstance(node_infer, nodes.Tuple): 

841 try: 

842 class_container = [ 

843 next(node.infer(context=context)) for node in node_infer.elts 

844 ] 

845 except StopIteration as e: 

846 raise InferenceError(node=node, context=context) from e 

847 else: 

848 class_container = [node_infer] 

849 return class_container 

850 

851 

852def infer_len(node, context: InferenceContext | None = None) -> nodes.Const: 

853 """Infer length calls. 

854 

855 :param nodes.Call node: len call to infer 

856 :param context.InferenceContext: node context 

857 :rtype nodes.Const: a Const node with the inferred length, if possible 

858 """ 

859 call = arguments.CallSite.from_call(node, context=context) 

860 if call.keyword_arguments: 

861 raise UseInferenceDefault("TypeError: len() must take no keyword arguments") 

862 if len(call.positional_arguments) != 1: 

863 raise UseInferenceDefault( 

864 "TypeError: len() must take exactly one argument " 

865 "({len}) given".format(len=len(call.positional_arguments)) 

866 ) 

867 [argument_node] = call.positional_arguments 

868 

869 try: 

870 return nodes.Const(helpers.object_len(argument_node, context=context)) 

871 except (AstroidTypeError, InferenceError) as exc: 

872 raise UseInferenceDefault(str(exc)) from exc 

873 

874 

875def infer_str(node, context: InferenceContext | None = None) -> nodes.Const: 

876 """Infer str() calls. 

877 

878 :param nodes.Call node: str() call to infer 

879 :param context.InferenceContext: node context 

880 :rtype nodes.Const: a Const containing an empty string 

881 """ 

882 call = arguments.CallSite.from_call(node, context=context) 

883 if call.keyword_arguments: 

884 raise UseInferenceDefault("TypeError: str() must take no keyword arguments") 

885 try: 

886 return nodes.Const("") 

887 except (AstroidTypeError, InferenceError) as exc: 

888 raise UseInferenceDefault(str(exc)) from exc 

889 

890 

891def infer_int(node, context: InferenceContext | None = None): 

892 """Infer int() calls. 

893 

894 :param nodes.Call node: int() call to infer 

895 :param context.InferenceContext: node context 

896 :rtype nodes.Const: a Const containing the integer value of the int() call 

897 """ 

898 call = arguments.CallSite.from_call(node, context=context) 

899 if call.keyword_arguments: 

900 raise UseInferenceDefault("TypeError: int() must take no keyword arguments") 

901 

902 if call.positional_arguments: 

903 try: 

904 first_value = next(call.positional_arguments[0].infer(context=context)) 

905 except (InferenceError, StopIteration) as exc: 

906 raise UseInferenceDefault(str(exc)) from exc 

907 

908 if isinstance(first_value, util.UninferableBase): 

909 raise UseInferenceDefault 

910 

911 if isinstance(first_value, nodes.Const) and isinstance( 

912 first_value.value, (int, str) 

913 ): 

914 try: 

915 actual_value = int(first_value.value) 

916 except ValueError: 

917 return nodes.Const(0) 

918 return nodes.Const(actual_value) 

919 

920 return nodes.Const(0) 

921 

922 

923def infer_dict_fromkeys(node, context: InferenceContext | None = None): 

924 """Infer dict.fromkeys. 

925 

926 :param nodes.Call node: dict.fromkeys() call to infer 

927 :param context.InferenceContext context: node context 

928 :rtype nodes.Dict: 

929 a Dictionary containing the values that astroid was able to infer. 

930 In case the inference failed for any reason, an empty dictionary 

931 will be inferred instead. 

932 """ 

933 

934 def _build_dict_with_elements(elements: list) -> nodes.Dict: 

935 new_node = nodes.Dict( 

936 col_offset=node.col_offset, 

937 lineno=node.lineno, 

938 parent=node.parent, 

939 end_lineno=node.end_lineno, 

940 end_col_offset=node.end_col_offset, 

941 ) 

942 new_node.postinit(elements) 

943 return new_node 

944 

945 call = arguments.CallSite.from_call(node, context=context) 

946 if call.keyword_arguments: 

947 raise UseInferenceDefault("TypeError: int() must take no keyword arguments") 

948 if len(call.positional_arguments) not in {1, 2}: 

949 raise UseInferenceDefault( 

950 "TypeError: Needs between 1 and 2 positional arguments" 

951 ) 

952 

953 default = nodes.Const(None) 

954 values = call.positional_arguments[0] 

955 try: 

956 inferred_values = next(values.infer(context=context)) 

957 except (InferenceError, StopIteration): 

958 return _build_dict_with_elements([]) 

959 if inferred_values is util.Uninferable: 

960 return _build_dict_with_elements([]) 

961 

962 # Limit to a couple of potential values, as this can become pretty complicated 

963 accepted_iterable_elements = (nodes.Const,) 

964 if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)): 

965 elements = inferred_values.elts 

966 for element in elements: 

967 if not isinstance(element, accepted_iterable_elements): 

968 # Fallback to an empty dict 

969 return _build_dict_with_elements([]) 

970 

971 elements_with_value = [(element, default) for element in elements] 

972 return _build_dict_with_elements(elements_with_value) 

973 if isinstance(inferred_values, nodes.Const) and isinstance( 

974 inferred_values.value, (str, bytes) 

975 ): 

976 elements_with_value = [ 

977 (nodes.Const(element), default) for element in inferred_values.value 

978 ] 

979 return _build_dict_with_elements(elements_with_value) 

980 if isinstance(inferred_values, nodes.Dict): 

981 keys = inferred_values.itered() 

982 for key in keys: 

983 if not isinstance(key, accepted_iterable_elements): 

984 # Fallback to an empty dict 

985 return _build_dict_with_elements([]) 

986 

987 elements_with_value = [(element, default) for element in keys] 

988 return _build_dict_with_elements(elements_with_value) 

989 

990 # Fallback to an empty dictionary 

991 return _build_dict_with_elements([]) 

992 

993 

994def _infer_copy_method( 

995 node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any 

996) -> Iterator[CopyResult]: 

997 assert isinstance(node.func, nodes.Attribute) 

998 inferred_orig, inferred_copy = itertools.tee(node.func.expr.infer(context=context)) 

999 if all( 

1000 isinstance( 

1001 inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet) 

1002 ) 

1003 for inferred_node in inferred_orig 

1004 ): 

1005 return cast(Iterator[CopyResult], inferred_copy) 

1006 

1007 raise UseInferenceDefault 

1008 

1009 

1010def _is_str_format_call(node: nodes.Call) -> bool: 

1011 """Catch calls to str.format().""" 

1012 if not isinstance(node.func, nodes.Attribute) or not node.func.attrname == "format": 

1013 return False 

1014 

1015 if isinstance(node.func.expr, nodes.Name): 

1016 value = util.safe_infer(node.func.expr) 

1017 else: 

1018 value = node.func.expr 

1019 

1020 return isinstance(value, nodes.Const) and isinstance(value.value, str) 

1021 

1022 

1023def _infer_str_format_call( 

1024 node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any 

1025) -> Iterator[ConstFactoryResult | util.UninferableBase]: 

1026 """Return a Const node based on the template and passed arguments.""" 

1027 call = arguments.CallSite.from_call(node, context=context) 

1028 assert isinstance(node.func, (nodes.Attribute, nodes.AssignAttr, nodes.DelAttr)) 

1029 

1030 value: nodes.Const 

1031 if isinstance(node.func.expr, nodes.Name): 

1032 if not (inferred := util.safe_infer(node.func.expr)) or not isinstance( 

1033 inferred, nodes.Const 

1034 ): 

1035 return iter([util.Uninferable]) 

1036 value = inferred 

1037 elif isinstance(node.func.expr, nodes.Const): 

1038 value = node.func.expr 

1039 else: # pragma: no cover 

1040 return iter([util.Uninferable]) 

1041 

1042 format_template = value.value 

1043 

1044 # Get the positional arguments passed 

1045 inferred_positional: list[nodes.Const] = [] 

1046 for i in call.positional_arguments: 

1047 one_inferred = util.safe_infer(i, context) 

1048 if not isinstance(one_inferred, nodes.Const): 

1049 return iter([util.Uninferable]) 

1050 inferred_positional.append(one_inferred) 

1051 

1052 pos_values: list[str] = [i.value for i in inferred_positional] 

1053 

1054 # Get the keyword arguments passed 

1055 inferred_keyword: dict[str, nodes.Const] = {} 

1056 for k, v in call.keyword_arguments.items(): 

1057 one_inferred = util.safe_infer(v, context) 

1058 if not isinstance(one_inferred, nodes.Const): 

1059 return iter([util.Uninferable]) 

1060 inferred_keyword[k] = one_inferred 

1061 

1062 keyword_values: dict[str, str] = {k: v.value for k, v in inferred_keyword.items()} 

1063 

1064 try: 

1065 formatted_string = format_template.format(*pos_values, **keyword_values) 

1066 except (AttributeError, IndexError, KeyError, TypeError, ValueError): 

1067 # AttributeError: named field in format string was not found in the arguments 

1068 # IndexError: there are too few arguments to interpolate 

1069 # TypeError: Unsupported format string 

1070 # ValueError: Unknown format code 

1071 return iter([util.Uninferable]) 

1072 

1073 return iter([nodes.const_factory(formatted_string)]) 

1074 

1075 

1076def register(manager: AstroidManager) -> None: 

1077 # Builtins inference 

1078 register_builtin_transform(manager, infer_bool, "bool") 

1079 register_builtin_transform(manager, infer_super, "super") 

1080 register_builtin_transform(manager, infer_callable, "callable") 

1081 register_builtin_transform(manager, infer_property, "property") 

1082 register_builtin_transform(manager, infer_getattr, "getattr") 

1083 register_builtin_transform(manager, infer_hasattr, "hasattr") 

1084 register_builtin_transform(manager, infer_tuple, "tuple") 

1085 register_builtin_transform(manager, infer_set, "set") 

1086 register_builtin_transform(manager, infer_list, "list") 

1087 register_builtin_transform(manager, infer_dict, "dict") 

1088 register_builtin_transform(manager, infer_frozenset, "frozenset") 

1089 register_builtin_transform(manager, infer_type, "type") 

1090 register_builtin_transform(manager, infer_slice, "slice") 

1091 register_builtin_transform(manager, infer_isinstance, "isinstance") 

1092 register_builtin_transform(manager, infer_issubclass, "issubclass") 

1093 register_builtin_transform(manager, infer_len, "len") 

1094 register_builtin_transform(manager, infer_str, "str") 

1095 register_builtin_transform(manager, infer_int, "int") 

1096 register_builtin_transform(manager, infer_dict_fromkeys, "dict.fromkeys") 

1097 

1098 # Infer object.__new__ calls 

1099 manager.register_transform( 

1100 nodes.ClassDef, 

1101 inference_tip(_infer_object__new__decorator), 

1102 _infer_object__new__decorator_check, 

1103 ) 

1104 

1105 manager.register_transform( 

1106 nodes.Call, 

1107 inference_tip(_infer_copy_method), 

1108 lambda node: isinstance(node.func, nodes.Attribute) 

1109 and node.func.attrname == "copy", 

1110 ) 

1111 

1112 manager.register_transform( 

1113 nodes.Call, 

1114 inference_tip(_infer_str_format_call), 

1115 _is_str_format_call, 

1116 )