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

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 

11from functools import partial 

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

13 

14from astroid import arguments, helpers, inference_tip, 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.manager import AstroidManager 

25from astroid.nodes import scoped_nodes 

26from astroid.typing import ( 

27 ConstFactoryResult, 

28 InferenceResult, 

29 SuccessfulInferenceResult, 

30) 

31 

32if TYPE_CHECKING: 

33 from astroid.bases import Instance 

34 

35ContainerObjects = Union[ 

36 objects.FrozenSet, 

37 objects.DictItems, 

38 objects.DictKeys, 

39 objects.DictValues, 

40] 

41 

42BuiltContainers = Union[ 

43 Type[tuple], 

44 Type[list], 

45 Type[set], 

46 Type[frozenset], 

47] 

48 

49CopyResult = Union[ 

50 nodes.Dict, 

51 nodes.List, 

52 nodes.Set, 

53 objects.FrozenSet, 

54] 

55 

56OBJECT_DUNDER_NEW = "object.__new__" 

57 

58STR_CLASS = """ 

59class whatever(object): 

60 def join(self, iterable): 

61 return {rvalue} 

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

63 return {rvalue} 

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

65 return {rvalue} 

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

67 return b'' 

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

69 return u'' 

70 def capitalize(self): 

71 return {rvalue} 

72 def title(self): 

73 return {rvalue} 

74 def lower(self): 

75 return {rvalue} 

76 def upper(self): 

77 return {rvalue} 

78 def swapcase(self): 

79 return {rvalue} 

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

81 return 0 

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

83 return 0 

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

85 return 0 

86 def strip(self, chars=None): 

87 return {rvalue} 

88 def lstrip(self, chars=None): 

89 return {rvalue} 

90 def rstrip(self, chars=None): 

91 return {rvalue} 

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

93 return {rvalue} 

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

95 return {rvalue} 

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

97 return {rvalue} 

98""" 

99 

100 

101BYTES_CLASS = """ 

102class whatever(object): 

103 def join(self, iterable): 

104 return {rvalue} 

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

106 return {rvalue} 

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

108 return u'' 

109 def capitalize(self): 

110 return {rvalue} 

111 def title(self): 

112 return {rvalue} 

113 def lower(self): 

114 return {rvalue} 

115 def upper(self): 

116 return {rvalue} 

117 def swapcase(self): 

118 return {rvalue} 

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

120 return 0 

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

122 return 0 

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

124 return 0 

125 def strip(self, chars=None): 

126 return {rvalue} 

127 def lstrip(self, chars=None): 

128 return {rvalue} 

129 def rstrip(self, chars=None): 

130 return {rvalue} 

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

132 return {rvalue} 

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

134 return {rvalue} 

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

136 return {rvalue} 

137""" 

138 

139 

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

141 raise UseInferenceDefault() 

142 

143 

144def _extend_string_class(class_node, code, rvalue): 

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

146 code = code.format(rvalue=rvalue) 

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

148 for method in fake.mymethods(): 

149 method.parent = class_node 

150 method.lineno = None 

151 method.col_offset = None 

152 if "__class__" in method.locals: 

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

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

155 method.parent = class_node 

156 

157 

158def _extend_builtins(class_transforms): 

159 builtin_ast = AstroidManager().builtins_module 

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

161 transform(builtin_ast[class_name]) 

162 

163 

164def on_bootstrap(): 

165 """Called by astroid_bootstrapping().""" 

166 _extend_builtins( 

167 { 

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

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

170 } 

171 ) 

172 

173 

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

175 if ( 

176 builtin_name == "type" 

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

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

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

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

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

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

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

184 ): 

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

186 # Match these patterns from stdlib/re.py 

187 # ```py 

188 # Pattern = type(...) 

189 # Match = type(...) 

190 # ``` 

191 return False 

192 if isinstance(node.func, nodes.Name) and node.func.name == builtin_name: 

193 return True 

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

195 return ( 

196 node.func.attrname == "fromkeys" 

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

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

199 ) 

200 return False 

201 

202 

203def register_builtin_transform( 

204 manager: AstroidManager, transform, builtin_name 

205) -> None: 

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

207 

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

209 an optional context. 

210 """ 

211 

212 def _transform_wrapper( 

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

214 ) -> Iterator: 

215 result = transform(node, context=context) 

216 if result: 

217 if not result.parent: 

218 # Let the transformation function determine 

219 # the parent for its result. Otherwise, 

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

221 result.parent = node 

222 

223 if result.lineno is None: 

224 result.lineno = node.lineno 

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

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

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

228 result.col_offset = node.col_offset 

229 return iter([result]) 

230 

231 manager.register_transform( 

232 nodes.Call, 

233 inference_tip(_transform_wrapper), 

234 partial(_builtin_filter_predicate, builtin_name=builtin_name), 

235 ) 

236 

237 

238def _container_generic_inference( 

239 node: nodes.Call, 

240 context: InferenceContext | None, 

241 node_type: type[nodes.BaseContainer], 

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

243) -> nodes.BaseContainer: 

244 args = node.args 

245 if not args: 

246 return node_type( 

247 lineno=node.lineno, 

248 col_offset=node.col_offset, 

249 parent=node.parent, 

250 end_lineno=node.end_lineno, 

251 end_col_offset=node.end_col_offset, 

252 ) 

253 if len(node.args) > 1: 

254 raise UseInferenceDefault() 

255 

256 (arg,) = args 

257 transformed = transform(arg) 

258 if not transformed: 

259 try: 

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

261 except (InferenceError, StopIteration) as exc: 

262 raise UseInferenceDefault from exc 

263 if isinstance(inferred, util.UninferableBase): 

264 raise UseInferenceDefault 

265 transformed = transform(inferred) 

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

267 raise UseInferenceDefault 

268 return transformed 

269 

270 

271def _container_generic_transform( 

272 arg: SuccessfulInferenceResult, 

273 context: InferenceContext | None, 

274 klass: type[nodes.BaseContainer], 

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

276 build_elts: BuiltContainers, 

277) -> nodes.BaseContainer | None: 

278 elts: Iterable | str | bytes 

279 

280 if isinstance(arg, klass): 

281 return arg 

282 if isinstance(arg, iterables): 

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

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

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

286 else: 

287 # TODO: Does not handle deduplication for sets. 

288 elts = [] 

289 for element in arg.elts: 

290 if not element: 

291 continue 

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

293 if inferred: 

294 evaluated_object = nodes.EvaluatedObject( 

295 original=element, value=inferred 

296 ) 

297 elts.append(evaluated_object) 

298 elif isinstance(arg, nodes.Dict): 

299 # Dicts need to have consts as strings already. 

300 elts = [ 

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

302 for item in arg.items 

303 ] 

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

305 elts = arg.value 

306 else: 

307 return None 

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

309 

310 

311def _infer_builtin_container( 

312 node: nodes.Call, 

313 context: InferenceContext | None, 

314 klass: type[nodes.BaseContainer], 

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

316 build_elts: BuiltContainers, 

317) -> nodes.BaseContainer: 

318 transform_func = partial( 

319 _container_generic_transform, 

320 context=context, 

321 klass=klass, 

322 iterables=iterables, 

323 build_elts=build_elts, 

324 ) 

325 

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

327 

328 

329# pylint: disable=invalid-name 

330infer_tuple = partial( 

331 _infer_builtin_container, 

332 klass=nodes.Tuple, 

333 iterables=( 

334 nodes.List, 

335 nodes.Set, 

336 objects.FrozenSet, 

337 objects.DictItems, 

338 objects.DictKeys, 

339 objects.DictValues, 

340 ), 

341 build_elts=tuple, 

342) 

343 

344infer_list = partial( 

345 _infer_builtin_container, 

346 klass=nodes.List, 

347 iterables=( 

348 nodes.Tuple, 

349 nodes.Set, 

350 objects.FrozenSet, 

351 objects.DictItems, 

352 objects.DictKeys, 

353 objects.DictValues, 

354 ), 

355 build_elts=list, 

356) 

357 

358infer_set = partial( 

359 _infer_builtin_container, 

360 klass=nodes.Set, 

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

362 build_elts=set, 

363) 

364 

365infer_frozenset = partial( 

366 _infer_builtin_container, 

367 klass=objects.FrozenSet, 

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

369 build_elts=frozenset, 

370) 

371 

372 

373def _get_elts(arg, context): 

374 def is_iterable(n): 

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

376 

377 try: 

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

379 except (InferenceError, StopIteration) as exc: 

380 raise UseInferenceDefault from exc 

381 if isinstance(inferred, nodes.Dict): 

382 items = inferred.items 

383 elif is_iterable(inferred): 

384 items = [] 

385 for elt in inferred.elts: 

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

387 # then fallback to the default inference. 

388 # Also, take in consideration only hashable items, 

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

390 if not is_iterable(elt): 

391 raise UseInferenceDefault() 

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

393 raise UseInferenceDefault() 

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

395 raise UseInferenceDefault() 

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

397 else: 

398 raise UseInferenceDefault() 

399 return items 

400 

401 

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

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

404 

405 The function treats the following cases: 

406 

407 * dict() 

408 * dict(mapping) 

409 * dict(iterable) 

410 * dict(iterable, **kwargs) 

411 * dict(mapping, **kwargs) 

412 * dict(**kwargs) 

413 

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

415 """ 

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

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

418 raise UseInferenceDefault 

419 

420 args = call.positional_arguments 

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

422 

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

424 if not args and not kwargs: 

425 # dict() 

426 return nodes.Dict( 

427 lineno=node.lineno, 

428 col_offset=node.col_offset, 

429 parent=node.parent, 

430 end_lineno=node.end_lineno, 

431 end_col_offset=node.end_col_offset, 

432 ) 

433 if kwargs and not args: 

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

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

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

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

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

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

440 items = elts + keys 

441 elif len(args) == 1: 

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

443 else: 

444 raise UseInferenceDefault() 

445 value = nodes.Dict( 

446 col_offset=node.col_offset, 

447 lineno=node.lineno, 

448 parent=node.parent, 

449 end_lineno=node.end_lineno, 

450 end_col_offset=node.end_col_offset, 

451 ) 

452 value.postinit(items) 

453 return value 

454 

455 

456def infer_super( 

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

458) -> objects.Super: 

459 """Understand super calls. 

460 

461 There are some restrictions for what can be understood: 

462 

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

464 

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

466 then the default inference will be used. 

467 

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

469 will be used. 

470 """ 

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

472 # Ignore unbounded super. 

473 raise UseInferenceDefault 

474 

475 scope = node.scope() 

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

477 # Ignore non-method uses of super. 

478 raise UseInferenceDefault 

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

480 # Not interested in staticmethods. 

481 raise UseInferenceDefault 

482 

483 cls = scoped_nodes.get_wrapping_class(scope) 

484 assert cls is not None 

485 if not node.args: 

486 mro_pointer = cls 

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

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

489 if scope.type == "classmethod": 

490 mro_type = cls 

491 else: 

492 mro_type = cls.instantiate_class() 

493 else: 

494 try: 

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

496 except (InferenceError, StopIteration) as exc: 

497 raise UseInferenceDefault from exc 

498 try: 

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

500 except (InferenceError, StopIteration) as exc: 

501 raise UseInferenceDefault from exc 

502 

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

504 mro_type, util.UninferableBase 

505 ): 

506 # No way we could understand this. 

507 raise UseInferenceDefault 

508 

509 super_obj = objects.Super( 

510 mro_pointer=mro_pointer, 

511 mro_type=mro_type, 

512 self_class=cls, 

513 scope=scope, 

514 call=node, 

515 ) 

516 super_obj.parent = node 

517 return super_obj 

518 

519 

520def _infer_getattr_args(node, context): 

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

522 # Not a valid getattr call. 

523 raise UseInferenceDefault 

524 

525 try: 

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

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

528 except (InferenceError, StopIteration) as exc: 

529 raise UseInferenceDefault from exc 

530 

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

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

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

534 # which is unknown. 

535 return util.Uninferable, util.Uninferable 

536 

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

538 if not is_string: 

539 raise UseInferenceDefault 

540 

541 return obj, attr.value 

542 

543 

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

545 """Understand getattr calls. 

546 

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

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

549 lookup will be done. 

550 """ 

551 obj, attr = _infer_getattr_args(node, context) 

552 if ( 

553 isinstance(obj, util.UninferableBase) 

554 or isinstance(attr, util.UninferableBase) 

555 or not hasattr(obj, "igetattr") 

556 ): 

557 return util.Uninferable 

558 

559 try: 

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

561 except (StopIteration, InferenceError, AttributeInferenceError): 

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

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

564 try: 

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

566 except (StopIteration, InferenceError) as exc: 

567 raise UseInferenceDefault from exc 

568 

569 raise UseInferenceDefault 

570 

571 

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

573 """Understand hasattr calls. 

574 

575 This always guarantees three possible outcomes for calling 

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

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

578 we know that the object has the attribute and Uninferable 

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

580 """ 

581 try: 

582 obj, attr = _infer_getattr_args(node, context) 

583 if ( 

584 isinstance(obj, util.UninferableBase) 

585 or isinstance(attr, util.UninferableBase) 

586 or not hasattr(obj, "getattr") 

587 ): 

588 return util.Uninferable 

589 obj.getattr(attr, context=context) 

590 except UseInferenceDefault: 

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

592 return util.Uninferable 

593 except AttributeInferenceError: 

594 # Doesn't have it. 

595 return nodes.Const(False) 

596 return nodes.Const(True) 

597 

598 

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

600 """Understand callable calls. 

601 

602 This follows Python's semantics, where an object 

603 is callable if it provides an attribute __call__, 

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

605 called. 

606 """ 

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

608 # Invalid callable call. 

609 raise UseInferenceDefault 

610 

611 argument = node.args[0] 

612 try: 

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

614 except (InferenceError, StopIteration): 

615 return util.Uninferable 

616 if isinstance(inferred, util.UninferableBase): 

617 return util.Uninferable 

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

619 

620 

621def infer_property( 

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

623) -> objects.Property: 

624 """Understand `property` class. 

625 

626 This only infers the output of `property` 

627 call, not the arguments themselves. 

628 """ 

629 if len(node.args) < 1: 

630 # Invalid property call. 

631 raise UseInferenceDefault 

632 

633 getter = node.args[0] 

634 try: 

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

636 except (InferenceError, StopIteration) as exc: 

637 raise UseInferenceDefault from exc 

638 

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

640 raise UseInferenceDefault 

641 

642 prop_func = objects.Property( 

643 function=inferred, 

644 name=inferred.name, 

645 lineno=node.lineno, 

646 col_offset=node.col_offset, 

647 ) 

648 # Set parent outside __init__: https://github.com/pylint-dev/astroid/issues/1490 

649 prop_func.parent = node 

650 prop_func.postinit( 

651 body=[], 

652 args=inferred.args, 

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

654 ) 

655 return prop_func 

656 

657 

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

659 """Understand bool calls.""" 

660 if len(node.args) > 1: 

661 # Invalid bool call. 

662 raise UseInferenceDefault 

663 

664 if not node.args: 

665 return nodes.Const(False) 

666 

667 argument = node.args[0] 

668 try: 

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

670 except (InferenceError, StopIteration): 

671 return util.Uninferable 

672 if isinstance(inferred, util.UninferableBase): 

673 return util.Uninferable 

674 

675 bool_value = inferred.bool_value(context=context) 

676 if isinstance(bool_value, util.UninferableBase): 

677 return util.Uninferable 

678 return nodes.Const(bool_value) 

679 

680 

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

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

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

684 raise UseInferenceDefault 

685 

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

687 

688 

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

690 """Understand `slice` calls.""" 

691 args = node.args 

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

693 raise UseInferenceDefault 

694 

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

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

697 for arg in args: 

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

699 raise UseInferenceDefault 

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

701 raise UseInferenceDefault 

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

703 raise UseInferenceDefault 

704 

705 if len(args) < 3: 

706 # Make sure we have 3 arguments. 

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

708 

709 slice_node = nodes.Slice( 

710 lineno=node.lineno, 

711 col_offset=node.col_offset, 

712 parent=node.parent, 

713 end_lineno=node.end_lineno, 

714 end_col_offset=node.end_col_offset, 

715 ) 

716 slice_node.postinit(*args) 

717 return slice_node 

718 

719 

720def _infer_object__new__decorator( 

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

722) -> Iterator[Instance]: 

723 # Instantiate class immediately 

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

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

726 

727 

728def _infer_object__new__decorator_check(node) -> bool: 

729 """Predicate before inference_tip. 

730 

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

732 """ 

733 if not node.decorators: 

734 return False 

735 

736 for decorator in node.decorators.nodes: 

737 if isinstance(decorator, nodes.Attribute): 

738 if decorator.as_string() == OBJECT_DUNDER_NEW: 

739 return True 

740 return False 

741 

742 

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

744 """Infer issubclass() calls. 

745 

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

747 :param InferenceContext context: the context for the inference 

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

749 :raises UseInferenceDefault: If the node cannot be inferred 

750 """ 

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

752 if call.keyword_arguments: 

753 # issubclass doesn't support keyword arguments 

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

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

756 raise UseInferenceDefault( 

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

758 ) 

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

760 obj_node, class_or_tuple_node = call.positional_arguments 

761 

762 try: 

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

764 except (InferenceError, StopIteration) as exc: 

765 raise UseInferenceDefault from exc 

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

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

768 

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

770 # object is to be checked against. 

771 try: 

772 class_container = _class_or_tuple_to_container( 

773 class_or_tuple_node, context=context 

774 ) 

775 except InferenceError as exc: 

776 raise UseInferenceDefault from exc 

777 try: 

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

779 except AstroidTypeError as exc: 

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

781 except MroError as exc: 

782 raise UseInferenceDefault from exc 

783 return nodes.Const(issubclass_bool) 

784 

785 

786def infer_isinstance( 

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

788) -> nodes.Const: 

789 """Infer isinstance calls. 

790 

791 :param nodes.Call callnode: an isinstance call 

792 :raises UseInferenceDefault: If the node cannot be inferred 

793 """ 

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

795 if call.keyword_arguments: 

796 # isinstance doesn't support keyword arguments 

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

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

799 raise UseInferenceDefault( 

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

801 ) 

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

803 obj_node, class_or_tuple_node = call.positional_arguments 

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

805 # obj is to be check is an instance of 

806 try: 

807 class_container = _class_or_tuple_to_container( 

808 class_or_tuple_node, context=context 

809 ) 

810 except InferenceError as exc: 

811 raise UseInferenceDefault from exc 

812 try: 

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

814 except AstroidTypeError as exc: 

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

816 except MroError as exc: 

817 raise UseInferenceDefault from exc 

818 if isinstance(isinstance_bool, util.UninferableBase): 

819 raise UseInferenceDefault 

820 return nodes.Const(isinstance_bool) 

821 

822 

823def _class_or_tuple_to_container( 

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

825) -> list[InferenceResult]: 

826 # Move inferences results into container 

827 # to simplify later logic 

828 # raises InferenceError if any of the inferences fall through 

829 try: 

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

831 except StopIteration as e: 

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

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

834 # for isinstance 

835 if isinstance(node_infer, nodes.Tuple): 

836 try: 

837 class_container = [ 

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

839 ] 

840 except StopIteration as e: 

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

842 else: 

843 class_container = [node_infer] 

844 return class_container 

845 

846 

847def infer_len(node, context: InferenceContext | None = None): 

848 """Infer length calls. 

849 

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

851 :param context.InferenceContext: node context 

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

853 """ 

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

855 if call.keyword_arguments: 

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

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

858 raise UseInferenceDefault( 

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

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

861 ) 

862 [argument_node] = call.positional_arguments 

863 

864 try: 

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

866 except (AstroidTypeError, InferenceError) as exc: 

867 raise UseInferenceDefault(str(exc)) from exc 

868 

869 

870def infer_str(node, context: InferenceContext | None = None): 

871 """Infer str() calls. 

872 

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

874 :param context.InferenceContext: node context 

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

876 """ 

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

878 if call.keyword_arguments: 

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

880 try: 

881 return nodes.Const("") 

882 except (AstroidTypeError, InferenceError) as exc: 

883 raise UseInferenceDefault(str(exc)) from exc 

884 

885 

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

887 """Infer int() calls. 

888 

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

890 :param context.InferenceContext: node context 

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

892 """ 

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

894 if call.keyword_arguments: 

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

896 

897 if call.positional_arguments: 

898 try: 

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

900 except (InferenceError, StopIteration) as exc: 

901 raise UseInferenceDefault(str(exc)) from exc 

902 

903 if isinstance(first_value, util.UninferableBase): 

904 raise UseInferenceDefault 

905 

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

907 first_value.value, (int, str) 

908 ): 

909 try: 

910 actual_value = int(first_value.value) 

911 except ValueError: 

912 return nodes.Const(0) 

913 return nodes.Const(actual_value) 

914 

915 return nodes.Const(0) 

916 

917 

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

919 """Infer dict.fromkeys. 

920 

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

922 :param context.InferenceContext context: node context 

923 :rtype nodes.Dict: 

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

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

926 will be inferred instead. 

927 """ 

928 

929 def _build_dict_with_elements(elements): 

930 new_node = nodes.Dict( 

931 col_offset=node.col_offset, 

932 lineno=node.lineno, 

933 parent=node.parent, 

934 end_lineno=node.end_lineno, 

935 end_col_offset=node.end_col_offset, 

936 ) 

937 new_node.postinit(elements) 

938 return new_node 

939 

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

941 if call.keyword_arguments: 

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

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

944 raise UseInferenceDefault( 

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

946 ) 

947 

948 default = nodes.Const(None) 

949 values = call.positional_arguments[0] 

950 try: 

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

952 except (InferenceError, StopIteration): 

953 return _build_dict_with_elements([]) 

954 if inferred_values is util.Uninferable: 

955 return _build_dict_with_elements([]) 

956 

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

958 accepted_iterable_elements = (nodes.Const,) 

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

960 elements = inferred_values.elts 

961 for element in elements: 

962 if not isinstance(element, accepted_iterable_elements): 

963 # Fallback to an empty dict 

964 return _build_dict_with_elements([]) 

965 

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

967 return _build_dict_with_elements(elements_with_value) 

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

969 inferred_values.value, (str, bytes) 

970 ): 

971 elements_with_value = [ 

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

973 ] 

974 return _build_dict_with_elements(elements_with_value) 

975 if isinstance(inferred_values, nodes.Dict): 

976 keys = inferred_values.itered() 

977 for key in keys: 

978 if not isinstance(key, accepted_iterable_elements): 

979 # Fallback to an empty dict 

980 return _build_dict_with_elements([]) 

981 

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

983 return _build_dict_with_elements(elements_with_value) 

984 

985 # Fallback to an empty dictionary 

986 return _build_dict_with_elements([]) 

987 

988 

989def _infer_copy_method( 

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

991) -> Iterator[CopyResult]: 

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

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

994 if all( 

995 isinstance( 

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

997 ) 

998 for inferred_node in inferred_orig 

999 ): 

1000 return cast(Iterator[CopyResult], inferred_copy) 

1001 

1002 raise UseInferenceDefault 

1003 

1004 

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

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

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

1008 return False 

1009 

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

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

1012 else: 

1013 value = node.func.expr 

1014 

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

1016 

1017 

1018def _infer_str_format_call( 

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

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

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

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

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

1024 

1025 value: nodes.Const 

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

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

1028 inferred, nodes.Const 

1029 ): 

1030 return iter([util.Uninferable]) 

1031 value = inferred 

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

1033 value = node.func.expr 

1034 else: # pragma: no cover 

1035 return iter([util.Uninferable]) 

1036 

1037 format_template = value.value 

1038 

1039 # Get the positional arguments passed 

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

1041 for i in call.positional_arguments: 

1042 one_inferred = util.safe_infer(i, context) 

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

1044 return iter([util.Uninferable]) 

1045 inferred_positional.append(one_inferred) 

1046 

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

1048 

1049 # Get the keyword arguments passed 

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

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

1052 one_inferred = util.safe_infer(v, context) 

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

1054 return iter([util.Uninferable]) 

1055 inferred_keyword[k] = one_inferred 

1056 

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

1058 

1059 try: 

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

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

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

1063 # IndexError: there are too few arguments to interpolate 

1064 # TypeError: Unsupported format string 

1065 # ValueError: Unknown format code 

1066 return iter([util.Uninferable]) 

1067 

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

1069 

1070 

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

1072 # Builtins inference 

1073 register_builtin_transform(manager, infer_bool, "bool") 

1074 register_builtin_transform(manager, infer_super, "super") 

1075 register_builtin_transform(manager, infer_callable, "callable") 

1076 register_builtin_transform(manager, infer_property, "property") 

1077 register_builtin_transform(manager, infer_getattr, "getattr") 

1078 register_builtin_transform(manager, infer_hasattr, "hasattr") 

1079 register_builtin_transform(manager, infer_tuple, "tuple") 

1080 register_builtin_transform(manager, infer_set, "set") 

1081 register_builtin_transform(manager, infer_list, "list") 

1082 register_builtin_transform(manager, infer_dict, "dict") 

1083 register_builtin_transform(manager, infer_frozenset, "frozenset") 

1084 register_builtin_transform(manager, infer_type, "type") 

1085 register_builtin_transform(manager, infer_slice, "slice") 

1086 register_builtin_transform(manager, infer_isinstance, "isinstance") 

1087 register_builtin_transform(manager, infer_issubclass, "issubclass") 

1088 register_builtin_transform(manager, infer_len, "len") 

1089 register_builtin_transform(manager, infer_str, "str") 

1090 register_builtin_transform(manager, infer_int, "int") 

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

1092 

1093 # Infer object.__new__ calls 

1094 manager.register_transform( 

1095 nodes.ClassDef, 

1096 inference_tip(_infer_object__new__decorator), 

1097 _infer_object__new__decorator_check, 

1098 ) 

1099 

1100 manager.register_transform( 

1101 nodes.Call, 

1102 inference_tip(_infer_copy_method), 

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

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

1105 ) 

1106 

1107 manager.register_transform( 

1108 nodes.Call, 

1109 inference_tip(_infer_str_format_call), 

1110 _is_str_format_call, 

1111 )