Skip to content

Commit 750fb76

Browse files
Morgan BartholomewMorgan Bartholomew
authored andcommitted
show 'narrowed from' in reveal_type
1 parent 4e0e763 commit 750fb76

File tree

9 files changed

+101
-29
lines changed

9 files changed

+101
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Basedmypy Changelog
22

33
## [Unreleased]
4+
### Enhancements
5+
- Show 'narrowed from' in `reveal_type` (#550)
46
### Fixes
57
- Render star args in error messages properly (#551)
68

docs/source/based_features.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,15 @@ Basedmypy shows::
253253

254254
Revealed type is "(T@f, str | 1 | 2)"
255255
Revealed type is "def [T: int] (a: T, b: str | 1 | 2) -> Never"
256+
257+
258+
Reveal Type Narrowed
259+
--------------------
260+
261+
The defined type of a variable will be shown in the message for `reveal_type`:
262+
263+
.. code-block:: python
264+
265+
a: object
266+
a = 1
267+
reveal_type(a) # Revealed type is "int" (narrowed from "object")

mypy/checkexpr.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4491,8 +4491,26 @@ def visit_reveal_expr(self, expr: RevealExpr) -> Type:
44914491
revealed_type = self.accept(
44924492
expr.expr, type_context=self.type_context[-1], allow_none_return=True
44934493
)
4494+
4495+
defined = None
4496+
if isinstance(expr.expr, NameExpr):
4497+
if isinstance(expr.expr.node, Var):
4498+
defined = expr.expr.node.type
4499+
# TODO: attributes, methods, properties
4500+
# elif isinstance(expr.expr, MemberExpr):
4501+
# if isinstance(expr.expr.expr, NameExpr):
4502+
# if isinstance(expr.expr.expr.node, Var):
4503+
# ert = expr.expr.expr.node.type
4504+
# if isinstance(ert, Instance):
4505+
# a = ert.type.names.get(expr.expr.name)
4506+
# if a:
4507+
# defined = a.type
4508+
4509+
if not mypy.options._based:
4510+
defined = None
4511+
44944512
if not self.chk.current_node_deferred:
4495-
self.msg.reveal_type(revealed_type, expr.expr)
4513+
self.msg.reveal_type(revealed_type, expr.expr, defined=defined)
44964514
if not self.chk.in_checked_function():
44974515
self.msg.note(
44984516
"'reveal_type' always outputs 'Any' in unchecked functions", expr.expr

mypy/messages.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,9 +1727,21 @@ def invalid_signature_for_special_method(
17271727
context,
17281728
)
17291729

1730-
def reveal_type(self, typ: Type, context: Context) -> None:
1730+
def reveal_type(self, typ: Type, context: Context, defined: Type | None = None) -> None:
17311731
visitor = TypeStrVisitor(options=self.options)
1732-
self.note(f'Revealed type is "{typ.accept(visitor)}"', context, code=codes.REVEAL)
1732+
type_string = typ.accept(visitor)
1733+
defined_string = ""
1734+
if defined:
1735+
defined_string = defined.accept(visitor)
1736+
if defined_string.replace("partially defined: ", "") == type_string.replace(
1737+
"Any (unannotated)", "?"
1738+
):
1739+
type_string = defined_string
1740+
defined_string = ""
1741+
message = f'Revealed type is "{type_string}"'
1742+
if defined_string:
1743+
message += f' (narrowed from "{defined_string}")'
1744+
self.note(message, context, code=codes.REVEAL)
17331745

17341746
def reveal_locals(self, type_map: dict[str, Type | None], context: Context) -> None:
17351747
# To ensure that the output is predictable on Python < 3.6,

mypy/test/helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,6 @@ def parse_options(
357357
raise RuntimeError("Specifying targets via the flags pragma is not supported.")
358358
if not based and "--show-error-codes" not in flag_list:
359359
options.hide_error_codes = True
360-
print(options.pretty)
361360
else:
362361
flag_list = []
363362
options = Options()

mypy/types.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3636,9 +3636,11 @@ def visit_intersection_type(self, t: IntersectionType) -> str:
36363636

36373637
def visit_partial_type(self, t: PartialType) -> str:
36383638
if t.type is None:
3639-
return "<partial None>"
3639+
return "partially defined: ? | None"
36403640
else:
3641-
return "<partial {}[{}]>".format(t.type.name, ", ".join(["?"] * len(t.type.type_vars)))
3641+
return "partially defined: {}[{}]".format(
3642+
t.type.name, ", ".join(["?"] * len(t.type.type_vars))
3643+
)
36423644

36433645
def visit_ellipsis_type(self, t: EllipsisType) -> str:
36443646
return "..."

test-data/unit/check-based-intersection.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class B: pass
6060

6161
i: A
6262
assert isinstance(i, B)
63-
reveal_type(i) # N: Revealed type is "__main__.A & __main__.B"
63+
reveal_type(i) # N: Revealed type is "__main__.A & __main__.B" (narrowed from "__main__.A")
6464
[builtins fixtures/tuple.pyi]
6565

6666

@@ -146,7 +146,7 @@ a = ""
146146

147147
b: Any
148148
assert isinstance(b, str)
149-
reveal_type(b) # N: Revealed type is "str"
149+
reveal_type(b) # N: Revealed type is "str" (narrowed from "Any")
150150
[builtins fixtures/tuple.pyi]
151151

152152

test-data/unit/check-based-type-render.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,30 @@ def f2(*args: int, **kwargs: str): ...
9292
a = f2
9393
a = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "(*args: int, **kwargs: str) -> None") [assignment]
9494
[builtins fixtures/tuple.pyi]
95+
96+
97+
[case testNarrowedFrom]
98+
a: object
99+
a = 1
100+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
101+
102+
103+
[case testNarrowedFromClass]
104+
# XFAIL
105+
class A:
106+
a: object
107+
class B(A):
108+
a: int
109+
a: A
110+
a = B()
111+
reveal_type(a.a) # N: Revealed type is "int"
112+
# Actually expected N: Revealed type is "int" (narrowed from "object")
113+
114+
115+
[case testNarrowedFromPartial]
116+
a = [] # E: Need type annotation for "a" (hint: "a: list[<type>] = ...") [var-annotated]
117+
reveal_type(a) # E: Expression type contains "Any" (has type "list[Any (unannotated)]") [no-any-expr] \
118+
# E: Expression type contains "Any" (has type "list[Any (unannotated)]") [no-any-expr] \
119+
# N: Revealed type is "partially defined: list[?]"
120+
b = None # E: Need type annotation for "b" (hint: "b: Optional[<type>] = ...") [var-annotated]
121+
reveal_type(b) # N: Revealed type is "None" (narrowed from "partially defined: ? | None")

test-data/unit/check-based-typeguard.test

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ def r(t: T) -> T: ...
77
a: object
88
f2 = r(f)
99
assert f2(a)
10-
reveal_type(a) # N: Revealed type is "int"
10+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
1111

1212
b: object
1313
assert (lambda x: (lambda y: r(f)(y))(x))(b)
14-
reveal_type(b) # N: Revealed type is "int"
14+
reveal_type(b) # N: Revealed type is "int" (narrowed from "object")
1515
[builtins fixtures/tuple.pyi]
1616

1717

@@ -21,7 +21,7 @@ def f(it: object) -> TypeGuard[int]: ...
2121

2222
a: object
2323
assert (lambda: f(a))()
24-
reveal_type(a) # N: Revealed type is "int"
24+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
2525
[builtins fixtures/tuple.pyi]
2626

2727

@@ -42,13 +42,13 @@ def guard(x: object) -> "x is int": ...
4242

4343
o: object
4444
if (lambda y: guard(y))(o):
45-
reveal_type(o) # N: Revealed type is "int"
45+
reveal_type(o) # N: Revealed type is "int" (narrowed from "object")
4646

4747
if (lambda _, y: guard(y))(1, o):
48-
reveal_type(o) # N: Revealed type is "int"
48+
reveal_type(o) # N: Revealed type is "int" (narrowed from "object")
4949

5050
if (lambda _, y: guard(x=y))(1, o):
51-
reveal_type(o) # N: Revealed type is "int"
51+
reveal_type(o) # N: Revealed type is "int" (narrowed from "object")
5252

5353
if (lambda _, y: guard(y))(o, 1):
5454
reveal_type(o) # N: Revealed type is "object"
@@ -58,7 +58,7 @@ class A:
5858
class B(A): ...
5959
a: A
6060
assert (lambda x: x.guard())(a)
61-
reveal_type(a) # N: Revealed type is "__main__.B"
61+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
6262
[builtins fixtures/tuple.pyi]
6363

6464

@@ -117,7 +117,7 @@ class A:
117117

118118
a: object
119119
assert A.guard(A(), a)
120-
reveal_type(a) # N: Revealed type is "int"
120+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
121121
[builtins fixtures/tuple.pyi]
122122

123123

@@ -129,7 +129,7 @@ class B(A): ...
129129

130130
a: A
131131
assert a.guard()
132-
reveal_type(a) # N: Revealed type is "__main__.B"
132+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
133133

134134

135135
[case testGuardCls]
@@ -142,7 +142,7 @@ class B(A): ...
142142

143143
a: Type[A]
144144
assert a.guard()
145-
reveal_type(a) # N: Revealed type is "type[__main__.B]"
145+
reveal_type(a) # N: Revealed type is "type[__main__.B]" (narrowed from "type[__main__.A]")
146146
[builtins fixtures/classmethod.pyi]
147147

148148

@@ -151,7 +151,7 @@ def guard(x: object, /) -> "x is int": ...
151151

152152
a: object
153153
assert guard(a)
154-
reveal_type(a) # N: Revealed type is "int"
154+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
155155
reveal_type(guard) # N: Revealed type is "def (object) -> argument 1 is int"
156156
[builtins fixtures/tuple.pyi]
157157

@@ -161,7 +161,7 @@ from __future__ import annotations
161161
def guard(x: object) -> x is int: ...
162162
a: object
163163
assert guard(a)
164-
reveal_type(a) # N: Revealed type is "int"
164+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
165165
[builtins fixtures/tuple.pyi]
166166

167167

@@ -180,7 +180,7 @@ def make_guard() -> Callable[[object], TypeGuard[int]]: ...
180180
a: object
181181
assert make_guard()() # E: Too few arguments [call-arg]
182182
assert make_guard()(a)
183-
reveal_type(a) # N: Revealed type is "int"
183+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
184184
[builtins fixtures/tuple.pyi]
185185

186186

@@ -190,7 +190,7 @@ class A:
190190
class B(A): ...
191191

192192
assert (a := A()).g()
193-
reveal_type(a) # N: Revealed type is "__main__.B"
193+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
194194
[builtins fixtures/tuple.pyi]
195195

196196

@@ -225,7 +225,7 @@ class B(A): ...
225225

226226
a: A
227227
assert a()
228-
reveal_type(a) # N: Revealed type is "__main__.B"
228+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
229229
[builtins fixtures/tuple.pyi]
230230

231231

@@ -238,9 +238,9 @@ class B(A): ...
238238

239239
a: A
240240
if (lambda x: x.guard())(a):
241-
reveal_type(a) # N: Revealed type is "__main__.B"
241+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
242242
if (lambda x: x())(a):
243-
reveal_type(a) # N: Revealed type is "__main__.B"
243+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
244244
[builtins fixtures/tuple.pyi]
245245

246246

@@ -263,7 +263,7 @@ class B(A): ...
263263
a: A
264264
o: object
265265
assert a.guard(o)
266-
reveal_type(a) # N: Revealed type is "__main__.B"
266+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
267267
reveal_type(o) # N: Revealed type is "object"
268268
[builtins fixtures/tuple.pyi]
269269

@@ -307,7 +307,7 @@ class A:
307307
class B(A): ...
308308
a: A
309309
assert a.guard()
310-
reveal_type(a) # N: Revealed type is "__main__.B"
310+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
311311
[builtins fixtures/tuple.pyi]
312312

313313

@@ -319,7 +319,7 @@ class A:
319319
class B(A): ...
320320
a: A
321321
assert a.guard()
322-
reveal_type(a) # N: Revealed type is "__main__.B"
322+
reveal_type(a) # N: Revealed type is "__main__.B" (narrowed from "__main__.A")
323323
[builtins fixtures/tuple.pyi]
324324

325325

@@ -328,7 +328,7 @@ def guard(a: object) -> 'a is int': ...
328328

329329
a: object
330330
assert guard(a)
331-
reveal_type(a) # N: Revealed type is "int"
331+
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
332332
[builtins fixtures/tuple.pyi]
333333

334334

0 commit comments

Comments
 (0)