Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
bpo-42195: Ensure consistency of Callable's __args__ in collections.abc and typing#23060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
2c4a297588d421050fa13f60ea8a2f3c6dc2c4508e19d29733116c8ef2b593a93d51e4327e1a5e971ccbabd8b983ddca061ab59c5ee2d2e120157386704ffd598d29bc43ebcf37ae3a9adbfcadd1dd6272c210459f71667a7896201890b374e928c66b11d334215c3b585bf19File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| from _collections_abc import * | ||
| from _collections_abc import __all__ | ||
| from _collections_abc import _CallableGenericAlias |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -62,7 +62,6 @@ class BaseTest(unittest.TestCase): | ||
| Iterable, Iterator, | ||
| Reversible, | ||
| Container, Collection, | ||
| Callable, | ||
| Mailbox, _PartialFile, | ||
| ContextVar, Token, | ||
| Field, | ||
| @@ -307,6 +306,63 @@ def test_no_kwargs(self): | ||
| with self.assertRaises(TypeError): | ||
| GenericAlias(bad=float) | ||
| def test_subclassing_types_genericalias(self): | ||
| class SubClass(GenericAlias): ... | ||
| alias = SubClass(list, int) | ||
| class Bad(GenericAlias): | ||
| def __new__(cls, *args, **kwargs): | ||
| super().__new__(cls, *args, **kwargs) | ||
| self.assertEqual(alias, list[int]) | ||
| with self.assertRaises(TypeError): | ||
| Bad(list, int, bad=int) | ||
| def test_abc_callable(self): | ||
Fidget-Spinner marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| # A separate test is needed for Callable since it uses a subclass of | ||
| # GenericAlias. | ||
| alias = Callable[[int, str], float] | ||
| with self.subTest("Testing subscription"): | ||
| self.assertIs(alias.__origin__, Callable) | ||
| self.assertEqual(alias.__args__, (int, str, float)) | ||
| self.assertEqual(alias.__parameters__, ()) | ||
| with self.subTest("Testing instance checks"): | ||
| self.assertIsInstance(alias, GenericAlias) | ||
| with self.subTest("Testing weakref"): | ||
| self.assertEqual(ref(alias)(), alias) | ||
| with self.subTest("Testing pickling"): | ||
| s = pickle.dumps(alias) | ||
| loaded = pickle.loads(s) | ||
| self.assertEqual(alias.__origin__, loaded.__origin__) | ||
| self.assertEqual(alias.__args__, loaded.__args__) | ||
| self.assertEqual(alias.__parameters__, loaded.__parameters__) | ||
| with self.subTest("Testing TypeVar substitution"): | ||
| C1 = Callable[[int, T], T] | ||
| C2 = Callable[[K, T], V] | ||
| C3 = Callable[..., T] | ||
| self.assertEqual(C1[str], Callable[[int, str], str]) | ||
| self.assertEqual(C2[int, float, str], Callable[[int, float], str]) | ||
| self.assertEqual(C3[int], Callable[..., int]) | ||
| with self.subTest("Testing type erasure"): | ||
| class C1(Callable): | ||
| def __call__(self): | ||
| return None | ||
| a = C1[[int], T] | ||
| self.assertIs(a().__class__, C1) | ||
| self.assertEqual(a().__orig_class__, C1[[int], T]) | ||
| # bpo-42195 | ||
| with self.subTest("Testing collections.abc.Callable's consistency " | ||
| "with typing.Callable"): | ||
| c1 = typing.Callable[[int, str], dict] | ||
| c2 = Callable[[int, str], dict] | ||
| self.assertEqual(c1.__args__, c2.__args__) | ||
| self.assertEqual(hash(c1.__args__), hash(c2.__args__)) | ||
| if __name__ == "__main__": | ||
| unittest.main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -446,14 +446,6 @@ def test_cannot_instantiate(self): | ||
| type(c)() | ||
| def test_callable_wrong_forms(self): | ||
| with self.assertRaises(TypeError): | ||
| Callable[[...], int] | ||
| with self.assertRaises(TypeError): | ||
| Callable[(), int] | ||
| with self.assertRaises(TypeError): | ||
| Callable[[()], int] | ||
| with self.assertRaises(TypeError): | ||
| Callable[[int, 1], 2] | ||
| with self.assertRaises(TypeError): | ||
| Callable[int] | ||
| @@ -1807,10 +1799,9 @@ def barfoo2(x: CT): ... | ||
| def test_extended_generic_rules_subclassing(self): | ||
| class T1(Tuple[T, KT]): ... | ||
| class T2(Tuple[T, ...]): ... | ||
| class C1(Callable[[T], T]): ... | ||
| class C2(Callable[..., int]): | ||
| def __call__(self): | ||
| return None | ||
| class C1(typing.Container[T]): | ||
| def __contains__(self, item): | ||
| return False | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in the 3.9 backport this file should be untouched. | ||
| self.assertEqual(T1.__parameters__, (T, KT)) | ||
| self.assertEqual(T1[int, str].__args__, (int, str)) | ||
| @@ -1824,10 +1815,9 @@ def __call__(self): | ||
| ## T2[int, str] | ||
| self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]') | ||
| self.assertEqual(C2.__parameters__, ()) | ||
| self.assertIsInstance(C2(), collections.abc.Callable) | ||
| self.assertIsSubclass(C2, collections.abc.Callable) | ||
| self.assertIsSubclass(C1, collections.abc.Callable) | ||
| self.assertEqual(C1.__parameters__, (T,)) | ||
| self.assertIsInstance(C1(), collections.abc.Container) | ||
| self.assertIsSubclass(C1, collections.abc.Container) | ||
| self.assertIsInstance(T1(), tuple) | ||
| self.assertIsSubclass(T2, tuple) | ||
| with self.assertRaises(TypeError): | ||
| @@ -1861,10 +1851,6 @@ def test_type_erasure_special(self): | ||
| class MyTup(Tuple[T, T]): ... | ||
| self.assertIs(MyTup[int]().__class__, MyTup) | ||
| self.assertEqual(MyTup[int]().__orig_class__, MyTup[int]) | ||
| class MyCall(Callable[..., T]): | ||
| def __call__(self): return None | ||
| self.assertIs(MyCall[T]().__class__, MyCall) | ||
| self.assertEqual(MyCall[T]().__orig_class__, MyCall[T]) | ||
| class MyDict(typing.Dict[T, T]): ... | ||
| self.assertIs(MyDict[int]().__class__, MyDict) | ||
| self.assertEqual(MyDict[int]().__orig_class__, MyDict[int]) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| The ``__args__`` of the parameterized generics for :data:`typing.Callable` | ||
| and :class:`collections.abc.Callable` are now consistent. The ``__args__`` | ||
| for :class:`collections.abc.Callable` are now flattened while | ||
| :data:`typing.Callable`'s have not changed. To allow this change, | ||
| :class:`types.GenericAlias` can now be subclassed and | ||
| ``collections.abc.Callable``'s ``__class_getitem__`` will now return a subclass | ||
| of ``types.GenericAlias``. Tests for typing were also updated to not subclass | ||
| things like ``Callable[..., T]`` as that is not a valid base class. Finally, | ||
| both ``Callable``s no longer validate their ``argtypes``, in | ||
| ``Callable[[argtypes], resulttype]`` to prepare for :pep:`612`. Patch by Ken Jin. | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, for 3.10 I think this is the right solution.
For 3.9, I think we need to issue a
DeprecationWarning(using the warnings module) with the same message (for bothTypeErrors below) and return what was returned in 3.9.0.