|
9 | 9 |
|
10 | 10 | import itertools |
11 | 11 | from collections.abc import Iterable, Sequence |
12 | | -from typing import Any, TypeVar, cast |
| 12 | +from typing import Any, Callable, TypeVar, cast |
13 | 13 |
|
14 | 14 | from mypy.copytype import copy_type |
15 | 15 | from mypy.expandtype import expand_type, expand_type_by_instance |
|
27 | 27 | FuncItem, |
28 | 28 | OverloadedFuncDef, |
29 | 29 | StrExpr, |
| 30 | + SymbolNode, |
30 | 31 | TypeInfo, |
31 | 32 | Var, |
32 | 33 | ) |
|
63 | 64 | get_proper_type, |
64 | 65 | get_proper_types, |
65 | 66 | ) |
| 67 | +from mypy.typetraverser import TypeTraverserVisitor |
66 | 68 | from mypy.typevars import fill_typevars |
67 | 69 |
|
68 | 70 |
|
@@ -132,6 +134,90 @@ def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Typ |
132 | 134 | return None |
133 | 135 |
|
134 | 136 |
|
| 137 | +def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: |
| 138 | + """Return the type of a type object. |
| 139 | +
|
| 140 | + For a generic type G with type variables T and S the type is generally of form |
| 141 | +
|
| 142 | + Callable[..., G[T, S]] |
| 143 | +
|
| 144 | + where ... are argument types for the __init__/__new__ method (without the self |
| 145 | + argument). Also, the fallback type will be 'type' instead of 'function'. |
| 146 | + """ |
| 147 | + |
| 148 | + # We take the type from whichever of __init__ and __new__ is first |
| 149 | + # in the MRO, preferring __init__ if there is a tie. |
| 150 | + init_method = info.get("__init__") |
| 151 | + new_method = info.get("__new__") |
| 152 | + if not init_method or not is_valid_constructor(init_method.node): |
| 153 | + # Must be an invalid class definition. |
| 154 | + return AnyType(TypeOfAny.from_error) |
| 155 | + # There *should* always be a __new__ method except the test stubs |
| 156 | + # lack it, so just copy init_method in that situation |
| 157 | + new_method = new_method or init_method |
| 158 | + if not is_valid_constructor(new_method.node): |
| 159 | + # Must be an invalid class definition. |
| 160 | + return AnyType(TypeOfAny.from_error) |
| 161 | + |
| 162 | + # The two is_valid_constructor() checks ensure this. |
| 163 | + assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) |
| 164 | + assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) |
| 165 | + |
| 166 | + init_index = info.mro.index(init_method.node.info) |
| 167 | + new_index = info.mro.index(new_method.node.info) |
| 168 | + |
| 169 | + fallback = info.metaclass_type or named_type("builtins.type") |
| 170 | + if init_index < new_index: |
| 171 | + method: FuncBase | Decorator = init_method.node |
| 172 | + is_new = False |
| 173 | + elif init_index > new_index: |
| 174 | + method = new_method.node |
| 175 | + is_new = True |
| 176 | + else: |
| 177 | + if init_method.node.info.fullname == "builtins.object": |
| 178 | + # Both are defined by object. But if we've got a bogus |
| 179 | + # base class, we can't know for sure, so check for that. |
| 180 | + if info.fallback_to_any: |
| 181 | + # Construct a universal callable as the prototype. |
| 182 | + any_type = AnyType(TypeOfAny.special_form) |
| 183 | + sig = CallableType( |
| 184 | + arg_types=[any_type, any_type], |
| 185 | + arg_kinds=[ARG_STAR, ARG_STAR2], |
| 186 | + arg_names=["_args", "_kwds"], |
| 187 | + ret_type=any_type, |
| 188 | + fallback=named_type("builtins.function"), |
| 189 | + ) |
| 190 | + return class_callable(sig, info, fallback, None, is_new=False) |
| 191 | + |
| 192 | + # Otherwise prefer __init__ in a tie. It isn't clear that this |
| 193 | + # is the right thing, but __new__ caused problems with |
| 194 | + # typeshed (#5647). |
| 195 | + method = init_method.node |
| 196 | + is_new = False |
| 197 | + # Construct callable type based on signature of __init__. Adjust |
| 198 | + # return type and insert type arguments. |
| 199 | + if isinstance(method, FuncBase): |
| 200 | + t = function_type(method, fallback) |
| 201 | + else: |
| 202 | + assert isinstance(method.type, ProperType) |
| 203 | + assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this |
| 204 | + t = method.type |
| 205 | + return type_object_type_from_function(t, info, method.info, fallback, is_new) |
| 206 | + |
| 207 | + |
| 208 | +def is_valid_constructor(n: SymbolNode | None) -> bool: |
| 209 | + """Does this node represents a valid constructor method? |
| 210 | +
|
| 211 | + This includes normal functions, overloaded functions, and decorators |
| 212 | + that return a callable type. |
| 213 | + """ |
| 214 | + if isinstance(n, SYMBOL_FUNCBASE_TYPES): |
| 215 | + return True |
| 216 | + if isinstance(n, Decorator): |
| 217 | + return isinstance(get_proper_type(n.type), FunctionLike) |
| 218 | + return False |
| 219 | + |
| 220 | + |
135 | 221 | def type_object_type_from_function( |
136 | 222 | signature: FunctionLike, info: TypeInfo, def_info: TypeInfo, fallback: Instance, is_new: bool |
137 | 223 | ) -> FunctionLike: |
@@ -1070,6 +1156,17 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> list[TypeVarLikeType]: |
1070 | 1156 | return [t] if self.include_all else [] |
1071 | 1157 |
|
1072 | 1158 |
|
| 1159 | +def freeze_all_type_vars(member_type: Type) -> None: |
| 1160 | + member_type.accept(FreezeTypeVarsVisitor()) |
| 1161 | + |
| 1162 | + |
| 1163 | +class FreezeTypeVarsVisitor(TypeTraverserVisitor): |
| 1164 | + def visit_callable_type(self, t: CallableType) -> None: |
| 1165 | + for v in t.variables: |
| 1166 | + v.id.meta_level = 0 |
| 1167 | + super().visit_callable_type(t) |
| 1168 | + |
| 1169 | + |
1073 | 1170 | def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool: |
1074 | 1171 | """Does this type have a custom special method such as __format__() or __eq__()? |
1075 | 1172 |
|
@@ -1152,7 +1249,6 @@ def get_protocol_member( |
1152 | 1249 | ) -> Type | None: |
1153 | 1250 | if member == "__call__" and class_obj: |
1154 | 1251 | # Special case: class objects always have __call__ that is just the constructor. |
1155 | | - from mypy.checkmember import type_object_type |
1156 | 1252 |
|
1157 | 1253 | def named_type(fullname: str) -> Instance: |
1158 | 1254 | return Instance(left.type.mro[-1], []) |
|
0 commit comments