|
5 | 5 | """This module contains base classes and functions for the nodes and some
|
6 | 6 | inference utils.
|
7 | 7 | """
|
| 8 | +from __future__ import annotations |
8 | 9 |
|
9 | 10 | import collections
|
| 11 | +import collections.abc |
| 12 | +from typing import TYPE_CHECKING |
10 | 13 |
|
11 | 14 | from astroid import decorators
|
12 | 15 | from astroid.const import PY310_PLUS
|
|
28 | 31 | helpers = lazy_import("helpers")
|
29 | 32 | manager = lazy_import("manager")
|
30 | 33 |
|
| 34 | +if TYPE_CHECKING: |
| 35 | + from astroid import nodes |
| 36 | + |
31 | 37 |
|
32 | 38 | # TODO: check if needs special treatment
|
33 | 39 | BOOL_SPECIAL_METHOD = "__bool__"
|
@@ -372,25 +378,50 @@ def infer_call_result(self, caller, context):
|
372 | 378 | on ``object.__new__`` will be of type ``object``,
|
373 | 379 | which is incorrect for the argument in general.
|
374 | 380 | If no context is given the ``object.__new__`` call argument will
|
375 |
| - correctly inferred except when inside a call that requires |
| 381 | + be correctly inferred except when inside a call that requires |
376 | 382 | the additional context (such as a classmethod) of the boundnode
|
377 | 383 | to determine which class the method was called from
|
378 | 384 | """
|
379 | 385 |
|
380 |
| - # If we're unbound method __new__ of builtin object, the result is an |
| 386 | + # If we're unbound method __new__ of a builtin, the result is an |
381 | 387 | # instance of the class given as first argument.
|
382 |
| - if ( |
383 |
| - self._proxied.name == "__new__" |
384 |
| - and self._proxied.parent.frame(future=True).qname() == "builtins.object" |
385 |
| - ): |
386 |
| - if caller.args: |
387 |
| - node_context = context.extra_context.get(caller.args[0]) |
388 |
| - infer = caller.args[0].infer(context=node_context) |
389 |
| - else: |
390 |
| - infer = [] |
391 |
| - return (Instance(x) if x is not Uninferable else x for x in infer) |
| 388 | + if self._proxied.name == "__new__": |
| 389 | + qname = self._proxied.parent.frame(future=True).qname() |
| 390 | + # Avoid checking builtins.type: _infer_type_new_call() does more validation |
| 391 | + if qname.startswith("builtins.") and qname != "builtins.type": |
| 392 | + return self._infer_builtin_new(caller, context) |
392 | 393 | return self._proxied.infer_call_result(caller, context)
|
393 | 394 |
|
| 395 | + def _infer_builtin_new( |
| 396 | + self, |
| 397 | + caller: nodes.Call, |
| 398 | + context: InferenceContext, |
| 399 | + ) -> collections.abc.Generator[ |
| 400 | + nodes.Const | Instance | type[Uninferable], None, None |
| 401 | + ]: |
| 402 | + # pylint: disable-next=import-outside-toplevel; circular import |
| 403 | + from astroid import nodes |
| 404 | + |
| 405 | + if not caller.args: |
| 406 | + return |
| 407 | + # Attempt to create a constant |
| 408 | + if len(caller.args) > 1: |
| 409 | + value = None |
| 410 | + if isinstance(caller.args[1], nodes.Const): |
| 411 | + value = caller.args[1].value |
| 412 | + else: |
| 413 | + inferred_arg = next(caller.args[1].infer(), None) |
| 414 | + if isinstance(inferred_arg, nodes.Const): |
| 415 | + value = inferred_arg.value |
| 416 | + if value is not None: |
| 417 | + yield nodes.const_factory(value) |
| 418 | + return |
| 419 | + |
| 420 | + node_context = context.extra_context.get(caller.args[0]) |
| 421 | + infer = caller.args[0].infer(context=node_context) |
| 422 | + |
| 423 | + yield from (Instance(x) if x is not Uninferable else x for x in infer) |
| 424 | + |
394 | 425 | def bool_value(self, context=None):
|
395 | 426 | return True
|
396 | 427 |
|
|
0 commit comments