Skip to content

Commit 0925b89

Browse files
committed
More detailed checking of type objects in stubtest
This uses checkmember.type_object_type and context to produce better types of type objects.
1 parent 5082a22 commit 0925b89

File tree

1 file changed

+55
-4
lines changed

1 file changed

+55
-4
lines changed

mypy/stubtest.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
from typing_extensions import get_origin, is_typeddict
3333

3434
import mypy.build
35+
import mypy.checkexpr
36+
import mypy.checkmember
37+
import mypy.erasetype
3538
import mypy.modulefinder
3639
import mypy.nodes
3740
import mypy.state
@@ -670,7 +673,11 @@ def _verify_arg_default_value(
670673
"has a default value but stub argument does not"
671674
)
672675
else:
673-
runtime_type = get_mypy_type_of_runtime_value(runtime_arg.default)
676+
type_context = stub_arg.variable.type
677+
runtime_type = get_mypy_type_of_runtime_value(
678+
runtime_arg.default, type_context=type_context
679+
)
680+
674681
# Fallback to the type annotation type if var type is missing. The type annotation
675682
# is an UnboundType, but I don't know enough to know what the pros and cons here are.
676683
# UnboundTypes have ugly question marks following them, so default to var type.
@@ -1097,7 +1104,7 @@ def verify_var(
10971104
):
10981105
yield Error(object_path, "is read-only at runtime but not in the stub", stub, runtime)
10991106

1100-
runtime_type = get_mypy_type_of_runtime_value(runtime)
1107+
runtime_type = get_mypy_type_of_runtime_value(runtime, type_context=stub.type)
11011108
if (
11021109
runtime_type is not None
11031110
and stub.type is not None
@@ -1586,7 +1593,9 @@ def is_subtype_helper(left: mypy.types.Type, right: mypy.types.Type) -> bool:
15861593
return mypy.subtypes.is_subtype(left, right)
15871594

15881595

1589-
def get_mypy_type_of_runtime_value(runtime: Any) -> mypy.types.Type | None:
1596+
def get_mypy_type_of_runtime_value(
1597+
runtime: Any, type_context: mypy.types.Type | None = None
1598+
) -> mypy.types.Type | None:
15901599
"""Returns a mypy type object representing the type of ``runtime``.
15911600
15921601
Returns None if we can't find something that works.
@@ -1647,7 +1656,49 @@ def anytype() -> mypy.types.AnyType:
16471656
is_ellipsis_args=True,
16481657
)
16491658

1650-
# Try and look up a stub for the runtime object
1659+
if type_context:
1660+
# Don't attempt to account for context if the context is generic
1661+
# This is related to issue #3737
1662+
if isinstance(type_context, mypy.types.CallableType):
1663+
if isinstance(type_context.ret_type, mypy.types.TypeVarType):
1664+
type_context = None
1665+
if isinstance(type_context, mypy.types.TypeType):
1666+
if isinstance(type_context.item, mypy.types.TypeVarType):
1667+
type_context = None
1668+
1669+
if type_context:
1670+
1671+
def _named_type(name: str) -> mypy.types.Instance:
1672+
parts = name.rsplit(".", maxsplit=1)
1673+
stub = get_stub(parts[0])
1674+
if stub is not None:
1675+
if parts[1] in stub.names:
1676+
node = stub.names[parts[1]]
1677+
assert isinstance(node.node, nodes.TypeInfo)
1678+
any_type = mypy.types.AnyType(mypy.types.TypeOfAny.special_form)
1679+
return mypy.types.Instance(
1680+
node.node, [any_type] * len(node.node.defn.type_vars)
1681+
)
1682+
1683+
any_type = mypy.types.AnyType(mypy.types.TypeOfAny.from_error)
1684+
return mypy.types.Instance(node.node, [])
1685+
1686+
if isinstance(runtime, type):
1687+
# Try and look up a stub for the runtime object itself
1688+
# The logic here is similar to ExpressionChecker.analyze_ref_expr
1689+
stub = get_stub(runtime.__module__)
1690+
if stub is not None:
1691+
if runtime.__name__ in stub.names:
1692+
type_info = stub.names[runtime.__name__].node
1693+
if isinstance(type_info, nodes.TypeInfo):
1694+
result = mypy.checkmember.type_object_type(type_info, _named_type)
1695+
if mypy.checkexpr.is_type_type_context(type_context):
1696+
# This is the type in a type[] expression, so substitute type
1697+
# variables with Any.
1698+
result = mypy.erasetype.erase_typevars(result)
1699+
return result
1700+
1701+
# Try and look up a stub for the runtime object's type
16511702
stub = get_stub(type(runtime).__module__)
16521703
if stub is None:
16531704
return None

0 commit comments

Comments
 (0)