Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2970,11 +2970,20 @@ def [T <: int] f(self, x: int, y: T) -> None
if tp.arg_kinds[i] == ARG_STAR2:
s += "**"
name = tp.arg_names[i]
if not name and not options.reveal_verbose_types:
# Avoid ambiguous (and weird) formatting for anonymous args/kwargs.
if tp.arg_kinds[i] == ARG_STAR and isinstance(tp.arg_types[i], UnpackType):
name = "args"
elif tp.arg_kinds[i] == ARG_STAR2 and tp.unpack_kwargs:
name = "kwargs"
if name:
s += name + ": "
type_str = format_type_bare(tp.arg_types[i], options)
if tp.arg_kinds[i] == ARG_STAR2 and tp.unpack_kwargs:
type_str = f"Unpack[{type_str}]"
if options.reveal_verbose_types:
type_str = f"Unpack[{type_str}]"
else:
type_str = f"**{type_str}"
s += type_str
if tp.arg_kinds[i].is_optional():
s += " = ..."
Expand Down
4 changes: 3 additions & 1 deletion mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ def __init__(self) -> None:
self.show_error_end: bool = False
self.hide_error_codes = False
self.show_error_code_links = False
# This is an internal-only flag to simplify migrating test output.
self.reveal_verbose_types = False
# Use soft word wrap and show trimmed source snippets with error location markers.
self.pretty = False
self.dump_graph = False
Expand Down Expand Up @@ -433,7 +435,7 @@ def __init__(self) -> None:
self.mypyc_skip_c_generation = False

def use_star_unpack(self) -> bool:
return self.python_version >= (3, 11)
return self.python_version >= (3, 11) or not self.reveal_verbose_types

def snapshot(self) -> dict[str, object]:
"""Produce a comparable snapshot of this Option"""
Expand Down
3 changes: 2 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
TypeAliasType,
TypedDictType,
TypeOfAny,
TypeStrVisitor,
TypeType,
TypeVarId,
TypeVarLikeType,
Expand Down Expand Up @@ -1376,7 +1377,7 @@ def process_deprecated_overload(self, defn: OverloadedFuncDef) -> None:
elif (deprecated := self.get_deprecated(d)) is not None:
deprecation = True
if isinstance(typ := item.func.type, CallableType):
typestr = f" {typ} "
typestr = f" {typ.accept(TypeStrVisitor(options=self.options))} "
else:
typestr = " "
item.func.deprecated = (
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def run_case_once(
options.use_builtins_fixtures = True
options.show_traceback = True
options.native_parser = bool(os.environ.get("TEST_NATIVE_PARSER"))
options.reveal_verbose_types = not testcase.name.endswith("_no_verbose_reveal")

if options.num_workers:
options.fixed_format_cache = True
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testfinegrained.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def get_options(self, source: str, testcase: DataDrivenTestCase, build_cache: bo
options.export_types = "inspect" in testcase.file
# Treat empty bodies safely for these test cases.
options.allow_empty_bodies = not testcase.name.endswith("_no_empty")
options.reveal_verbose_types = True
if re.search("flags:.*--follow-imports", source) is None:
# Override the default for follow_imports
options.follow_imports = "error"
Expand Down
4 changes: 3 additions & 1 deletion mypy/test/testmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def build(self, source: str, testcase: DataDrivenTestCase) -> BuildResult | None
options.export_types = True
options.show_traceback = True
options.allow_empty_bodies = True
options.reveal_verbose_types = True
main_path = os.path.join(test_temp_dir, "main")

self.str_conv.options = options
Expand Down Expand Up @@ -222,7 +223,8 @@ def dump_types(
key=lambda n: (
n.line,
short_type(n),
n.str_with_options(self.str_conv.options) + str(type_map[n]),
n.str_with_options(self.str_conv.options)
+ type_map[n].accept(self.type_str_conv),
),
):
typ = type_map[expr]
Expand Down
23 changes: 17 additions & 6 deletions mypy/test/testsemanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from mypy.modulefinder import BuildSource
from mypy.nodes import TypeInfo
from mypy.options import Options
from mypy.strconv import StrConv
from mypy.test.config import test_temp_dir
from mypy.test.data import DataDrivenTestCase, DataSuite
from mypy.test.helpers import (
Expand All @@ -19,8 +20,9 @@
)

# Semantic analyzer test cases: dump parse tree

# Semantic analysis test case description files.
from mypy.types import TypeStrVisitor

semanal_files = find_test_files(
pattern="semanal-*.test",
exclude=[
Expand All @@ -38,6 +40,7 @@ def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Opti
options.semantic_analysis_only = True
options.show_traceback = True
options.python_version = PYTHON3_VERSION
options.reveal_verbose_types = True
return options


Expand Down Expand Up @@ -154,14 +157,22 @@ class SemAnalTypeInfoSuite(DataSuite):
required_out_section = True
files = ["semanal-typeinfo.test"]

def setup(self) -> None:
super().setup()
self.str_conv = StrConv(options=Options())
self.type_str_conv = TypeStrVisitor(options=Options())

def run_case(self, testcase: DataDrivenTestCase) -> None:
"""Perform a test case."""
src = "\n".join(testcase.input)
options = get_semanal_options(src, testcase)
self.str_conv.options = options
self.type_str_conv.options = options
try:
# Build test case input.
src = "\n".join(testcase.input)
result = build.build(
sources=[BuildSource("main", None, src)],
options=get_semanal_options(src, testcase),
options=options,
alt_lib_path=test_temp_dir,
)
a = result.errors
Expand All @@ -179,7 +190,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
typeinfos[n.fullname] = n.node

# The output is the symbol table converted into a string.
a = str(typeinfos).split("\n")
a = typeinfos.dump(self.str_conv, self.type_str_conv).split("\n")
except CompileError as e:
a = e.messages
assert_string_arrays_equal(
Expand All @@ -190,10 +201,10 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:


class TypeInfoMap(dict[str, TypeInfo]):
def __str__(self) -> str:
def dump(self, str_conv: StrConv, type_str_conv: TypeStrVisitor) -> str:
a: list[str] = ["TypeInfoMap("]
for x, y in sorted(self.items()):
ti = ("\n" + " ").join(str(y).split("\n"))
ti = ("\n" + " ").join(y.dump(str_conv, type_str_conv).split("\n"))
a.append(f" {x} : {ti}")
a[-1] += ")"
return "\n".join(a)
4 changes: 2 additions & 2 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2756,7 +2756,7 @@ def test_output(self) -> None:
f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub parameter "number" differs '
'from runtime parameter "num"\n'
f"Stub: in file {TEST_MODULE_NAME}.pyi:1\n"
"def (number: builtins.int, text: builtins.str)\n"
"def (number: int, text: str)\n"
f"Runtime: in file {TEST_MODULE_NAME}.py:1\ndef (num, text)\n\n"
"Found 1 error (checked 1 module)\n"
)
Expand Down Expand Up @@ -2951,7 +2951,7 @@ def myfunction(arg: str, /) -> None: ...
stub = result.files["__main__"].names["myfunction"].node
assert isinstance(stub, nodes.OverloadedFuncDef)
sig = mypy.stubtest.Signature.from_overloadedfuncdef(stub)
assert str(sig) == "def (arg: builtins.int | builtins.str)"
assert str(sig) == "def (arg: int | str)"

def test_config_file(self) -> None:
runtime = "temp = 5\n"
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testtransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None:
options.use_builtins_fixtures = True
options.semantic_analysis_only = True
options.show_traceback = True
options.reveal_verbose_types = True
result = build.build(
sources=[BuildSource("main", None, src)], options=options, alt_lib_path=test_temp_dir
)
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testtypegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
options.export_types = True
options.preserve_asts = True
options.allow_empty_bodies = True
options.reveal_verbose_types = True
result = build.build(
sources=[BuildSource("main", None, src)],
options=options,
Expand Down
4 changes: 2 additions & 2 deletions mypy/test/testtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def test_type_variable_binding(self) -> None:
"X", "X", TypeVarId(1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)
)
),
"X`1",
"X",
)
assert_equal(
str(
Expand All @@ -157,7 +157,7 @@ def test_type_variable_binding(self) -> None:
AnyType(TypeOfAny.from_omitted_generics),
)
),
"X`1",
"X",
)

def test_generic_function_type(self) -> None:
Expand Down
46 changes: 36 additions & 10 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3741,12 +3741,16 @@ def visit_deleted_type(self, t: DeletedType, /) -> str:
return f"<Deleted '{t.source}'>"

def visit_instance(self, t: Instance, /) -> str:
fullname = t.type.fullname
if not self.options.reveal_verbose_types and fullname.startswith("builtins."):
fullname = t.type.name
if t.last_known_value and not t.args:
# Instances with a literal fallback should never be generic. If they are,
# something went wrong so we fall back to showing the full Instance repr.
s = f"{t.last_known_value.accept(self)}?"

else:
s = t.type.fullname or t.type.name or "<???>"
s = fullname or t.type.name or "<???>"

if t.args:
if t.type.fullname == "builtins.tuple":
Expand All @@ -3761,10 +3765,13 @@ def visit_instance(self, t: Instance, /) -> str:
return s

def visit_type_var(self, t: TypeVarType, /) -> str:
s = f"{t.name}`{t.id}"
if not self.options.reveal_verbose_types:
s = t.name
else:
s = f"{t.name}`{t.id}"
if self.id_mapper and t.upper_bound:
s += f"(upper_bound={t.upper_bound.accept(self)})"
if t.has_default():
if t.has_default() and self.options.reveal_verbose_types:
s += f" = {t.default.accept(self)}"
return s

Expand All @@ -3773,10 +3780,13 @@ def visit_param_spec(self, t: ParamSpecType, /) -> str:
s = ""
if t.prefix.arg_types:
s += f"[{self.list_str(t.prefix.arg_types)}, **"
s += f"{t.name_with_suffix()}`{t.id}"
if not self.options.reveal_verbose_types:
s += t.name_with_suffix()
else:
s += f"{t.name_with_suffix()}`{t.id}"
if t.prefix.arg_types:
s += "]"
if t.has_default():
if t.has_default() and self.options.reveal_verbose_types:
s += f" = {t.default.accept(self)}"
return s

Expand Down Expand Up @@ -3810,8 +3820,11 @@ def visit_parameters(self, t: Parameters, /) -> str:
return f"[{s}]"

def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> str:
s = f"{t.name}`{t.id}"
if t.has_default():
if not self.options.reveal_verbose_types:
s = t.name
else:
s = f"{t.name}`{t.id}"
if t.has_default() and self.options.reveal_verbose_types:
s += f" = {t.default.accept(self)}"
return s

Expand All @@ -3836,11 +3849,20 @@ def visit_callable_type(self, t: CallableType, /) -> str:
if t.arg_kinds[i] == ARG_STAR2:
s += "**"
name = t.arg_names[i]
if not name and not self.options.reveal_verbose_types:
# Avoid ambiguous (and weird) formatting for anonymous args/kwargs.
if t.arg_kinds[i] == ARG_STAR and isinstance(t.arg_types[i], UnpackType):
name = "args"
elif t.arg_kinds[i] == ARG_STAR2 and t.unpack_kwargs:
name = "kwargs"
if name:
s += name + ": "
type_str = t.arg_types[i].accept(self)
if t.arg_kinds[i] == ARG_STAR2 and t.unpack_kwargs:
type_str = f"Unpack[{type_str}]"
if not self.options.reveal_verbose_types:
type_str = f"**{type_str}"
else:
type_str = f"Unpack[{type_str}]"
s += type_str
if t.arg_kinds[i].is_optional():
s += " ="
Expand All @@ -3867,7 +3889,6 @@ def visit_callable_type(self, t: CallableType, /) -> str:
vs = []
for var in t.variables:
if isinstance(var, TypeVarType):
# We reimplement TypeVarType.__repr__ here in order to support id_mapper.
if var.values:
vals = f"({', '.join(val.accept(self) for val in var.values)})"
vs.append(f"{var.name} in {vals}")
Expand Down Expand Up @@ -3919,7 +3940,10 @@ def item_str(name: str, typ: str) -> str:
prefix = ""
if t.fallback and t.fallback.type:
if t.fallback.type.fullname not in TPDICT_FB_NAMES:
prefix = repr(t.fallback.type.fullname) + ", "
if not self.options.reveal_verbose_types:
prefix = t.fallback.type.fullname + ", "
else:
prefix = repr(t.fallback.type.fullname) + ", "
return f"TypedDict({prefix}{s})"

def visit_raw_expression_type(self, t: RawExpressionType, /) -> str:
Expand Down Expand Up @@ -3970,6 +3994,8 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> str:
return type_str

def visit_unpack_type(self, t: UnpackType, /) -> str:
if not self.options.reveal_verbose_types:
return f"*{t.type.accept(self)}"
return f"Unpack[{t.type.accept(self)}]"

def list_str(self, a: Iterable[Type], *, use_or_syntax: bool = False) -> str:
Expand Down
28 changes: 28 additions & 0 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -2737,3 +2737,31 @@ def f() -> None:
[file mypy.ini]
\[mypy]
disallow_redefinition = true

[case testRevealSimpleTypes_no_verbose_reveal]
from typing import Generic, TypeVar, Callable
from typing_extensions import TypeVarTuple, Unpack, TypedDict

T = TypeVar("T", bound=int)
S = TypeVar("S", default=list[T])

class A(Generic[T, S]): ...
reveal_type(A) # N: Revealed type is "def [T <: int, S = list[T]] () -> __main__.A[T, S]"

Ts = TypeVarTuple("Ts")
def foo(x: int, *xs: Unpack[Ts]) -> tuple[int, Unpack[Ts]]: ...
reveal_type(foo) # N: Revealed type is "def [Ts] (x: int, *xs: *Ts) -> tuple[int, *Ts]"

class User(TypedDict):
age: int
name: str

def bar(x: int, **kwargs: Unpack[User]) -> None: ...
reveal_type(bar) # N: Revealed type is "def (x: int, **kwargs: **TypedDict(__main__.User, {'age': int, 'name': str}))"

fn: Callable[[Unpack[Ts], Unpack[User]], tuple[Unpack[Ts]]]
reveal_type(fn) # N: Revealed type is "def [Ts] (*args: *Ts, **kwargs: **TypedDict(__main__.User, {'age': int, 'name': str})) -> tuple[*Ts]"

fx: Callable[[int, Unpack[Ts], int], tuple[Unpack[Ts]]]
reveal_type(fx) # N: Revealed type is "def [Ts] (int, *args: *tuple[*Ts, int]) -> tuple[*Ts]"
[builtins fixtures/tuple.pyi]
8 changes: 4 additions & 4 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -2432,7 +2432,7 @@ reveal_type(test) # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __m
reveal_type(test(1, 2, 3)) # N: Revealed type is "__main__.CM[tuple[Literal[1]?, Literal[2]?, Literal[3]?]]"
[builtins fixtures/tuple.pyi]

[case testTypeVarTupleAgainstParamSpecActualFailedNoCrash]
[case testTypeVarTupleAgainstParamSpecActualFailedNoCrash_no_verbose_reveal]
from typing import Generic, TypeVar, TypeVarTuple, Unpack, Callable, Tuple, List
from typing_extensions import ParamSpec

Expand All @@ -2443,7 +2443,7 @@ class CM(Generic[R]): ...
def cm(fn: Callable[P, List[R]]) -> Callable[P, CM[R]]: ...

Ts = TypeVarTuple("Ts")
@cm # E: Argument 1 to "cm" has incompatible type "def [Ts`-1] test(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]"; expected "def (*args: Never) -> list[Never]"
@cm # E: Argument 1 to "cm" has incompatible type "def [Ts] test(*args: *Ts) -> tuple[*Ts]"; expected "def (*args: Never) -> list[Never]"
def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ...

reveal_type(test) # N: Revealed type is "def (*args: Never) -> __main__.CM[Never]"
Expand Down Expand Up @@ -2719,7 +2719,7 @@ x: MyTuple[int, str]
reveal_type(x[0]) # N: Revealed type is "Any"
[builtins fixtures/tuple.pyi]

[case testHigherOrderFunctionUnpackTypeVarTupleViaParamSpec]
[case testHigherOrderFunctionUnpackTypeVarTupleViaParamSpec_no_verbose_reveal]
from typing import Callable, ParamSpec, TypeVar, TypeVarTuple, Unpack

P = ParamSpec("P")
Expand All @@ -2739,7 +2739,7 @@ def foo() -> str:


# this is a false positive, but it no longer crashes
call(run, foo, some_kwarg="a") # E: Argument 1 to "call" has incompatible type "def [Ts`-1, T] run(func: def (*Unpack[Ts]) -> T, *args: Unpack[Ts], some_kwarg: str = ...) -> T"; expected "Callable[[Callable[[], str], str], str]"
call(run, foo, some_kwarg="a") # E: Argument 1 to "call" has incompatible type "def [Ts, T] run(func: def (*args: *Ts) -> T, *args: *Ts, some_kwarg: str = ...) -> T"; expected "Callable[[Callable[[], str], str], str]"
[builtins fixtures/tuple.pyi]

[case testTypeVarTuplePassedParameters]
Expand Down
Loading