summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2022-04-25 13:40:18 (GMT)
committerGitHub <noreply@github.com>2022-04-25 13:40:18 (GMT)
commit93d280141c369fd1906569ff605148b6e22f6a43 (patch)
tree01714d7314fffffdac59f85006d0b23759a22044
parent9ff2f12c876289a7192fd1672ed08a1876a51e17 (diff)
downloadcpython-93d280141c369fd1906569ff605148b6e22f6a43.zip
cpython-93d280141c369fd1906569ff605148b6e22f6a43.tar.gz
cpython-93d280141c369fd1906569ff605148b6e22f6a43.tar.bz2
gh-90633: Improve error and docs for typing.assert_never (#91720)
Closes #90633 Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
-rw-r--r--Doc/library/typing.rst16
-rw-r--r--Lib/test/test_typing.py13
-rw-r--r--Lib/typing.py8
-rw-r--r--Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst2
4 files changed, 37 insertions, 2 deletions
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 2f62193..4635da7 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -2345,11 +2345,25 @@ Functions and decorators
case _ as unreachable:
assert_never(unreachable)
+ Here, the annotations allow the type checker to infer that the
+ last case can never execute, because ``arg`` is either
+ an :class:`int` or a :class:`str`, and both options are covered by
+ earlier cases.
If a type checker finds that a call to ``assert_never()`` is
- reachable, it will emit an error.
+ reachable, it will emit an error. For example, if the type annotation
+ for ``arg`` was instead ``int | str | float``, the type checker would
+ emit an error pointing out that ``unreachable`` is of type :class:`float`.
+ For a call to ``assert_never`` to succeed, the inferred type of
+ the argument passed in must be the bottom type, :data:`Never`, and nothing
+ else.
At runtime, this throws an exception when called.
+ .. seealso::
+ `Unreachable Code and Exhaustiveness Checking
+ <https://typing.readthedocs.io/en/latest/source/unreachable.html>_` has more
+ information about exhaustiveness checking with static typing.
+
.. versionadded:: 3.11
.. function:: reveal_type(obj)
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 1fd99a0..fce340f 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -223,6 +223,19 @@ class AssertNeverTests(BaseTestCase):
with self.assertRaises(AssertionError):
assert_never(None)
+ value = "some value"
+ with self.assertRaisesRegex(AssertionError, value):
+ assert_never(value)
+
+ # Make sure a huge value doesn't get printed in its entirety
+ huge_value = "a" * 10000
+ with self.assertRaises(AssertionError) as cm:
+ assert_never(huge_value)
+ self.assertLess(
+ len(cm.exception.args[0]),
+ typing._ASSERT_NEVER_REPR_MAX_LENGTH * 2,
+ )
+
class SelfTests(BaseTestCase):
def test_equality(self):
diff --git a/Lib/typing.py b/Lib/typing.py
index a6f4fa9..9d8149c 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2382,6 +2382,9 @@ def is_typeddict(tp):
return isinstance(tp, _TypedDictMeta)
+_ASSERT_NEVER_REPR_MAX_LENGTH = 100
+
+
def assert_never(arg: Never, /) -> Never:
"""Statically assert that a line of code is unreachable.
@@ -2402,7 +2405,10 @@ def assert_never(arg: Never, /) -> Never:
At runtime, this throws an exception when called.
"""
- raise AssertionError("Expected code to be unreachable")
+ value = repr(arg)
+ if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH:
+ value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...'
+ raise AssertionError(f"Expected code to be unreachable, but got: {value}")
def no_type_check(arg):
diff --git a/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst b/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst
new file mode 100644
index 0000000..d86c2d3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst
@@ -0,0 +1,2 @@
+Include the passed value in the exception thrown by
+:func:`typing.assert_never`. Patch by Jelle Zijlstra.