[3.10] bpo-46032: Check types in singledispatch's register() at declaration time (GH-30050) (GH-30254)
The registry() method of functools.singledispatch() functions checks now
the first argument or the first parameter annotation and raises a TypeError if it is
not supported. Previously unsupported "types" were ignored (e.g. typing.List[int])
or caused an error at calling time (e.g. list[int]).
(cherry picked from commit 078abb676cf759b1e960f78390b6e80f256f0255)
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index 7bc355f..9343d4e 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -2665,6 +2665,74 @@ def f(*args):
with self.assertRaisesRegex(TypeError, msg):
f()
+ def test_register_genericalias(self):
+ @functools.singledispatch
+ def f(arg):
+ return "default"
+
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(list[int], lambda arg: "types.GenericAlias")
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.List[int], lambda arg: "typing.GenericAlias")
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(list[int] | str, lambda arg: "types.UnionTypes(types.GenericAlias)")
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.List[float] | bytes, lambda arg: "typing.Union[typing.GenericAlias]")
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.Any, lambda arg: "typing.Any")
+
+ self.assertEqual(f([1]), "default")
+ self.assertEqual(f([1.0]), "default")
+ self.assertEqual(f(""), "default")
+ self.assertEqual(f(b""), "default")
+
+ def test_register_genericalias_decorator(self):
+ @functools.singledispatch
+ def f(arg):
+ return "default"
+
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(list[int])
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.List[int])
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(list[int] | str)
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.List[int] | str)
+ with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
+ f.register(typing.Any)
+
+ def test_register_genericalias_annotation(self):
+ @functools.singledispatch
+ def f(arg):
+ return "default"
+
+ with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
+ @f.register
+ def _(arg: list[int]):
+ return "types.GenericAlias"
+ with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
+ @f.register
+ def _(arg: typing.List[float]):
+ return "typing.GenericAlias"
+ with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
+ @f.register
+ def _(arg: list[int] | str):
+ return "types.UnionType(types.GenericAlias)"
+ with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
+ @f.register
+ def _(arg: typing.List[float] | bytes):
+ return "typing.Union[typing.GenericAlias]"
+ with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
+ @f.register
+ def _(arg: typing.Any):
+ return "typing.Any"
+
+ self.assertEqual(f([1]), "default")
+ self.assertEqual(f([1.0]), "default")
+ self.assertEqual(f(""), "default")
+ self.assertEqual(f(b""), "default")
+
class CachedCostItem:
_cost = 1