diff --git a/mypy/build.py b/mypy/build.py index 2a9e7f5bf8f1..f6162af07dd0 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -143,7 +143,6 @@ from mypy import errorcodes as codes from mypy.config_parser import get_config_module_names, parse_mypy_comments -from mypy.fixer_state import fixer_state from mypy.fixup import NodeFixer from mypy.freetree import free_tree from mypy.fscache import FileSystemCache @@ -159,6 +158,7 @@ SearchPaths, compute_search_paths, ) +from mypy.modules_state import modules_state from mypy.nodes import Expression from mypy.options import Options from mypy.parse import load_from_raw, parse @@ -816,7 +816,8 @@ def __init__( # Share same modules dictionary with the global fixer state. # We need to set allow_missing when doing a fine-grained cache # load because we need to gracefully handle missing modules. - fixer_state.node_fixer = NodeFixer(self.modules, self.options.use_fine_grained_cache) + modules_state.modules = self.modules + modules_state.node_fixer = NodeFixer(self.modules, self.options.use_fine_grained_cache) self.import_map: dict[str, set[str]] = {} self.missing_modules: dict[str, int] = {} self.fg_deps_meta: dict[str, FgDepMeta] = {} @@ -2816,9 +2817,9 @@ def load_tree(self, temporary: bool = False) -> None: def fix_cross_refs(self) -> None: assert self.tree is not None, "Internal error: method must be called on parsed file only" # Do initial lightweight pass fixing TypeInfos and module cross-references. - assert fixer_state.node_fixer is not None - fixer_state.node_fixer.visit_symbol_table(self.tree.names) - type_fixer = fixer_state.node_fixer.type_fixer + assert modules_state.node_fixer is not None + modules_state.node_fixer.visit_symbol_table(self.tree.names) + type_fixer = modules_state.node_fixer.type_fixer # Eagerly fix shared instances, before they are used by named_type() calls. if instance_cache.str_type is not None: instance_cache.str_type.accept(type_fixer) diff --git a/mypy/checker.py b/mypy/checker.py index 6a0e8f3718d3..58f34e8b126c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2702,7 +2702,7 @@ def visit_class_def(self, defn: ClassDef) -> None: self.check_final_deletable(typ) if defn.decorators: - sig: Type = type_object_type(defn.info, self.named_type) + sig: Type = type_object_type(defn.info) # Decorators are applied in reverse order. for decorator in reversed(defn.decorators): if isinstance(decorator, CallExpr) and isinstance( diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index a173025cda29..7e53f6638c04 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -427,7 +427,7 @@ def analyze_static_reference( # We special case NoneType, because its stub definition is not related to None. return TypeType(NoneType()) else: - return type_object_type(node, self.named_type) + return type_object_type(node) elif isinstance(node, TypeAlias): # Something that refers to a type alias appears in runtime context. # Note that we suppress bogus errors for alias redefinitions, @@ -1908,7 +1908,7 @@ def analyze_type_type_callee(self, item: ProperType, context: Context) -> Type: if isinstance(item, AnyType): return AnyType(TypeOfAny.from_another_any, source_any=item) if isinstance(item, Instance): - res = type_object_type(item.type, self.named_type) + res = type_object_type(item.type) if isinstance(res, CallableType): res = res.copy_modified(from_type_type=True) expanded = expand_type_by_instance(res, item) @@ -4076,6 +4076,44 @@ def check_method_call( object_type=base_type, ) + def lookup_operator(self, op_name: str, base_type: Type, context: Context) -> Type | None: + """Looks up the given operator and returns the corresponding type, + if it exists.""" + + # This check is an important performance optimization. + if not has_operator(base_type, op_name): + return None + + with self.msg.filter_errors() as w: + member = analyze_member_access( + name=op_name, + typ=base_type, + is_lvalue=False, + is_super=False, + is_operator=True, + original_type=base_type, + context=context, + chk=self.chk, + in_literal_context=self.is_literal_context(), + ) + return None if w.has_new_errors() else member + + def lookup_definer(self, typ: Instance, attr_name: str) -> str | None: + """Returns the name of the class that contains the actual definition of attr_name. + + So if class A defines foo and class B subclasses A, running + `get_class_defined_in(B, "foo")` would return the full name of A. + + However, if B were to override and redefine foo, that method call would + return the full name of B instead. + + If the attr name is not present in the given class or its MRO, returns None. + """ + for cls in typ.type.mro: + if cls.names.get(attr_name): + return cls.fullname + return None + def check_op_reversible( self, op_name: str, @@ -4085,48 +4123,10 @@ def check_op_reversible( right_expr: Expression, context: Context, ) -> tuple[Type, Type]: - def lookup_operator(op_name: str, base_type: Type) -> Type | None: - """Looks up the given operator and returns the corresponding type, - if it exists.""" - - # This check is an important performance optimization. - if not has_operator(base_type, op_name, self.named_type): - return None - - with self.msg.filter_errors() as w: - member = analyze_member_access( - name=op_name, - typ=base_type, - is_lvalue=False, - is_super=False, - is_operator=True, - original_type=base_type, - context=context, - chk=self.chk, - in_literal_context=self.is_literal_context(), - ) - return None if w.has_new_errors() else member - - def lookup_definer(typ: Instance, attr_name: str) -> str | None: - """Returns the name of the class that contains the actual definition of attr_name. - - So if class A defines foo and class B subclasses A, running - 'get_class_defined_in(B, "foo")` would return the full name of A. - - However, if B were to override and redefine foo, that method call would - return the full name of B instead. - - If the attr name is not present in the given class or its MRO, returns None. - """ - for cls in typ.type.mro: - if cls.names.get(attr_name): - return cls.fullname - return None - left_type = get_proper_type(left_type) right_type = get_proper_type(right_type) - # If either the LHS or the RHS are Any, we can't really concluding anything + # If either the LHS or the RHS are Any, we can't really conclude anything # about the operation since the Any type may or may not define an # __op__ or __rop__ method. So, we punt and return Any instead. @@ -4142,8 +4142,8 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: rev_op_name = operators.reverse_op_methods[op_name] - left_op = lookup_operator(op_name, left_type) - right_op = lookup_operator(rev_op_name, right_type) + left_op = self.lookup_operator(op_name, left_type, context) + right_op = self.lookup_operator(rev_op_name, right_type, context) # STEP 2a: # We figure out in which order Python will call the operator methods. As it @@ -4168,7 +4168,8 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: # B: right's __rop__ method is different from left's __op__ method not (isinstance(left_type, Instance) and isinstance(right_type, Instance)) or ( - lookup_definer(left_type, op_name) != lookup_definer(right_type, rev_op_name) + self.lookup_definer(left_type, op_name) + != self.lookup_definer(right_type, rev_op_name) and ( left_type.type.alt_promote is None or left_type.type.alt_promote.type is not right_type.type @@ -4931,10 +4932,10 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: ) item = get_proper_type(item) if isinstance(item, Instance): - tp = type_object_type(item.type, self.named_type) + tp = type_object_type(item.type) return self.apply_type_arguments_to_callable(tp, item.args, tapp) elif isinstance(item, TupleType) and item.partial_fallback.type.is_named_tuple: - tp = type_object_type(item.partial_fallback.type, self.named_type) + tp = type_object_type(item.partial_fallback.type) return self.apply_type_arguments_to_callable(tp, item.partial_fallback.args, tapp) elif isinstance(item, TypedDictType): return self.typeddict_callable_from_context(item) @@ -5003,7 +5004,7 @@ class LongName(Generic[T]): ... if isinstance(item, Instance): # Normally we get a callable type (or overloaded) with .is_type_obj() true # representing the class's constructor - tp = type_object_type(item.type, self.named_type) + tp = type_object_type(item.type) if alias.no_args: return tp return self.apply_type_arguments_to_callable(tp, item.args, ctx) @@ -5013,7 +5014,7 @@ class LongName(Generic[T]): ... # Tuple[str, int]() fails at runtime, only named tuples and subclasses work. tuple_fallback(item).type.fullname != "builtins.tuple" ): - return type_object_type(tuple_fallback(item).type, self.named_type) + return type_object_type(tuple_fallback(item).type) elif isinstance(item, TypedDictType): return self.typeddict_callable_from_context(item) elif isinstance(item, NoneType): diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 3359190092af..b5dcf94a0b20 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -13,9 +13,11 @@ expand_type_by_instance, freshen_all_functions_type_vars, ) +from mypy.lookup import lookup_stdlib_typeinfo from mypy.maptype import map_instance_to_supertype from mypy.meet import is_overlapping_types from mypy.messages import MessageBuilder +from mypy.modules_state import modules_state from mypy.nodes import ( ARG_POS, ARG_STAR, @@ -74,6 +76,7 @@ UninhabitedType, UnionType, get_proper_type, + instance_cache, ) @@ -1525,7 +1528,7 @@ def bind_self_fast(method: F, original_type: Type | None = None) -> F: ) -def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool: +def has_operator(typ: Type, op_method: str) -> bool: """Does type have operator with the given name? Note: this follows the rules for operator access, in particular: @@ -1544,7 +1547,7 @@ def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance if isinstance(typ, AnyType): return True if isinstance(typ, UnionType): - return all(has_operator(x, op_method, named_type) for x in typ.relevant_items()) + return all(has_operator(x, op_method) for x in typ.relevant_items()) if isinstance(typ, FunctionLike) and typ.is_type_obj(): return typ.fallback.type.has_readable_member(op_method) if isinstance(typ, TypeType): @@ -1555,27 +1558,33 @@ def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance if isinstance(item, TypeVarType): item = item.values_or_bound() if isinstance(item, UnionType): - return all(meta_has_operator(x, op_method, named_type) for x in item.relevant_items()) - return meta_has_operator(item, op_method, named_type) - return instance_fallback(typ, named_type).type.has_readable_member(op_method) + return all(meta_has_operator(x, op_method) for x in item.relevant_items()) + return meta_has_operator(item, op_method) + return instance_fallback(typ).type.has_readable_member(op_method) -def instance_fallback(typ: ProperType, named_type: Callable[[str], Instance]) -> Instance: +def instance_fallback(typ: ProperType) -> Instance: if isinstance(typ, Instance): return typ if isinstance(typ, TupleType): return tuple_fallback(typ) if isinstance(typ, (LiteralType, TypedDictType)): return typ.fallback - return named_type("builtins.object") + if instance_cache.object_type is None: + object_typeinfo = lookup_stdlib_typeinfo("builtins.object", modules_state.modules) + instance_cache.object_type = Instance(object_typeinfo, []) + return instance_cache.object_type -def meta_has_operator(item: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool: +def meta_has_operator(item: Type, op_method: str) -> bool: item = get_proper_type(item) if isinstance(item, AnyType): return True - item = instance_fallback(item, named_type) - meta = item.type.metaclass_type or named_type("builtins.type") + item = instance_fallback(item) + meta = item.type.metaclass_type + if meta is None: + type_type = lookup_stdlib_typeinfo("builtins.type", modules_state.modules) + meta = Instance(type_type, []) return meta.type.has_readable_member(op_method) diff --git a/mypy/lookup.py b/mypy/lookup.py index e3b195567fa0..594e39a188f3 100644 --- a/mypy/lookup.py +++ b/mypy/lookup.py @@ -66,3 +66,15 @@ def lookup_fully_qualified( assert node, f"Cannot find {name}" return None names = node.names + + +def lookup_stdlib_typeinfo(fullname: str, modules: dict[str, MypyFile]) -> TypeInfo: + """Find TypeInfo for a standard library type. + + This fast path assumes that the type exists at module top-level, use this + function only for common types like `builtins.object` or `typing.Iterable`. + """ + module, name = fullname.rsplit(".", maxsplit=1) + sym = modules[module].names[name] + assert isinstance(sym.node, TypeInfo) + return sym.node diff --git a/mypy/messages.py b/mypy/messages.py index 51bb0b7ee9be..cfd1a12ea77c 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -68,6 +68,7 @@ IS_VAR, find_member, get_member_flags, + get_protocol_member, is_same_type, is_subtype, ) @@ -3110,7 +3111,7 @@ def get_conflict_protocol_types( continue supertype = find_member(member, right, left) assert supertype is not None - subtype = mypy.typeops.get_protocol_member(left, member, class_obj) + subtype = get_protocol_member(left, member, class_obj) if not subtype: continue is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True, options=options) @@ -3126,7 +3127,7 @@ def get_conflict_protocol_types( different_setter = True supertype = set_supertype if IS_EXPLICIT_SETTER in get_member_flags(member, left): - set_subtype = mypy.typeops.get_protocol_member(left, member, class_obj, is_lvalue=True) + set_subtype = get_protocol_member(left, member, class_obj, is_lvalue=True) if set_subtype and not is_same_type(set_subtype, subtype): different_setter = True subtype = set_subtype diff --git a/mypy/fixer_state.py b/mypy/modules_state.py similarity index 78% rename from mypy/fixer_state.py rename to mypy/modules_state.py index 501b49f61aa9..4636b972ccab 100644 --- a/mypy/fixer_state.py +++ b/mypy/modules_state.py @@ -4,6 +4,7 @@ if TYPE_CHECKING: from mypy.fixup import NodeFixer + from mypy.nodes import MypyFile # This is global mutable state. Don't add anything here unless there's a very # good reason. This exists as a separate file to avoid method-level import in @@ -13,6 +14,7 @@ class FixerState: def __init__(self) -> None: self.node_fixer: NodeFixer | None = None + self.modules: dict[str, MypyFile] = {} -fixer_state: Final = FixerState() +modules_state: Final = FixerState() diff --git a/mypy/nodes.py b/mypy/nodes.py index 2b0e5940d6a9..6117d604c9e9 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -68,7 +68,7 @@ write_str_opt_list, write_tag, ) -from mypy.fixer_state import fixer_state +from mypy.modules_state import modules_state from mypy.options import Options from mypy.util import is_sunder, is_typeshed_file, short_type from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor @@ -4819,7 +4819,7 @@ def type(self) -> mypy.types.Type | None: @property def node(self) -> SymbolNode | None: if self.unfixed: - node_fixer = fixer_state.node_fixer + node_fixer = modules_state.node_fixer assert node_fixer is not None if self.cross_ref is not None: node_fixer.resolve_cross_ref(self) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 617ae8eb83a6..f664acfc9ce8 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -734,7 +734,7 @@ def _parse_converter( ): converter_type = converter_expr.node.type elif isinstance(converter_expr.node, TypeInfo): - converter_type = type_object_type(converter_expr.node, ctx.api.named_type) + converter_type = type_object_type(converter_expr.node) elif ( isinstance(converter_expr, IndexExpr) and isinstance(converter_expr.analyzed, TypeApplication) @@ -742,7 +742,7 @@ def _parse_converter( and isinstance(converter_expr.base.node, TypeInfo) ): # The converter is a generic type. - converter_type = type_object_type(converter_expr.base.node, ctx.api.named_type) + converter_type = type_object_type(converter_expr.base.node) if isinstance(converter_type, CallableType): converter_type = apply_generic_arguments( converter_type, diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index c698223a8a46..16ccdb418429 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -3,7 +3,7 @@ from typing import NamedTuple from mypy.argmap import map_actuals_to_formals -from mypy.fixer_state import fixer_state +from mypy.modules_state import modules_state from mypy.nodes import ( ARG_POS, MDEF, @@ -436,6 +436,6 @@ def add_attribute_to_class( # We keep the unused `api` parameter, to avoid breaking 3rd party dataclass-like plugins. def deserialize_and_fixup_type(data: str | JsonDict, api: SemanticAnalyzerPluginInterface) -> Type: typ = deserialize_type(data) - assert fixer_state.node_fixer is not None - typ.accept(fixer_state.node_fixer.type_fixer) + assert modules_state.node_fixer is not None + typ.accept(modules_state.node_fixer.type_fixer) return typ diff --git a/mypy/semanal.py b/mypy/semanal.py index 8f2005fdefcd..aa74122be255 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4370,9 +4370,7 @@ def disable_invalid_recursive_aliases( "tuple" if isinstance(get_proper_type(current_node.target), TupleType) else "union" ) messages.append(f"Invalid recursive alias: a {target} item of itself") - if detect_diverging_alias( - current_node, current_node.target, self.lookup_qualified, self.tvar_scope - ): + if detect_diverging_alias(current_node, current_node.target): messages.append("Invalid recursive alias: type variable nesting on right hand side") if messages: current_node.target = AnyType(TypeOfAny.from_error) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 4cf0de84d989..6bf3c0617dad 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -2097,20 +2097,11 @@ def anytype() -> mypy.types.AnyType: skip_type_object_type = True if isinstance(runtime, type) and not skip_type_object_type: - - def _named_type(name: str) -> mypy.types.Instance: - parts = name.rsplit(".", maxsplit=1) - node = get_mypy_node_for_name(parts[0], parts[1]) - assert isinstance(node, nodes.TypeInfo) - any_type = mypy.types.AnyType(mypy.types.TypeOfAny.special_form) - return mypy.types.Instance(node, [any_type] * len(node.defn.type_vars)) - # Try and look up a stub for the runtime object itself # The logic here is similar to ExpressionChecker.analyze_ref_expr type_info = get_mypy_node_for_name(runtime.__module__, runtime.__name__) if isinstance(type_info, nodes.TypeInfo): - result: mypy.types.Type | None = None - result = mypy.typeops.type_object_type(type_info, _named_type) + result = mypy.typeops.type_object_type(type_info) if mypy.checkexpr.is_type_type_context(type_context): # This is the type in a type[] expression, so substitute type # variables with Any. diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 66d7a95eb425..b8e8d5e3b79d 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1224,7 +1224,7 @@ def f(self) -> A: ... supertype = find_member(member, right, left) assert supertype is not None - subtype = mypy.typeops.get_protocol_member(left, member, class_obj) + subtype = get_protocol_member(left, member, class_obj) # Useful for debugging: # print(member, 'of', left, 'has type', subtype) # print(member, 'of', right, 'has type', supertype) @@ -1253,9 +1253,7 @@ def f(self) -> A: ... if IS_EXPLICIT_SETTER in superflags: supertype = find_member(member, right, left, is_lvalue=True) if IS_EXPLICIT_SETTER in subflags: - subtype = mypy.typeops.get_protocol_member( - left, member, class_obj, is_lvalue=True - ) + subtype = get_protocol_member(left, member, class_obj, is_lvalue=True) # At this point we know attribute is present on subtype, otherwise we # would return False above. assert supertype is not None and subtype is not None @@ -1293,6 +1291,30 @@ def f(self) -> A: ... return True +def get_protocol_member( + left: Instance, member: str, class_obj: bool, is_lvalue: bool = False +) -> Type | None: + if member == "__call__" and class_obj: + # Special case: class objects always have __call__ that is just the constructor. + return mypy.typeops.type_object_type(left.type) + + if member == "__call__" and left.type.is_metaclass(precise=True): + # Special case: we want to avoid falling back to metaclass __call__ + # if constructor signature didn't match, this can cause many false negatives. + return None + + subtype = find_member(member, left, left, class_obj=class_obj, is_lvalue=is_lvalue) + if isinstance(subtype, PartialType): + subtype = ( + NoneType() + if subtype.type is None + else Instance( + subtype.type, [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars) + ) + ) + return subtype + + def find_member( name: str, itype: Instance, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b22e1f80be59..a685ea2383cb 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2351,16 +2351,9 @@ class DivergingAliasDetector(TrivialSyntheticTypeTranslator): """See docstring of detect_diverging_alias() for details.""" # TODO: this doesn't really need to be a translator, but we don't have a trivial visitor. - def __init__( - self, - seen_nodes: set[TypeAlias], - lookup: Callable[[str, Context], SymbolTableNode | None], - scope: TypeVarLikeScope, - ) -> None: + def __init__(self, seen_nodes: set[TypeAlias]) -> None: super().__init__() self.seen_nodes = seen_nodes - self.lookup = lookup - self.scope = scope self.diverging = False def visit_type_alias_type(self, t: TypeAliasType) -> Type: @@ -2377,19 +2370,14 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: # All clear for this expansion chain. return t new_nodes = self.seen_nodes | {t.alias} - visitor = DivergingAliasDetector(new_nodes, self.lookup, self.scope) + visitor = DivergingAliasDetector(new_nodes) _ = get_proper_type(t).accept(visitor) if visitor.diverging: self.diverging = True return t -def detect_diverging_alias( - node: TypeAlias, - target: Type, - lookup: Callable[[str, Context], SymbolTableNode | None], - scope: TypeVarLikeScope, -) -> bool: +def detect_diverging_alias(node: TypeAlias, target: Type) -> bool: """This detects type aliases that will diverge during type checking. For example F = Something[..., F[List[T]]]. At each expansion step this will produce @@ -2401,7 +2389,7 @@ def detect_diverging_alias( They may be handy in rare cases, e.g. to express a union of non-mixed nested lists: Nested = Union[T, Nested[List[T]]] ~> Union[T, List[T], List[List[T]], ...] """ - visitor = DivergingAliasDetector({node}, lookup, scope) + visitor = DivergingAliasDetector({node}) _ = target.accept(visitor) return visitor.diverging diff --git a/mypy/typeops.py b/mypy/typeops.py index 839c6454ca28..3d1b9b4bd3fa 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -14,7 +14,9 @@ from mypy.checker_state import checker_state from mypy.copytype import copy_type from mypy.expandtype import expand_type, expand_type_by_instance +from mypy.lookup import lookup_stdlib_typeinfo from mypy.maptype import map_instance_to_supertype +from mypy.modules_state import modules_state from mypy.nodes import ( ARG_POS, ARG_STAR, @@ -65,6 +67,7 @@ flatten_nested_unions, get_proper_type, get_proper_types, + instance_cache, remove_dups, ) from mypy.typetraverser import TypeTraverserVisitor @@ -138,7 +141,10 @@ def get_self_type(func: CallableType, def_info: TypeInfo) -> Type | None: return None -def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: +# We keep the unused `named_type` argument to avoid breaking plugins. +def type_object_type( + info: TypeInfo, named_type: Callable[[str], Instance] | None = None +) -> ProperType: """Return the type of a type object. For a generic type G with type variables T and S the type is generally of form @@ -181,12 +187,9 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P if info.metaclass_type is not None: fallback = info.metaclass_type - elif checker_state.type_checker: - # Prefer direct call when it is available. It is faster, and, - # unfortunately, some callers provide bogus callback. - fallback = checker_state.type_checker.named_type("builtins.type") else: - fallback = named_type("builtins.type") + type_type = lookup_stdlib_typeinfo("builtins.type", modules_state.modules) + fallback = Instance(type_type, []) if init_index < new_index: method: FuncBase | Decorator = init_method.node @@ -201,13 +204,18 @@ def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> P if info.fallback_to_any: # Construct a universal callable as the prototype. any_type = AnyType(TypeOfAny.special_form) + if instance_cache.function_type is None: + function_typeinfo = lookup_stdlib_typeinfo( + "builtins.function", modules_state.modules + ) + instance_cache.function_type = Instance(function_typeinfo, []) sig = CallableType( arg_types=[any_type, any_type], arg_kinds=[ARG_STAR, ARG_STAR2], arg_names=["_args", "_kwds"], ret_type=any_type, is_bound=True, - fallback=named_type("builtins.function"), + fallback=instance_cache.function_type, ) result: FunctionLike = class_callable(sig, info, fallback, None, is_new=False) if allow_cache and state.strict_optional: @@ -1261,38 +1269,6 @@ def fixup_partial_type(typ: Type) -> Type: return Instance(typ.type, [AnyType(TypeOfAny.unannotated)] * len(typ.type.type_vars)) -def get_protocol_member( - left: Instance, member: str, class_obj: bool, is_lvalue: bool = False -) -> Type | None: - if member == "__call__" and class_obj: - # Special case: class objects always have __call__ that is just the constructor. - - # TODO: this is wrong, it creates callables that are not recognized as type objects. - # Long-term, we should probably get rid of this callback argument altogether. - def named_type(fullname: str) -> Instance: - return Instance(left.type.mro[-1], []) - - return type_object_type(left.type, named_type) - - if member == "__call__" and left.type.is_metaclass(precise=True): - # Special case: we want to avoid falling back to metaclass __call__ - # if constructor signature didn't match, this can cause many false negatives. - return None - - from mypy.subtypes import find_member - - subtype = find_member(member, left, left, class_obj=class_obj, is_lvalue=is_lvalue) - if isinstance(subtype, PartialType): - subtype = ( - NoneType() - if subtype.type is None - else Instance( - subtype.type, [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars) - ) - ) - return subtype - - def _is_disjoint_base(info: TypeInfo) -> bool: # It either has the @disjoint_base decorator or defines nonempty __slots__. if info.is_disjoint_base: