|
34 | 34 | from typing_extensions import get_origin, is_typeddict
|
35 | 35 |
|
36 | 36 | import mypy.build
|
| 37 | +import mypy.checkexpr |
| 38 | +import mypy.checkmember |
| 39 | +import mypy.erasetype |
37 | 40 | import mypy.modulefinder
|
38 | 41 | import mypy.nodes
|
39 | 42 | import mypy.state
|
@@ -792,7 +795,11 @@ def _verify_arg_default_value(
|
792 | 795 | "has a default value but stub parameter does not"
|
793 | 796 | )
|
794 | 797 | else:
|
795 |
| - runtime_type = get_mypy_type_of_runtime_value(runtime_arg.default) |
| 798 | + type_context = stub_arg.variable.type |
| 799 | + runtime_type = get_mypy_type_of_runtime_value( |
| 800 | + runtime_arg.default, type_context=type_context |
| 801 | + ) |
| 802 | + |
796 | 803 | # Fallback to the type annotation type if var type is missing. The type annotation
|
797 | 804 | # is an UnboundType, but I don't know enough to know what the pros and cons here are.
|
798 | 805 | # UnboundTypes have ugly question marks following them, so default to var type.
|
@@ -1247,7 +1254,7 @@ def verify_var(
|
1247 | 1254 | ):
|
1248 | 1255 | yield Error(object_path, "is read-only at runtime but not in the stub", stub, runtime)
|
1249 | 1256 |
|
1250 |
| - runtime_type = get_mypy_type_of_runtime_value(runtime) |
| 1257 | + runtime_type = get_mypy_type_of_runtime_value(runtime, type_context=stub.type) |
1251 | 1258 | if (
|
1252 | 1259 | runtime_type is not None
|
1253 | 1260 | and stub.type is not None
|
@@ -1832,7 +1839,18 @@ def is_subtype_helper(left: mypy.types.Type, right: mypy.types.Type) -> bool:
|
1832 | 1839 | return mypy.subtypes.is_subtype(left, right)
|
1833 | 1840 |
|
1834 | 1841 |
|
1835 |
| -def get_mypy_type_of_runtime_value(runtime: Any) -> mypy.types.Type | None: |
| 1842 | +def get_mypy_node_for_name(module: str, type_name: str) -> mypy.nodes.SymbolNode | None: |
| 1843 | + stub = get_stub(module) |
| 1844 | + if stub is None: |
| 1845 | + return None |
| 1846 | + if type_name not in stub.names: |
| 1847 | + return None |
| 1848 | + return stub.names[type_name].node |
| 1849 | + |
| 1850 | + |
| 1851 | +def get_mypy_type_of_runtime_value( |
| 1852 | + runtime: Any, type_context: mypy.types.Type | None = None |
| 1853 | +) -> mypy.types.Type | None: |
1836 | 1854 | """Returns a mypy type object representing the type of ``runtime``.
|
1837 | 1855 |
|
1838 | 1856 | Returns None if we can't find something that works.
|
@@ -1893,14 +1911,45 @@ def anytype() -> mypy.types.AnyType:
|
1893 | 1911 | is_ellipsis_args=True,
|
1894 | 1912 | )
|
1895 | 1913 |
|
1896 |
| - # Try and look up a stub for the runtime object |
1897 |
| - stub = get_stub(type(runtime).__module__) |
1898 |
| - if stub is None: |
1899 |
| - return None |
1900 |
| - type_name = type(runtime).__name__ |
1901 |
| - if type_name not in stub.names: |
| 1914 | + skip_type_object_type = False |
| 1915 | + if type_context: |
| 1916 | + # Don't attempt to process the type object when context is generic |
| 1917 | + # This is related to issue #3737 |
| 1918 | + type_context = mypy.types.get_proper_type(type_context) |
| 1919 | + # Callable types with a generic return value |
| 1920 | + if isinstance(type_context, mypy.types.CallableType): |
| 1921 | + if isinstance(type_context.ret_type, mypy.types.TypeVarType): |
| 1922 | + skip_type_object_type = True |
| 1923 | + # Type[x] where x is generic |
| 1924 | + if isinstance(type_context, mypy.types.TypeType): |
| 1925 | + if isinstance(type_context.item, mypy.types.TypeVarType): |
| 1926 | + skip_type_object_type = True |
| 1927 | + |
| 1928 | + if isinstance(runtime, type) and not skip_type_object_type: |
| 1929 | + |
| 1930 | + def _named_type(name: str) -> mypy.types.Instance: |
| 1931 | + parts = name.rsplit(".", maxsplit=1) |
| 1932 | + node = get_mypy_node_for_name(parts[0], parts[1]) |
| 1933 | + assert isinstance(node, nodes.TypeInfo) |
| 1934 | + any_type = mypy.types.AnyType(mypy.types.TypeOfAny.special_form) |
| 1935 | + return mypy.types.Instance(node, [any_type] * len(node.defn.type_vars)) |
| 1936 | + |
| 1937 | + # Try and look up a stub for the runtime object itself |
| 1938 | + # The logic here is similar to ExpressionChecker.analyze_ref_expr |
| 1939 | + type_info = get_mypy_node_for_name(runtime.__module__, runtime.__name__) |
| 1940 | + if isinstance(type_info, nodes.TypeInfo): |
| 1941 | + result: mypy.types.Type | None = None |
| 1942 | + result = mypy.typeops.type_object_type(type_info, _named_type) |
| 1943 | + if mypy.checkexpr.is_type_type_context(type_context): |
| 1944 | + # This is the type in a type[] expression, so substitute type |
| 1945 | + # variables with Any. |
| 1946 | + result = mypy.erasetype.erase_typevars(result) |
| 1947 | + return result |
| 1948 | + |
| 1949 | + # Try and look up a stub for the runtime object's type |
| 1950 | + type_info = get_mypy_node_for_name(type(runtime).__module__, type(runtime).__name__) |
| 1951 | + if type_info is None: |
1902 | 1952 | return None
|
1903 |
| - type_info = stub.names[type_name].node |
1904 | 1953 | if isinstance(type_info, nodes.Var):
|
1905 | 1954 | return type_info.type
|
1906 | 1955 | if not isinstance(type_info, nodes.TypeInfo):
|
|
0 commit comments