| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- """Simple type inference for decorated functions during semantic analysis."""
- from __future__ import annotations
- from mypy.nodes import ARG_POS, CallExpr, Decorator, Expression, FuncDef, RefExpr, Var
- from mypy.semanal_shared import SemanticAnalyzerInterface
- from mypy.typeops import function_type
- from mypy.types import (
- AnyType,
- CallableType,
- ProperType,
- Type,
- TypeOfAny,
- TypeVarType,
- get_proper_type,
- )
- from mypy.typevars import has_no_typevars
- def infer_decorator_signature_if_simple(
- dec: Decorator, analyzer: SemanticAnalyzerInterface
- ) -> None:
- """Try to infer the type of the decorated function.
- This lets us resolve additional references to decorated functions
- during type checking. Otherwise the type might not be available
- when we need it, since module top levels can't be deferred.
- This basically uses a simple special-purpose type inference
- engine just for decorators.
- """
- if dec.var.is_property:
- # Decorators are expected to have a callable type (it's a little odd).
- if dec.func.type is None:
- dec.var.type = CallableType(
- [AnyType(TypeOfAny.special_form)],
- [ARG_POS],
- [None],
- AnyType(TypeOfAny.special_form),
- analyzer.named_type("builtins.function"),
- name=dec.var.name,
- )
- elif isinstance(dec.func.type, CallableType):
- dec.var.type = dec.func.type
- return
- decorator_preserves_type = True
- for expr in dec.decorators:
- preserve_type = False
- if isinstance(expr, RefExpr) and isinstance(expr.node, FuncDef):
- if expr.node.type and is_identity_signature(expr.node.type):
- preserve_type = True
- if not preserve_type:
- decorator_preserves_type = False
- break
- if decorator_preserves_type:
- # No non-identity decorators left. We can trivially infer the type
- # of the function here.
- dec.var.type = function_type(dec.func, analyzer.named_type("builtins.function"))
- if dec.decorators:
- return_type = calculate_return_type(dec.decorators[0])
- if return_type and isinstance(return_type, AnyType):
- # The outermost decorator will return Any so we know the type of the
- # decorated function.
- dec.var.type = AnyType(TypeOfAny.from_another_any, source_any=return_type)
- sig = find_fixed_callable_return(dec.decorators[0])
- if sig:
- # The outermost decorator always returns the same kind of function,
- # so we know that this is the type of the decorated function.
- orig_sig = function_type(dec.func, analyzer.named_type("builtins.function"))
- sig.name = orig_sig.items[0].name
- dec.var.type = sig
- def is_identity_signature(sig: Type) -> bool:
- """Is type a callable of form T -> T (where T is a type variable)?"""
- sig = get_proper_type(sig)
- if isinstance(sig, CallableType) and sig.arg_kinds == [ARG_POS]:
- if isinstance(sig.arg_types[0], TypeVarType) and isinstance(sig.ret_type, TypeVarType):
- return sig.arg_types[0].id == sig.ret_type.id
- return False
- def calculate_return_type(expr: Expression) -> ProperType | None:
- """Return the return type if we can calculate it.
- This only uses information available during semantic analysis so this
- will sometimes return None because of insufficient information (as
- type inference hasn't run yet).
- """
- if isinstance(expr, RefExpr):
- if isinstance(expr.node, FuncDef):
- typ = expr.node.type
- if typ is None:
- # No signature -> default to Any.
- return AnyType(TypeOfAny.unannotated)
- # Explicit Any return?
- if isinstance(typ, CallableType):
- return get_proper_type(typ.ret_type)
- return None
- elif isinstance(expr.node, Var):
- return get_proper_type(expr.node.type)
- elif isinstance(expr, CallExpr):
- return calculate_return_type(expr.callee)
- return None
- def find_fixed_callable_return(expr: Expression) -> CallableType | None:
- """Return the return type, if expression refers to a callable that returns a callable.
- But only do this if the return type has no type variables. Return None otherwise.
- This approximates things a lot as this is supposed to be called before type checking
- when full type information is not available yet.
- """
- if isinstance(expr, RefExpr):
- if isinstance(expr.node, FuncDef):
- typ = expr.node.type
- if typ:
- if isinstance(typ, CallableType) and has_no_typevars(typ.ret_type):
- ret_type = get_proper_type(typ.ret_type)
- if isinstance(ret_type, CallableType):
- return ret_type
- elif isinstance(expr, CallExpr):
- t = find_fixed_callable_return(expr.callee)
- if t:
- ret_type = get_proper_type(t.ret_type)
- if isinstance(ret_type, CallableType):
- return ret_type
- return None
|