Add more tests for `(Async)Generator` (#385)
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index ad35373..43b8c09 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py
@@ -1590,12 +1590,92 @@ self.assertIsInstance(d, collections.Counter) self.assertIsInstance(d, typing_extensions.Counter) - def test_async_generator(self): + +# These are a separate TestCase class, +# as (unlike most collections.abc aliases in typing_extensions), +# these are reimplemented on Python <=3.12 so that we can provide +# default values for the second and third parameters +class GeneratorTests(BaseTestCase): + + def test_generator_basics(self): + def foo(): + yield 42 + g = foo() + + self.assertIsInstance(g, typing_extensions.Generator) + self.assertNotIsInstance(foo, typing_extensions.Generator) + self.assertIsSubclass(type(g), typing_extensions.Generator) + self.assertNotIsSubclass(type(foo), typing_extensions.Generator) + + parameterized = typing_extensions.Generator[int, str, None] + with self.assertRaises(TypeError): + isinstance(g, parameterized) + with self.assertRaises(TypeError): + issubclass(type(g), parameterized) + + def test_generator_default(self): + g1 = typing_extensions.Generator[int] + g2 = typing_extensions.Generator[int, None, None] + self.assertEqual(get_args(g1), (int, type(None), type(None))) + self.assertEqual(get_args(g1), get_args(g2)) + + g3 = typing_extensions.Generator[int, float] + g4 = typing_extensions.Generator[int, float, None] + self.assertEqual(get_args(g3), (int, float, type(None))) + self.assertEqual(get_args(g3), get_args(g4)) + + def test_no_generator_instantiation(self): + with self.assertRaises(TypeError): + typing_extensions.Generator() + with self.assertRaises(TypeError): + typing_extensions.Generator[T, T, T]() + with self.assertRaises(TypeError): + typing_extensions.Generator[int, int, int]() + + def test_subclassing_generator(self): + class G(typing_extensions.Generator[int, int, None]): + def send(self, value): + pass + def throw(self, typ, val=None, tb=None): + pass + + def g(): yield 0 + + self.assertIsSubclass(G, typing_extensions.Generator) + self.assertIsSubclass(G, typing_extensions.Iterable) + self.assertIsSubclass(G, collections.abc.Generator) + self.assertIsSubclass(G, collections.abc.Iterable) + self.assertNotIsSubclass(type(g), G) + + instance = G() + self.assertIsInstance(instance, typing_extensions.Generator) + self.assertIsInstance(instance, typing_extensions.Iterable) + self.assertIsInstance(instance, collections.abc.Generator) + self.assertIsInstance(instance, collections.abc.Iterable) + self.assertNotIsInstance(type(g), G) + self.assertNotIsInstance(g, G) + + def test_async_generator_basics(self): async def f(): yield 42 - g = f() + + self.assertIsInstance(g, typing_extensions.AsyncGenerator) self.assertIsSubclass(type(g), typing_extensions.AsyncGenerator) + self.assertNotIsInstance(f, typing_extensions.AsyncGenerator) + self.assertNotIsSubclass(type(f), typing_extensions.AsyncGenerator) + + parameterized = typing_extensions.AsyncGenerator[int, str] + with self.assertRaises(TypeError): + isinstance(g, parameterized) + with self.assertRaises(TypeError): + issubclass(type(g), parameterized) + + def test_async_generator_default(self): + ag1 = typing_extensions.AsyncGenerator[int] + ag2 = typing_extensions.AsyncGenerator[int, None] + self.assertEqual(get_args(ag1), (int, type(None))) + self.assertEqual(get_args(ag1), get_args(ag2)) def test_no_async_generator_instantiation(self): with self.assertRaises(TypeError): @@ -1628,16 +1708,67 @@ self.assertNotIsInstance(type(g), G) self.assertNotIsInstance(g, G) - def test_generator_default(self): - g1 = typing_extensions.Generator[int] - g2 = typing_extensions.Generator[int, None, None] - self.assertEqual(get_args(g1), (int, type(None), type(None))) - self.assertEqual(get_args(g1), get_args(g2)) + def test_subclassing_subclasshook(self): - g3 = typing_extensions.Generator[int, float] - g4 = typing_extensions.Generator[int, float, None] - self.assertEqual(get_args(g3), (int, float, type(None))) - self.assertEqual(get_args(g3), get_args(g4)) + class Base(typing_extensions.Generator): + @classmethod + def __subclasshook__(cls, other): + if other.__name__ == 'Foo': + return True + else: + return False + + class C(Base): ... + class Foo: ... + class Bar: ... + self.assertIsSubclass(Foo, Base) + self.assertIsSubclass(Foo, C) + self.assertNotIsSubclass(Bar, C) + + def test_subclassing_register(self): + + class A(typing_extensions.Generator): ... + class B(A): ... + + class C: ... + A.register(C) + self.assertIsSubclass(C, A) + self.assertNotIsSubclass(C, B) + + class D: ... + B.register(D) + self.assertIsSubclass(D, A) + self.assertIsSubclass(D, B) + + class M(): ... + collections.abc.Generator.register(M) + self.assertIsSubclass(M, typing_extensions.Generator) + + def test_collections_as_base(self): + + class M(collections.abc.Generator): ... + self.assertIsSubclass(M, typing_extensions.Generator) + self.assertIsSubclass(M, typing_extensions.Iterable) + + class S(collections.abc.AsyncGenerator): ... + self.assertIsSubclass(S, typing_extensions.AsyncGenerator) + self.assertIsSubclass(S, typing_extensions.AsyncIterator) + + class A(collections.abc.Generator, metaclass=abc.ABCMeta): ... + class B: ... + A.register(B) + self.assertIsSubclass(B, typing_extensions.Generator) + + @skipIf(sys.version_info < (3, 10), "PEP 604 has yet to be") + def test_or_and_ror(self): + self.assertEqual( + typing_extensions.Generator | typing_extensions.AsyncGenerator, + Union[typing_extensions.Generator, typing_extensions.AsyncGenerator] + ) + self.assertEqual( + typing_extensions.Generator | typing.Deque, + Union[typing_extensions.Generator, typing.Deque] + ) class OtherABCTests(BaseTestCase):