From a4d99eff559e6cf79dff52f69850782dcace4393 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 17 Jan 2022 10:00:09 -0800 Subject: [PATCH 1/6] bpo-46414: Add typing.reveal_type Part of the documentation was taken from mypy: https://mypy.readthedocs.io/en/stable/common_issues.html#reveal-type --- Doc/library/typing.rst | 28 +++++++++++++++++++ Lib/test/test_typing.py | 7 +++++ Lib/typing.py | 21 ++++++++++++++ .../2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst | 1 + 4 files changed, 57 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cb14db90711cff..73acb7970bd678 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1932,6 +1932,34 @@ Functions and decorators runtime we intentionally don't check anything (we want this to be as fast as possible). +.. function:: reveal_type(obj) + + Reveal the inferred static type of an expression. + + When a static type checker encounters a call to this function, + it emits a diagnostic with the type of the argument. For example:: + + x: int = 1 + reveal_type(x) # Revealed type is "builtins.int" + + This can be useful when you want to debug how your type checker + handles a particular piece of code. + + The function returns its argument unchanged, which allows using + it within an expression:: + + x = reveal_type(1) # Revealed type is "builtins.int" + + Most type checkers support ``reveal_type()`` anywhere, even if the + name is not imported from ``typing``. Importing the name from + ``typing`` allows your code to run without runtime errors and + communicates intent more clearly. + + At runtime, this function prints the runtime type of its argument + and returns it unchanged. + + .. versionadded:: 3.11 + .. decorator:: overload The ``@overload`` decorator allows describing functions and methods diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c8a077e2f1ff55..b94a2558f61f5d 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -20,6 +20,7 @@ from typing import get_type_hints from typing import get_origin, get_args from typing import is_typeddict +from typing import reveal_type from typing import no_type_check, no_type_check_decorator from typing import Type from typing import NewType @@ -5108,6 +5109,12 @@ def bar(self): self.assertIn('baz', dir(Foo[int])) +class RevealTypeTests(BaseTestCase): + def test_reveal_type(self): + obj = object() + self.assertIs(obj, reveal_type(obj)) + + class AllTests(BaseTestCase): """Tests for __all__.""" diff --git a/Lib/typing.py b/Lib/typing.py index 972b8ba24b27e8..5804a581bc00d6 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -130,6 +130,7 @@ def _idfunc(_, x): 'overload', 'ParamSpecArgs', 'ParamSpecKwargs', + 'reveal_type', 'runtime_checkable', 'Text', 'TYPE_CHECKING', @@ -2667,3 +2668,23 @@ class re(metaclass=_DeprecatedType): re.__name__ = __name__ + '.re' sys.modules[re.__name__] = re + + +def reveal_type(obj: T, /) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print("Runtime type is", type(obj)) + return obj diff --git a/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst b/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst new file mode 100644 index 00000000000000..cb1cfee45b5cb0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst @@ -0,0 +1 @@ +Add ``typing.reveal_type()``. Patch by Jelle Zijlstra. From e24a8a2fbc3f5db8d7f8df3a4fb169e3dfa9020e Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 17 Jan 2022 11:15:46 -0800 Subject: [PATCH 2/6] Update Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst Co-authored-by: Nikita Sobolev --- .../next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst b/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst index cb1cfee45b5cb0..0fdbfa77d9bc8b 100644 --- a/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst +++ b/Misc/NEWS.d/next/Library/2022-01-17-10-00-02.bpo-46414.Ld0b_y.rst @@ -1 +1 @@ -Add ``typing.reveal_type()``. Patch by Jelle Zijlstra. +Add :func:`typing.reveal_type`. Patch by Jelle Zijlstra. From 8f5f750dc57b20f0905272c727bc1b3a3b033234 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 17 Jan 2022 11:17:23 -0800 Subject: [PATCH 3/6] add example --- Doc/library/typing.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 73acb7970bd678..5b1ed4da9ed470 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1956,7 +1956,10 @@ Functions and decorators communicates intent more clearly. At runtime, this function prints the runtime type of its argument - and returns it unchanged. + and returns it unchanged:: + + x = reveal_type(1) # prints "Runtime type is int" + print(x) # prints "1" .. versionadded:: 3.11 From 742884a00706739773d77eedacee64d52c802bc1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 22 Jan 2022 12:56:09 -0800 Subject: [PATCH 4/6] test the stdout and make it nicer --- Lib/test/test_typing.py | 6 ++++-- Lib/typing.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b94a2558f61f5d..40a057831e0789 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -36,7 +36,7 @@ import weakref import types -from test.support import import_helper +from test.support import import_helper, captured_stdout from test import mod_generics_cache from test import _typed_dict_helper @@ -5112,7 +5112,9 @@ def bar(self): class RevealTypeTests(BaseTestCase): def test_reveal_type(self): obj = object() - self.assertIs(obj, reveal_type(obj)) + with captured_stdout() as stdout: + self.assertIs(obj, reveal_type(obj)) + self.assertEqual(stdout.getvalue(), "Runtime type is 'object'\n") class AllTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index 5804a581bc00d6..26f7c4e76da318 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2686,5 +2686,5 @@ def reveal_type(obj: T, /) -> T: argument and returns it unchanged. """ - print("Runtime type is", type(obj)) + print(f"Runtime type is {type(obj).__name__!r}") return obj From e447293d0290e53f30c0446cc37e3cb4181c4106 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 22 Jan 2022 17:25:36 -0800 Subject: [PATCH 5/6] write to stderr --- Lib/test/test_typing.py | 6 +++--- Lib/typing.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 40a057831e0789..04fbaecb10d82f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -36,7 +36,7 @@ import weakref import types -from test.support import import_helper, captured_stdout +from test.support import import_helper, captured_stderr from test import mod_generics_cache from test import _typed_dict_helper @@ -5112,9 +5112,9 @@ def bar(self): class RevealTypeTests(BaseTestCase): def test_reveal_type(self): obj = object() - with captured_stdout() as stdout: + with captured_stderr() as stderr: self.assertIs(obj, reveal_type(obj)) - self.assertEqual(stdout.getvalue(), "Runtime type is 'object'\n") + self.assertEqual(stderr.getvalue(), "Runtime type is 'object'\n") class AllTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index 26f7c4e76da318..dd5a4e133a66b9 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2686,5 +2686,5 @@ def reveal_type(obj: T, /) -> T: argument and returns it unchanged. """ - print(f"Runtime type is {type(obj).__name__!r}") + print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr) return obj From b2bbe064bcb8d59c52c5ab4d32f33eac2488d00e Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 22 Jan 2022 17:28:28 -0800 Subject: [PATCH 6/6] Update Doc/library/typing.rst Co-authored-by: Guido van Rossum --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 5b1ed4da9ed470..5aa2ce05abdb18 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1955,7 +1955,7 @@ Functions and decorators ``typing`` allows your code to run without runtime errors and communicates intent more clearly. - At runtime, this function prints the runtime type of its argument + At runtime, this function prints the runtime type of its argument to stderr and returns it unchanged:: x = reveal_type(1) # prints "Runtime type is int"