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

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

524 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 

10import re 

11from collections.abc import Callable, Iterable, Iterator 

12from functools import lru_cache, partial 

13from typing import NoReturn, cast 

14 

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

16from astroid.bases import Instance 

17from astroid.builder import AstroidBuilder 

18from astroid.context import InferenceContext 

19from astroid.exceptions import ( 

20 AstroidTypeError, 

21 AttributeInferenceError, 

22 InferenceError, 

23 MroError, 

24 UseInferenceDefault, 

25) 

26from astroid.inference_tip import inference_tip 

27from astroid.manager import AstroidManager 

28from astroid.nodes import scoped_nodes 

29from astroid.typing import ( 

30 ConstFactoryResult, 

31 InferenceResult, 

32 SuccessfulInferenceResult, 

33) 

34 

35ContainerObjects = ( 

36 objects.FrozenSet | objects.DictItems | objects.DictKeys | objects.DictValues 

37) 

38 

39BuiltContainers = type[tuple] | type[list] | type[set] | type[frozenset] 

40 

41CopyResult = nodes.Dict | nodes.List | nodes.Set | objects.FrozenSet 

42 

43OBJECT_DUNDER_NEW = "object.__new__" 

44 

45# Largest field width/precision we are willing to materialize when inferring a 

46# str.format() call. A crafted template such as "{:>2000000000}" would otherwise 

47# eagerly build a multi-gigabyte string during inference. 

48_MAX_FORMAT_FIELD_SIZE = int(1e8) 

49 

50# Largest str/bytes constant we are willing to expand into one node per 

51# character when inferring container calls and dict.fromkeys. 

52_MAX_INFERABLE_STR_LEN = int(1e8) 

53 

54 

55@lru_cache(maxsize=1) 

56def _get_bounded_formatter(): 

57 """Build the formatter lazily: importing ``string`` is costly at import time.""" 

58 import string # pylint: disable=import-outside-toplevel 

59 

60 class _BoundedFormatter(string.Formatter): 

61 """A ``str.format`` implementation that refuses oversized width/precision. 

62 

63 The only multi-digit numbers in a format spec are the width and the 

64 precision, so bailing when any digit run exceeds the limit covers both. 

65 Nested replacement fields ("{:>{}}") are already resolved by ``Formatter`` 

66 before ``format_field`` runs, so this also catches sizes passed as 

67 arguments. 

68 """ 

69 

70 def format_field(self, value: object, format_spec: str) -> str: 

71 for size in re.findall(r"\d+", format_spec): 

72 if int(size) > _MAX_FORMAT_FIELD_SIZE: 

73 raise ValueError("format field size exceeds inference limit") 

74 return cast(str, super().format_field(value, format_spec)) 

75 

76 return _BoundedFormatter() 

77 

78 

79STR_CLASS = """ 

80class whatever(object): 

81 def join(self, iterable): 

82 return {rvalue} 

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

84 return {rvalue} 

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

86 return {rvalue} 

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

88 return b'' 

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

90 return u'' 

91 def capitalize(self): 

92 return {rvalue} 

93 def title(self): 

94 return {rvalue} 

95 def lower(self): 

96 return {rvalue} 

97 def upper(self): 

98 return {rvalue} 

99 def swapcase(self): 

100 return {rvalue} 

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

102 return 0 

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

104 return 0 

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

106 return 0 

107 def strip(self, chars=None): 

108 return {rvalue} 

109 def lstrip(self, chars=None): 

110 return {rvalue} 

111 def rstrip(self, chars=None): 

112 return {rvalue} 

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

114 return {rvalue} 

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

116 return {rvalue} 

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

118 return {rvalue} 

119""" 

120 

121 

122BYTES_CLASS = """ 

123class whatever(object): 

124 def join(self, iterable): 

125 return {rvalue} 

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

127 return {rvalue} 

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

129 return u'' 

130 def capitalize(self): 

131 return {rvalue} 

132 def title(self): 

133 return {rvalue} 

134 def lower(self): 

135 return {rvalue} 

136 def upper(self): 

137 return {rvalue} 

138 def swapcase(self): 

139 return {rvalue} 

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

141 return 0 

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

143 return 0 

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

145 return 0 

146 def strip(self, chars=None): 

147 return {rvalue} 

148 def lstrip(self, chars=None): 

149 return {rvalue} 

150 def rstrip(self, chars=None): 

151 return {rvalue} 

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

153 return {rvalue} 

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

155 return {rvalue} 

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

157 return {rvalue} 

158""" 

159 

160 

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

162 raise UseInferenceDefault() 

163 

164 

165def _extend_string_class(class_node, code, rvalue): 

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

167 code = code.format(rvalue=rvalue) 

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

169 for method in fake.mymethods(): 

170 method.parent = class_node 

171 method.lineno = None 

172 method.col_offset = None 

173 if "__class__" in method.locals: 

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

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

176 method.parent = class_node 

177 

178 

179def _extend_builtins(class_transforms): 

180 builtin_ast = AstroidManager().builtins_module 

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

182 transform(builtin_ast[class_name]) 

183 

184 

185def on_bootstrap(): 

186 """Called by astroid_bootstrapping().""" 

187 _extend_builtins( 

188 { 

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

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

191 } 

192 ) 

193 

194 

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

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

197 if ( 

198 builtin_name == "type" 

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

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

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

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

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

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

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

206 ): 

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

208 # Match these patterns from stdlib/re.py 

209 # ```py 

210 # Pattern = type(...) 

211 # Match = type(...) 

212 # ``` 

213 return False 

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

215 return node.func.name == builtin_name 

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

217 return ( 

218 node.func.attrname == "fromkeys" 

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

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

221 ) 

222 return False 

223 

224 

225def register_builtin_transform( 

226 manager: AstroidManager, transform, builtin_name 

227) -> None: 

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

229 

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

231 an optional context. 

232 """ 

233 

234 def _transform_wrapper( 

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

236 ) -> Iterator: 

237 result = transform(node, context=context) 

238 if result: 

239 if not result.parent: 

240 # Let the transformation function determine 

241 # the parent for its result. Otherwise, 

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

243 result.parent = node 

244 

245 if result.lineno is None: 

246 result.lineno = node.lineno 

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

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

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

250 result.col_offset = node.col_offset 

251 return iter([result]) 

252 

253 manager.register_transform( 

254 nodes.Call, 

255 inference_tip(_transform_wrapper), 

256 partial(_builtin_filter_predicate, builtin_name=builtin_name), 

257 ) 

258 

259 

260def _container_generic_inference( 

261 node: nodes.Call, 

262 context: InferenceContext | None, 

263 node_type: type[nodes.BaseContainer], 

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

265) -> nodes.BaseContainer: 

266 args = node.args 

267 if not args: 

268 return node_type( 

269 lineno=node.lineno, 

270 col_offset=node.col_offset, 

271 parent=node.parent, 

272 end_lineno=node.end_lineno, 

273 end_col_offset=node.end_col_offset, 

274 ) 

275 if len(node.args) > 1: 

276 raise UseInferenceDefault() 

277 

278 (arg,) = args 

279 transformed = transform(arg) 

280 if not transformed: 

281 try: 

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

283 except (InferenceError, StopIteration) as exc: 

284 raise UseInferenceDefault from exc 

285 if isinstance(inferred, util.UninferableBase): 

286 raise UseInferenceDefault 

287 transformed = transform(inferred) 

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

289 raise UseInferenceDefault 

290 return transformed 

291 

292 

293def _container_generic_transform( 

294 arg: SuccessfulInferenceResult, 

295 context: InferenceContext | None, 

296 klass: type[nodes.BaseContainer], 

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

298 build_elts: BuiltContainers, 

299) -> nodes.BaseContainer | None: 

300 elts: Iterable | str | bytes 

301 

302 if isinstance(arg, klass): 

303 return arg 

304 if isinstance(arg, iterables): 

305 arg = cast((nodes.BaseContainer | ContainerObjects), arg) 

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

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

308 else: 

309 # TODO: Does not handle deduplication for sets. 

310 elts = [] 

311 for element in arg.elts: 

312 if not element: 

313 continue 

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

315 if inferred: 

316 evaluated_object = nodes.EvaluatedObject( 

317 original=element, value=inferred 

318 ) 

319 elts.append(evaluated_object) 

320 elif isinstance(arg, nodes.Dict): 

321 # Dicts need to have consts as strings already. 

322 elts = [ 

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

324 for item in arg.items 

325 ] 

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

327 # Don't expand an oversized string into one Const node per character. 

328 if len(arg.value) > _MAX_INFERABLE_STR_LEN: 

329 return None 

330 elts = arg.value 

331 else: 

332 return None 

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

334 

335 

336def _infer_builtin_container( 

337 node: nodes.Call, 

338 context: InferenceContext | None, 

339 klass: type[nodes.BaseContainer], 

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

341 build_elts: BuiltContainers, 

342) -> nodes.BaseContainer: 

343 transform_func = partial( 

344 _container_generic_transform, 

345 context=context, 

346 klass=klass, 

347 iterables=iterables, 

348 build_elts=build_elts, 

349 ) 

350 

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

352 

353 

354# pylint: disable=invalid-name 

355infer_tuple = partial( 

356 _infer_builtin_container, 

357 klass=nodes.Tuple, 

358 iterables=( 

359 nodes.List, 

360 nodes.Set, 

361 objects.FrozenSet, 

362 objects.DictItems, 

363 objects.DictKeys, 

364 objects.DictValues, 

365 ), 

366 build_elts=tuple, 

367) 

368 

369infer_list = partial( 

370 _infer_builtin_container, 

371 klass=nodes.List, 

372 iterables=( 

373 nodes.Tuple, 

374 nodes.Set, 

375 objects.FrozenSet, 

376 objects.DictItems, 

377 objects.DictKeys, 

378 objects.DictValues, 

379 ), 

380 build_elts=list, 

381) 

382 

383infer_set = partial( 

384 _infer_builtin_container, 

385 klass=nodes.Set, 

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

387 build_elts=set, 

388) 

389 

390infer_frozenset = partial( 

391 _infer_builtin_container, 

392 klass=objects.FrozenSet, 

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

394 build_elts=frozenset, 

395) 

396 

397 

398def _get_elts(arg, context): 

399 def is_iterable(n) -> bool: 

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

401 

402 try: 

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

404 except (InferenceError, StopIteration) as exc: 

405 raise UseInferenceDefault from exc 

406 if isinstance(inferred, nodes.Dict): 

407 items = inferred.items 

408 elif is_iterable(inferred): 

409 items = [] 

410 for elt in inferred.elts: 

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

412 # then fallback to the default inference. 

413 # Also, take in consideration only hashable items, 

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

415 if not is_iterable(elt): 

416 raise UseInferenceDefault() 

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

418 raise UseInferenceDefault() 

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

420 raise UseInferenceDefault() 

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

422 else: 

423 raise UseInferenceDefault() 

424 return items 

425 

426 

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

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

429 

430 The function treats the following cases: 

431 

432 * dict() 

433 * dict(mapping) 

434 * dict(iterable) 

435 * dict(iterable, **kwargs) 

436 * dict(mapping, **kwargs) 

437 * dict(**kwargs) 

438 

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

440 """ 

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

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

443 raise UseInferenceDefault 

444 

445 args = call.positional_arguments 

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

447 

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

449 if not args and not kwargs: 

450 # dict() 

451 return nodes.Dict( 

452 lineno=node.lineno, 

453 col_offset=node.col_offset, 

454 parent=node.parent, 

455 end_lineno=node.end_lineno, 

456 end_col_offset=node.end_col_offset, 

457 ) 

458 if kwargs and not args: 

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

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

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

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

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

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

465 items = elts + keys 

466 elif len(args) == 1: 

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

468 else: 

469 raise UseInferenceDefault() 

470 value = nodes.Dict( 

471 col_offset=node.col_offset, 

472 lineno=node.lineno, 

473 parent=node.parent, 

474 end_lineno=node.end_lineno, 

475 end_col_offset=node.end_col_offset, 

476 ) 

477 value.postinit(items) 

478 return value 

479 

480 

481def _mro_owner(cls: nodes.ClassDef, context: InferenceContext | None) -> nodes.ClassDef: 

482 """Return the class whose mro a ``super()`` call with no argument walks. 

483 

484 ``super()`` with no argument is ``super(__class__, self)``. It starts the 

485 lookup after the class the method is written in, but it walks the mro of the 

486 object the method was called on, which can be a subclass, or a class that 

487 only meets the method's own class in the mro of that subclass (a mixin). 

488 ``context.boundnode`` is that object when it is known. 

489 """ 

490 bound = context.boundnode if context is not None else None 

491 if isinstance(bound, Instance): 

492 bound_cls = bound._proxied 

493 elif isinstance(bound, nodes.ClassDef): 

494 bound_cls = bound 

495 else: 

496 return cls 

497 try: 

498 if cls in bound_cls.mro(): 

499 return bound_cls 

500 except MroError: 

501 pass 

502 return cls 

503 

504 

505def infer_super( 

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

507) -> objects.Super: 

508 """Understand super calls. 

509 

510 There are some restrictions for what can be understood: 

511 

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

513 

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

515 then the default inference will be used. 

516 

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

518 will be used. 

519 """ 

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

521 # Ignore unbounded super. 

522 raise UseInferenceDefault 

523 

524 scope = node.scope() 

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

526 # Ignore non-method uses of super. 

527 raise UseInferenceDefault 

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

529 # Not interested in staticmethods. 

530 raise UseInferenceDefault 

531 

532 cls = scoped_nodes.get_wrapping_class(scope) 

533 assert cls is not None 

534 if not node.args: 

535 mro_pointer = cls 

536 mro_owner = _mro_owner(cls, context) 

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

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

539 if scope.type == "classmethod": 

540 mro_type = mro_owner 

541 else: 

542 mro_type = mro_owner.instantiate_class() 

543 else: 

544 try: 

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

546 except (InferenceError, StopIteration) as exc: 

547 raise UseInferenceDefault from exc 

548 try: 

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

550 except (InferenceError, StopIteration) as exc: 

551 raise UseInferenceDefault from exc 

552 

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

554 mro_type, util.UninferableBase 

555 ): 

556 # No way we could understand this. 

557 raise UseInferenceDefault 

558 

559 super_obj = objects.Super( 

560 mro_pointer=mro_pointer, 

561 mro_type=mro_type, 

562 self_class=cls, 

563 scope=scope, 

564 call=node, 

565 ) 

566 super_obj.parent = node 

567 return super_obj 

568 

569 

570def _infer_getattr_args(node, context): 

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

572 # Not a valid getattr call. 

573 raise UseInferenceDefault 

574 

575 try: 

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

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

578 except (InferenceError, StopIteration) as exc: 

579 raise UseInferenceDefault from exc 

580 

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

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

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

584 # which is unknown. 

585 return util.Uninferable, util.Uninferable 

586 

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

588 if not is_string: 

589 raise UseInferenceDefault 

590 

591 return obj, attr.value 

592 

593 

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

595 """Understand getattr calls. 

596 

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

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

599 lookup will be done. 

600 """ 

601 obj, attr = _infer_getattr_args(node, context) 

602 if ( 

603 isinstance(obj, util.UninferableBase) 

604 or isinstance(attr, util.UninferableBase) 

605 or not hasattr(obj, "igetattr") 

606 ): 

607 return util.Uninferable 

608 

609 try: 

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

611 except (StopIteration, InferenceError, AttributeInferenceError): 

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

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

614 try: 

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

616 except (StopIteration, InferenceError) as exc: 

617 raise UseInferenceDefault from exc 

618 

619 raise UseInferenceDefault 

620 

621 

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

623 """Understand hasattr calls. 

624 

625 This always guarantees three possible outcomes for calling 

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

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

628 we know that the object has the attribute and Uninferable 

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

630 """ 

631 try: 

632 obj, attr = _infer_getattr_args(node, context) 

633 if ( 

634 isinstance(obj, util.UninferableBase) 

635 or isinstance(attr, util.UninferableBase) 

636 or not hasattr(obj, "getattr") 

637 ): 

638 return util.Uninferable 

639 obj.getattr(attr, context=context) 

640 except UseInferenceDefault: 

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

642 return util.Uninferable 

643 except AttributeInferenceError: 

644 # Doesn't have it. 

645 return nodes.Const(False) 

646 return nodes.Const(True) 

647 

648 

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

650 """Understand callable calls. 

651 

652 This follows Python's semantics, where an object 

653 is callable if it provides an attribute __call__, 

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

655 called. 

656 """ 

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

658 # Invalid callable call. 

659 raise UseInferenceDefault 

660 

661 argument = node.args[0] 

662 try: 

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

664 except (InferenceError, StopIteration): 

665 return util.Uninferable 

666 if isinstance(inferred, util.UninferableBase): 

667 return util.Uninferable 

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

669 

670 

671def infer_property( 

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

673) -> objects.Property: 

674 """Understand `property` class. 

675 

676 This only infers the output of `property` 

677 call, not the arguments themselves. 

678 """ 

679 if len(node.args) < 1: 

680 # Invalid property call. 

681 raise UseInferenceDefault 

682 

683 getter = node.args[0] 

684 try: 

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

686 except (InferenceError, StopIteration) as exc: 

687 raise UseInferenceDefault from exc 

688 

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

690 raise UseInferenceDefault 

691 

692 prop_func = objects.Property( 

693 function=inferred, 

694 name="<property>", 

695 lineno=node.lineno, 

696 col_offset=node.col_offset, 

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

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

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

700 # definition on the spot, ad-hoc. 

701 parent=scoped_nodes.SYNTHETIC_ROOT, 

702 ) 

703 prop_func.postinit( 

704 body=[], 

705 args=inferred.args, 

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

707 ) 

708 return prop_func 

709 

710 

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

712 """Understand bool calls.""" 

713 if len(node.args) > 1: 

714 # Invalid bool call. 

715 raise UseInferenceDefault 

716 

717 if not node.args: 

718 return nodes.Const(False) 

719 

720 argument = node.args[0] 

721 try: 

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

723 except (InferenceError, StopIteration): 

724 return util.Uninferable 

725 if isinstance(inferred, util.UninferableBase): 

726 return util.Uninferable 

727 

728 bool_value = inferred.bool_value(context=context) 

729 if isinstance(bool_value, util.UninferableBase): 

730 return util.Uninferable 

731 return nodes.Const(bool_value) 

732 

733 

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

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

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

737 raise UseInferenceDefault 

738 

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

740 

741 

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

743 """Understand `slice` calls.""" 

744 args = node.args 

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

746 raise UseInferenceDefault 

747 

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

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

750 for arg in args: 

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

752 raise UseInferenceDefault 

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

754 raise UseInferenceDefault 

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

756 raise UseInferenceDefault 

757 

758 if len(args) < 3: 

759 # Make sure we have 3 arguments. 

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

761 

762 slice_node = nodes.Slice( 

763 lineno=node.lineno, 

764 col_offset=node.col_offset, 

765 parent=node.parent, 

766 end_lineno=node.end_lineno, 

767 end_col_offset=node.end_col_offset, 

768 ) 

769 slice_node.postinit(*args) 

770 return slice_node 

771 

772 

773def _infer_object__new__decorator( 

774 node: nodes.ClassDef, 

775 context: InferenceContext | None = None, 

776) -> Iterator[Instance]: 

777 # Instantiate class immediately 

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

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

780 

781 

782def _infer_object__new__decorator_check(node) -> bool: 

783 """Predicate before inference_tip. 

784 

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

786 """ 

787 if not node.decorators: 

788 return False 

789 

790 for decorator in node.decorators.nodes: 

791 if isinstance(decorator, nodes.Attribute): 

792 if decorator.as_string() == OBJECT_DUNDER_NEW: 

793 return True 

794 return False 

795 

796 

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

798 """Infer issubclass() calls. 

799 

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

801 :param InferenceContext context: the context for the inference 

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

803 :raises UseInferenceDefault: If the node cannot be inferred 

804 """ 

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

806 if call.keyword_arguments: 

807 # issubclass doesn't support keyword arguments 

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

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

810 raise UseInferenceDefault( 

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

812 ) 

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

814 obj_node, class_or_tuple_node = call.positional_arguments 

815 

816 try: 

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

818 except (InferenceError, StopIteration) as exc: 

819 raise UseInferenceDefault from exc 

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

821 raise UseInferenceDefault( 

822 f"TypeError: arg 1 must be class, not {type(obj_type)!r}" 

823 ) 

824 

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

826 # object is to be checked against. 

827 try: 

828 class_container = helpers.class_or_tuple_to_container( 

829 class_or_tuple_node, context=context 

830 ) 

831 except InferenceError as exc: 

832 raise UseInferenceDefault from exc 

833 try: 

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

835 except AstroidTypeError as exc: 

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

837 except MroError as exc: 

838 raise UseInferenceDefault from exc 

839 return nodes.Const(issubclass_bool) 

840 

841 

842def infer_isinstance( 

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

844) -> nodes.Const: 

845 """Infer isinstance calls. 

846 

847 :param nodes.Call callnode: an isinstance call 

848 :raises UseInferenceDefault: If the node cannot be inferred 

849 """ 

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

851 if call.keyword_arguments: 

852 # isinstance doesn't support keyword arguments 

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

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

855 raise UseInferenceDefault( 

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

857 ) 

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

859 obj_node, class_or_tuple_node = call.positional_arguments 

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

861 # obj is to be check is an instance of 

862 try: 

863 class_container = helpers.class_or_tuple_to_container( 

864 class_or_tuple_node, context=context 

865 ) 

866 except InferenceError as exc: 

867 raise UseInferenceDefault from exc 

868 try: 

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

870 except AstroidTypeError as exc: 

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

872 except MroError as exc: 

873 raise UseInferenceDefault from exc 

874 if isinstance(isinstance_bool, util.UninferableBase): 

875 raise UseInferenceDefault 

876 return nodes.Const(isinstance_bool) 

877 

878 

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

880 """Infer length calls. 

881 

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

883 :param context.InferenceContext: node context 

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

885 """ 

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

887 if call.keyword_arguments: 

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

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

890 raise UseInferenceDefault( 

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

892 f"({len(call.positional_arguments)}) given" 

893 ) 

894 [argument_node] = call.positional_arguments 

895 

896 try: 

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

898 except (AstroidTypeError, InferenceError) as exc: 

899 raise UseInferenceDefault(str(exc)) from exc 

900 

901 

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

903 """Infer str() calls. 

904 

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

906 :param context.InferenceContext: node context 

907 :rtype nodes.Const: 

908 a Const containing a stringified value of str() call if possible, else an empty string 

909 """ 

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

911 if call.keyword_arguments: 

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

913 

914 fallback = nodes.Const("") 

915 

916 if not call.positional_arguments: 

917 return fallback 

918 

919 # Accept only if all inferred values resolve to the same string 

920 candidates: set[str] = set() 

921 try: 

922 for inferred in call.positional_arguments[0].infer(context=context): 

923 if not isinstance(inferred, nodes.Const): 

924 return fallback 

925 

926 try: 

927 candidates.add(str(inferred.value)) 

928 except ValueError: 

929 return fallback 

930 except InferenceError: 

931 return fallback 

932 

933 if len(candidates) == 1: 

934 return nodes.Const(next(iter(candidates))) 

935 return fallback 

936 

937 

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

939 """Infer int() calls. 

940 

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

942 :param context.InferenceContext: node context 

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

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 

949 if call.positional_arguments: 

950 try: 

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

952 except (InferenceError, StopIteration) as exc: 

953 raise UseInferenceDefault(str(exc)) from exc 

954 

955 if isinstance(first_value, util.UninferableBase): 

956 raise UseInferenceDefault 

957 

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

959 first_value.value, (int, str) 

960 ): 

961 try: 

962 actual_value = int(first_value.value) 

963 except ValueError: 

964 return nodes.Const(0) 

965 return nodes.Const(actual_value) 

966 

967 return nodes.Const(0) 

968 

969 

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

971 """Infer dict.fromkeys. 

972 

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

974 :param context.InferenceContext context: node context 

975 :rtype nodes.Dict: 

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

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

978 will be inferred instead. 

979 """ 

980 

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

982 new_node = nodes.Dict( 

983 col_offset=node.col_offset, 

984 lineno=node.lineno, 

985 parent=node.parent, 

986 end_lineno=node.end_lineno, 

987 end_col_offset=node.end_col_offset, 

988 ) 

989 new_node.postinit(elements) 

990 return new_node 

991 

992 def _unique_const_keys(keys: Iterable[nodes.Const]) -> list[nodes.Const]: 

993 # dict.fromkeys deduplicates its keys, so keep only the first Const seen 

994 # for a given value. Emitting one entry per element is wrong 

995 # (dict.fromkeys("aab") has keys "a", "b") and lets a repeated string 

996 # balloon the inferred dict. 

997 seen: dict[object, nodes.Const] = {} 

998 for key in keys: 

999 seen.setdefault(key.value, key) 

1000 return list(seen.values()) 

1001 

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

1003 if call.keyword_arguments: 

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

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

1006 raise UseInferenceDefault( 

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

1008 ) 

1009 

1010 default = nodes.Const(None) 

1011 values = call.positional_arguments[0] 

1012 try: 

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

1014 except (InferenceError, StopIteration): 

1015 return _build_dict_with_elements([]) 

1016 if inferred_values is util.Uninferable: 

1017 return _build_dict_with_elements([]) 

1018 

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

1020 accepted_iterable_elements = (nodes.Const,) 

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

1022 elements = inferred_values.elts 

1023 for element in elements: 

1024 if not isinstance(element, accepted_iterable_elements): 

1025 # Fallback to an empty dict 

1026 return _build_dict_with_elements([]) 

1027 

1028 elements_with_value = [ 

1029 (element, default) for element in _unique_const_keys(elements) 

1030 ] 

1031 return _build_dict_with_elements(elements_with_value) 

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

1033 inferred_values.value, (str, bytes) 

1034 ): 

1035 # Same cap as the container builders above. 

1036 if len(inferred_values.value) > _MAX_INFERABLE_STR_LEN: 

1037 return _build_dict_with_elements([]) 

1038 # Deduplicate the characters/bytes before building Const nodes so that a 

1039 # compact but large string, e.g. dict.fromkeys("x" * 10**8), doesn't 

1040 # materialize one node per character for what is a single-key dict. 

1041 elements_with_value = [ 

1042 (nodes.Const(element), default) 

1043 for element in dict.fromkeys(inferred_values.value) 

1044 ] 

1045 return _build_dict_with_elements(elements_with_value) 

1046 if isinstance(inferred_values, nodes.Dict): 

1047 keys = inferred_values.itered() 

1048 for key in keys: 

1049 if not isinstance(key, accepted_iterable_elements): 

1050 # Fallback to an empty dict 

1051 return _build_dict_with_elements([]) 

1052 

1053 elements_with_value = [ 

1054 (element, default) for element in _unique_const_keys(keys) 

1055 ] 

1056 return _build_dict_with_elements(elements_with_value) 

1057 

1058 # Fallback to an empty dictionary 

1059 return _build_dict_with_elements([]) 

1060 

1061 

1062def _infer_copy_method( 

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

1064) -> Iterator[CopyResult]: 

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

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

1067 if all( 

1068 isinstance( 

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

1070 ) 

1071 for inferred_node in inferred_orig 

1072 ): 

1073 return cast(Iterator[CopyResult], inferred_copy) 

1074 

1075 raise UseInferenceDefault 

1076 

1077 

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

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

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

1081 return False 

1082 

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

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

1085 else: 

1086 value = node.func.expr 

1087 

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

1089 

1090 

1091def _infer_str_format_call( 

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

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

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

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

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

1097 

1098 value: nodes.Const 

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

1100 if not ( 

1101 (inferred := util.safe_infer(node.func.expr)) 

1102 and isinstance(inferred, nodes.Const) 

1103 ): 

1104 return iter([util.Uninferable]) 

1105 value = inferred 

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

1107 value = node.func.expr 

1108 else: # pragma: no cover 

1109 return iter([util.Uninferable]) 

1110 

1111 format_template = value.value 

1112 

1113 # Get the positional arguments passed 

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

1115 for i in call.positional_arguments: 

1116 one_inferred = util.safe_infer(i, context) 

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

1118 return iter([util.Uninferable]) 

1119 inferred_positional.append(one_inferred) 

1120 

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

1122 

1123 # Get the keyword arguments passed 

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

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

1126 one_inferred = util.safe_infer(v, context) 

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

1128 return iter([util.Uninferable]) 

1129 inferred_keyword[k] = one_inferred 

1130 

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

1132 

1133 formatter = _get_bounded_formatter() 

1134 try: 

1135 fields = list(formatter.parse(format_template)) 

1136 except ValueError: 

1137 return iter([util.Uninferable]) 

1138 if any(spec and util.format_spec_too_large(spec) for _, _, spec, _ in fields): 

1139 return iter([util.Uninferable]) 

1140 

1141 try: 

1142 formatted_string = formatter.format( 

1143 format_template, *pos_values, **keyword_values 

1144 ) 

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

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

1147 # IndexError: there are too few arguments to interpolate 

1148 # TypeError: Unsupported format string 

1149 # ValueError: Unknown format code 

1150 return iter([util.Uninferable]) 

1151 

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

1153 

1154 

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

1156 # Builtins inference 

1157 register_builtin_transform(manager, infer_bool, "bool") 

1158 register_builtin_transform(manager, infer_super, "super") 

1159 register_builtin_transform(manager, infer_callable, "callable") 

1160 register_builtin_transform(manager, infer_property, "property") 

1161 register_builtin_transform(manager, infer_getattr, "getattr") 

1162 register_builtin_transform(manager, infer_hasattr, "hasattr") 

1163 register_builtin_transform(manager, infer_tuple, "tuple") 

1164 register_builtin_transform(manager, infer_set, "set") 

1165 register_builtin_transform(manager, infer_list, "list") 

1166 register_builtin_transform(manager, infer_dict, "dict") 

1167 register_builtin_transform(manager, infer_frozenset, "frozenset") 

1168 register_builtin_transform(manager, infer_type, "type") 

1169 register_builtin_transform(manager, infer_slice, "slice") 

1170 register_builtin_transform(manager, infer_isinstance, "isinstance") 

1171 register_builtin_transform(manager, infer_issubclass, "issubclass") 

1172 register_builtin_transform(manager, infer_len, "len") 

1173 register_builtin_transform(manager, infer_str, "str") 

1174 register_builtin_transform(manager, infer_int, "int") 

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

1176 

1177 # Infer object.__new__ calls 

1178 manager.register_transform( 

1179 nodes.ClassDef, 

1180 inference_tip(_infer_object__new__decorator), 

1181 _infer_object__new__decorator_check, 

1182 ) 

1183 

1184 manager.register_transform( 

1185 nodes.Call, 

1186 inference_tip(_infer_copy_method), 

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

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

1189 ) 

1190 

1191 manager.register_transform( 

1192 nodes.Call, 

1193 inference_tip(_infer_str_format_call), 

1194 _is_str_format_call, 

1195 )