99
1010import collections
1111import collections .abc
12- from collections .abc import Iterator , Sequence
12+ from collections .abc import Iterable , Iterator
1313from typing import TYPE_CHECKING , Any , ClassVar , Literal
1414
1515from astroid import nodes
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
140151def _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):
413430class 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 ]:
0 commit comments