Skip to content

Commit 30f249d

Browse files
Merge branch 'main' into incomplete-cache-key
2 parents 0fa0233 + 06fafc4 commit 30f249d

18 files changed

+142
-103
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
- id: end-of-file-fixer
1111
exclude: tests/testdata
1212
- repo: https://github.com/charliermarsh/ruff-pre-commit
13-
rev: "v0.0.262"
13+
rev: "v0.0.263"
1414
hooks:
1515
- id: ruff
1616
exclude: tests/testdata

.readthedocs.yaml

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ version: 2
55
sphinx:
66
configuration: doc/conf.py
77

8+
build:
9+
os: ubuntu-22.04
10+
tools:
11+
python: "3.11"
12+
813
python:
9-
version: 3.8
1014
install:
1115
- requirements: doc/requirements.txt

astroid/arguments.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from astroid.context import CallContext, InferenceContext
1010
from astroid.exceptions import InferenceError, NoDefault
1111
from astroid.helpers import safe_infer
12+
from astroid.typing import InferenceResult
1213
from astroid.util import Uninferable, UninferableBase
1314

1415

@@ -134,14 +135,19 @@ def _unpack_args(self, args, context: InferenceContext | None = None):
134135
values.append(arg)
135136
return values
136137

137-
def infer_argument(self, funcnode, name, context): # noqa: C901
138-
"""Infer a function argument value according to the call context.
138+
def infer_argument(
139+
self, funcnode: InferenceResult, name: str, context: InferenceContext
140+
): # noqa: C901
141+
"""Infer a function argument value according to the call context."""
142+
if not isinstance(funcnode, (nodes.FunctionDef, nodes.Lambda)):
143+
raise InferenceError(
144+
f"Can not infer function argument value for non-function node {funcnode!r}.",
145+
call_site=self,
146+
func=funcnode,
147+
arg=name,
148+
context=context,
149+
)
139150

140-
Arguments:
141-
funcnode: The function being called.
142-
name: The name of the argument whose value is being inferred.
143-
context: Inference context object
144-
"""
145151
if name in self.duplicated_keywords:
146152
raise InferenceError(
147153
"The arguments passed to {func!r} have duplicate keywords.",
@@ -191,7 +197,7 @@ def infer_argument(self, funcnode, name, context): # noqa: C901
191197
positional.append(arg)
192198

193199
if argindex is not None:
194-
boundnode = getattr(context, "boundnode", None)
200+
boundnode = context.boundnode
195201
# 2. first argument of instance/class method
196202
if argindex == 0 and funcnode.type in {"method", "classmethod"}:
197203
# context.boundnode is None when an instance method is called with

astroid/bases.py

+62-32
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import collections
1111
import collections.abc
12-
from collections.abc import Iterator, Sequence
12+
from collections.abc import Iterable, Iterator
1313
from typing import TYPE_CHECKING, Any, ClassVar, Literal
1414

1515
from astroid import nodes
@@ -66,7 +66,9 @@
6666
}
6767

6868

69-
def _is_property(meth, context: InferenceContext | None = None) -> bool:
69+
def _is_property(
70+
meth: nodes.FunctionDef | UnboundMethod, context: InferenceContext | None = None
71+
) -> bool:
7072
from astroid import helpers # pylint: disable=import-outside-toplevel
7173

7274
decoratornames = meth.decoratornames(context=context)
@@ -92,7 +94,11 @@ def _is_property(meth, context: InferenceContext | None = None) -> bool:
9294
if not isinstance(base_class, nodes.Name):
9395
continue
9496
module, _ = base_class.lookup(base_class.name)
95-
if module.name == "builtins" and base_class.name == "property":
97+
if (
98+
isinstance(module, nodes.Module)
99+
and module.name == "builtins"
100+
and base_class.name == "property"
101+
):
96102
return True
97103

98104
return False
@@ -107,10 +113,15 @@ class Proxy:
107113
if new instance attributes are created. See the Const class
108114
"""
109115

110-
_proxied: nodes.ClassDef | nodes.FunctionDef
116+
_proxied: nodes.ClassDef | nodes.FunctionDef | nodes.Lambda | UnboundMethod
111117

112118
def __init__(
113-
self, proxied: nodes.ClassDef | nodes.FunctionDef | None = None
119+
self,
120+
proxied: nodes.ClassDef
121+
| nodes.FunctionDef
122+
| nodes.Lambda
123+
| UnboundMethod
124+
| None = None,
114125
) -> None:
115126
if proxied is None:
116127
# This is a hack to allow calling this __init__ during bootstrapping of
@@ -138,7 +149,7 @@ def infer( # type: ignore[return]
138149

139150

140151
def _infer_stmts(
141-
stmts: Sequence[InferenceResult],
152+
stmts: Iterator[InferenceResult],
142153
context: InferenceContext | None,
143154
frame: nodes.NodeNG | BaseInstance | None = None,
144155
) -> collections.abc.Generator[InferenceResult, None, None]:
@@ -148,7 +159,10 @@ def _infer_stmts(
148159
if context is not None:
149160
name = context.lookupname
150161
context = context.clone()
151-
constraints = context.constraints.get(name, {})
162+
if name is not None:
163+
constraints = context.constraints.get(name, {})
164+
else:
165+
constraints = {}
152166
else:
153167
name = None
154168
constraints = {}
@@ -159,8 +173,7 @@ def _infer_stmts(
159173
yield stmt
160174
inferred = True
161175
continue
162-
# 'context' is always InferenceContext and Instances get '_infer_name' from ClassDef
163-
context.lookupname = stmt._infer_name(frame, name) # type: ignore[union-attr]
176+
context.lookupname = stmt._infer_name(frame, name)
164177
try:
165178
stmt_constraints: set[Constraint] = set()
166179
for constraint_stmt, potential_constraints in constraints.items():
@@ -289,15 +302,17 @@ def igetattr(
289302
except AttributeInferenceError as error:
290303
raise InferenceError(**vars(error)) from error
291304

292-
def _wrap_attr(self, attrs, context: InferenceContext | None = None):
305+
def _wrap_attr(
306+
self, attrs: Iterable[InferenceResult], context: InferenceContext | None = None
307+
) -> Iterator[InferenceResult]:
293308
"""Wrap bound methods of attrs in a InstanceMethod proxies."""
294309
for attr in attrs:
295310
if isinstance(attr, UnboundMethod):
296311
if _is_property(attr):
297312
yield from attr.infer_call_result(self, context)
298313
else:
299314
yield BoundMethod(attr, self)
300-
elif hasattr(attr, "name") and attr.name == "<lambda>":
315+
elif isinstance(attr, nodes.Lambda):
301316
if attr.args.arguments and attr.args.arguments[0].name == "self":
302317
yield BoundMethod(attr, self)
303318
continue
@@ -390,7 +405,9 @@ def bool_value(
390405
return True
391406
return result
392407

393-
def getitem(self, index, context: InferenceContext | None = None):
408+
def getitem(
409+
self, index: nodes.Const, context: InferenceContext | None = None
410+
) -> InferenceResult | None:
394411
new_context = bind_context_to_node(context, self)
395412
if not context:
396413
context = new_context
@@ -413,13 +430,14 @@ def getitem(self, index, context: InferenceContext | None = None):
413430
class UnboundMethod(Proxy):
414431
"""A special node representing a method not bound to an instance."""
415432

416-
_proxied: nodes.FunctionDef
433+
_proxied: nodes.FunctionDef | UnboundMethod
417434

418435
special_attributes: objectmodel.BoundMethodModel | objectmodel.UnboundMethodModel = (
419436
objectmodel.UnboundMethodModel()
420437
)
421438

422439
def __repr__(self) -> str:
440+
assert self._proxied.parent, "Expected a parent node"
423441
frame = self._proxied.parent.frame(future=True)
424442
return "<{} {} of {} at 0x{}".format(
425443
self.__class__.__name__, self._proxied.name, frame.qname(), id(self)
@@ -431,7 +449,7 @@ def implicit_parameters(self) -> Literal[0, 1]:
431449
def is_bound(self) -> bool:
432450
return False
433451

434-
def getattr(self, name, context: InferenceContext | None = None):
452+
def getattr(self, name: str, context: InferenceContext | None = None):
435453
if name in self.special_attributes:
436454
return [self.special_attributes.lookup(name)]
437455
return self._proxied.getattr(name, context)
@@ -461,19 +479,22 @@ def infer_call_result(
461479
# If we're unbound method __new__ of a builtin, the result is an
462480
# instance of the class given as first argument.
463481
if self._proxied.name == "__new__":
482+
assert self._proxied.parent, "Expected a parent node"
464483
qname = self._proxied.parent.frame(future=True).qname()
465484
# Avoid checking builtins.type: _infer_type_new_call() does more validation
466485
if qname.startswith("builtins.") and qname != "builtins.type":
467-
return self._infer_builtin_new(caller, context)
486+
return self._infer_builtin_new(caller, context or InferenceContext())
468487
return self._proxied.infer_call_result(caller, context)
469488

470489
def _infer_builtin_new(
471490
self,
472-
caller: nodes.Call,
491+
caller: SuccessfulInferenceResult | None,
473492
context: InferenceContext,
474493
) -> collections.abc.Generator[
475494
nodes.Const | Instance | UninferableBase, None, None
476495
]:
496+
if not isinstance(caller, nodes.Call):
497+
return
477498
if not caller.args:
478499
return
479500
# Attempt to create a constant
@@ -508,7 +529,11 @@ class BoundMethod(UnboundMethod):
508529

509530
special_attributes = objectmodel.BoundMethodModel()
510531

511-
def __init__(self, proxy, bound):
532+
def __init__(
533+
self,
534+
proxy: nodes.FunctionDef | nodes.Lambda | UnboundMethod,
535+
bound: SuccessfulInferenceResult,
536+
) -> None:
512537
super().__init__(proxy)
513538
self.bound = bound
514539

@@ -521,7 +546,9 @@ def implicit_parameters(self) -> Literal[0, 1]:
521546
def is_bound(self) -> Literal[True]:
522547
return True
523548

524-
def _infer_type_new_call(self, caller, context): # noqa: C901
549+
def _infer_type_new_call(
550+
self, caller: nodes.Call, context: InferenceContext
551+
) -> nodes.ClassDef | None: # noqa: C901
525552
"""Try to infer what type.__new__(mcs, name, bases, attrs) returns.
526553
527554
In order for such call to be valid, the metaclass needs to be
@@ -536,7 +563,7 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
536563
mcs = next(caller.args[0].infer(context=context))
537564
except StopIteration as e:
538565
raise InferenceError(context=context) from e
539-
if mcs.__class__.__name__ != "ClassDef":
566+
if not isinstance(mcs, nodes.ClassDef):
540567
# Not a valid first argument.
541568
return None
542569
if not mcs.is_subtype_of("builtins.type"):
@@ -548,7 +575,7 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
548575
name = next(caller.args[1].infer(context=context))
549576
except StopIteration as e:
550577
raise InferenceError(context=context) from e
551-
if name.__class__.__name__ != "Const":
578+
if not isinstance(name, nodes.Const):
552579
# Not a valid name, needs to be a const.
553580
return None
554581
if not isinstance(name.value, str):
@@ -560,14 +587,14 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
560587
bases = next(caller.args[2].infer(context=context))
561588
except StopIteration as e:
562589
raise InferenceError(context=context) from e
563-
if bases.__class__.__name__ != "Tuple":
590+
if not isinstance(bases, nodes.Tuple):
564591
# Needs to be a tuple.
565592
return None
566593
try:
567594
inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
568595
except StopIteration as e:
569596
raise InferenceError(context=context) from e
570-
if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases):
597+
if any(not isinstance(base, nodes.ClassDef) for base in inferred_bases):
571598
# All the bases needs to be Classes
572599
return None
573600

@@ -576,10 +603,10 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
576603
attrs = next(caller.args[3].infer(context=context))
577604
except StopIteration as e:
578605
raise InferenceError(context=context) from e
579-
if attrs.__class__.__name__ != "Dict":
606+
if not isinstance(attrs, nodes.Dict):
580607
# Needs to be a dictionary.
581608
return None
582-
cls_locals = collections.defaultdict(list)
609+
cls_locals: dict[str, list[InferenceResult]] = collections.defaultdict(list)
583610
for key, value in attrs.items:
584611
try:
585612
key = next(key.infer(context=context))
@@ -590,14 +617,14 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
590617
except StopIteration as e:
591618
raise InferenceError(context=context) from e
592619
# Ignore non string keys
593-
if key.__class__.__name__ == "Const" and isinstance(key.value, str):
620+
if isinstance(key, nodes.Const) and isinstance(key.value, str):
594621
cls_locals[key.value].append(value)
595622

596623
# Build the class from now.
597624
cls = mcs.__class__(
598625
name=name.value,
599-
lineno=caller.lineno,
600-
col_offset=caller.col_offset,
626+
lineno=caller.lineno or 0,
627+
col_offset=caller.col_offset or 0,
601628
parent=caller,
602629
end_lineno=caller.end_lineno,
603630
end_col_offset=caller.end_col_offset,
@@ -612,7 +639,7 @@ def _infer_type_new_call(self, caller, context): # noqa: C901
612639
cls.postinit(
613640
bases=bases.elts,
614641
body=[empty],
615-
decorators=[],
642+
decorators=None,
616643
newstyle=True,
617644
metaclass=mcs,
618645
keywords=[],
@@ -627,9 +654,10 @@ def infer_call_result(
627654
) -> Iterator[InferenceResult]:
628655
context = bind_context_to_node(context, self.bound)
629656
if (
630-
self.bound.__class__.__name__ == "ClassDef"
657+
isinstance(self.bound, nodes.ClassDef)
631658
and self.bound.name == "type"
632659
and self.name == "__new__"
660+
and isinstance(caller, nodes.Call)
633661
and len(caller.args) == 4
634662
):
635663
# Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
@@ -655,16 +683,18 @@ class Generator(BaseInstance):
655683
special_attributes: objectmodel.GeneratorModel
656684

657685
def __init__(
658-
self, parent=None, generator_initial_context: InferenceContext | None = None
659-
):
686+
self,
687+
parent: nodes.FunctionDef,
688+
generator_initial_context: InferenceContext | None = None,
689+
) -> None:
660690
super().__init__()
661691
self.parent = parent
662692
self._call_context = copy_context(generator_initial_context)
663693

664694
# See comment above: this is a deferred initialization.
665695
Generator.special_attributes = objectmodel.GeneratorModel()
666696

667-
def infer_yield_types(self):
697+
def infer_yield_types(self) -> Iterator[InferenceResult]:
668698
yield from self.parent.infer_yield_result(self._call_context)
669699

670700
def callable(self) -> Literal[False]:

astroid/brain/brain_namedtuple_enum.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ def infer_named_tuple(
223223
except StopIteration as e:
224224
raise InferenceError(node=node) from e
225225
try:
226-
rename = next(call_site.infer_argument(func, "rename", context)).bool_value()
226+
rename = next(
227+
call_site.infer_argument(func, "rename", context or InferenceContext())
228+
).bool_value()
227229
except (InferenceError, StopIteration):
228230
rename = False
229231

0 commit comments

Comments
 (0)