|
|
|
@ -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() |