mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
bpo-32227: functools.singledispatch supports registering via type annotations (#4733)
This commit is contained in:
parent
8874342cf3
commit
e56975351b
4 changed files with 108 additions and 8 deletions
|
@ -10,6 +10,7 @@ import sys
|
|||
from test import support
|
||||
import threading
|
||||
import time
|
||||
import typing
|
||||
import unittest
|
||||
import unittest.mock
|
||||
from weakref import proxy
|
||||
|
@ -2119,6 +2120,73 @@ class TestSingleDispatch(unittest.TestCase):
|
|||
g._clear_cache()
|
||||
self.assertEqual(len(td), 0)
|
||||
|
||||
def test_annotations(self):
|
||||
@functools.singledispatch
|
||||
def i(arg):
|
||||
return "base"
|
||||
@i.register
|
||||
def _(arg: collections.abc.Mapping):
|
||||
return "mapping"
|
||||
@i.register
|
||||
def _(arg: "collections.abc.Sequence"):
|
||||
return "sequence"
|
||||
self.assertEqual(i(None), "base")
|
||||
self.assertEqual(i({"a": 1}), "mapping")
|
||||
self.assertEqual(i([1, 2, 3]), "sequence")
|
||||
self.assertEqual(i((1, 2, 3)), "sequence")
|
||||
self.assertEqual(i("str"), "sequence")
|
||||
|
||||
# Registering classes as callables doesn't work with annotations,
|
||||
# you need to pass the type explicitly.
|
||||
@i.register(str)
|
||||
class _:
|
||||
def __init__(self, arg):
|
||||
self.arg = arg
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.arg == other
|
||||
self.assertEqual(i("str"), "str")
|
||||
|
||||
def test_invalid_registrations(self):
|
||||
msg_prefix = "Invalid first argument to `register()`: "
|
||||
msg_suffix = (
|
||||
". Use either `@register(some_class)` or plain `@register` on an "
|
||||
"annotated function."
|
||||
)
|
||||
@functools.singledispatch
|
||||
def i(arg):
|
||||
return "base"
|
||||
with self.assertRaises(TypeError) as exc:
|
||||
@i.register(42)
|
||||
def _(arg):
|
||||
return "I annotated with a non-type"
|
||||
self.assertTrue(str(exc.exception).startswith(msg_prefix + "42"))
|
||||
self.assertTrue(str(exc.exception).endswith(msg_suffix))
|
||||
with self.assertRaises(TypeError) as exc:
|
||||
@i.register
|
||||
def _(arg):
|
||||
return "I forgot to annotate"
|
||||
self.assertTrue(str(exc.exception).startswith(msg_prefix +
|
||||
"<function TestSingleDispatch.test_invalid_registrations.<locals>._"
|
||||
))
|
||||
self.assertTrue(str(exc.exception).endswith(msg_suffix))
|
||||
|
||||
# FIXME: The following will only work after PEP 560 is implemented.
|
||||
return
|
||||
|
||||
with self.assertRaises(TypeError) as exc:
|
||||
@i.register
|
||||
def _(arg: typing.Iterable[str]):
|
||||
# At runtime, dispatching on generics is impossible.
|
||||
# When registering implementations with singledispatch, avoid
|
||||
# types from `typing`. Instead, annotate with regular types
|
||||
# or ABCs.
|
||||
return "I annotated with a generic collection"
|
||||
self.assertTrue(str(exc.exception).startswith(msg_prefix +
|
||||
"<function TestSingleDispatch.test_invalid_registrations.<locals>._"
|
||||
))
|
||||
self.assertTrue(str(exc.exception).endswith(msg_suffix))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue