summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_generators.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_generators.py')
-rw-r--r--Lib/test/test_generators.py138
1 files changed, 117 insertions, 21 deletions
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 5c455cd..25cc628 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -1,7 +1,10 @@
import gc
import sys
import unittest
+import warnings
import weakref
+import inspect
+import types
from test import support
@@ -70,6 +73,45 @@ class FinalizationTest(unittest.TestCase):
self.assertEqual(cm.exception.value, 2)
+class GeneratorTest(unittest.TestCase):
+
+ def test_name(self):
+ def func():
+ yield 1
+
+ # check generator names
+ gen = func()
+ self.assertEqual(gen.__name__, "func")
+ self.assertEqual(gen.__qualname__,
+ "GeneratorTest.test_name.<locals>.func")
+
+ # modify generator names
+ gen.__name__ = "name"
+ gen.__qualname__ = "qualname"
+ self.assertEqual(gen.__name__, "name")
+ self.assertEqual(gen.__qualname__, "qualname")
+
+ # generator names must be a string and cannot be deleted
+ self.assertRaises(TypeError, setattr, gen, '__name__', 123)
+ self.assertRaises(TypeError, setattr, gen, '__qualname__', 123)
+ self.assertRaises(TypeError, delattr, gen, '__name__')
+ self.assertRaises(TypeError, delattr, gen, '__qualname__')
+
+ # modify names of the function creating the generator
+ func.__qualname__ = "func_qualname"
+ func.__name__ = "func_name"
+ gen = func()
+ self.assertEqual(gen.__name__, "func_name")
+ self.assertEqual(gen.__qualname__, "func_qualname")
+
+ # unnamed generator
+ gen = (x for x in range(10))
+ self.assertEqual(gen.__name__,
+ "<genexpr>")
+ self.assertEqual(gen.__qualname__,
+ "GeneratorTest.test_name.<locals>.<genexpr>")
+
+
class ExceptionTest(unittest.TestCase):
# Tests for the issue #23353: check that the currently handled exception
# is correctly saved/restored in PyEval_EvalFrameEx().
@@ -178,6 +220,79 @@ class ExceptionTest(unittest.TestCase):
self.assertEqual(next(g), "done")
self.assertEqual(sys.exc_info(), (None, None, None))
+ def test_stopiteration_warning(self):
+ # See also PEP 479.
+
+ def gen():
+ raise StopIteration
+ yield
+
+ with self.assertRaises(StopIteration), \
+ self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"):
+
+ next(gen())
+
+ with self.assertRaisesRegex(PendingDeprecationWarning,
+ "generator .* raised StopIteration"), \
+ warnings.catch_warnings():
+
+ warnings.simplefilter('error')
+ next(gen())
+
+
+ def test_tutorial_stopiteration(self):
+ # Raise StopIteration" stops the generator too:
+
+ def f():
+ yield 1
+ raise StopIteration
+ yield 2 # never reached
+
+ g = f()
+ self.assertEqual(next(g), 1)
+
+ with self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"):
+ with self.assertRaises(StopIteration):
+ next(g)
+
+ with self.assertRaises(StopIteration):
+ # This time StopIteration isn't raised from the generator's body,
+ # hence no warning.
+ next(g)
+
+
+class YieldFromTests(unittest.TestCase):
+ def test_generator_gi_yieldfrom(self):
+ def a():
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_RUNNING)
+ self.assertIsNone(gen_b.gi_yieldfrom)
+ yield
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_RUNNING)
+ self.assertIsNone(gen_b.gi_yieldfrom)
+
+ def b():
+ self.assertIsNone(gen_b.gi_yieldfrom)
+ yield from a()
+ self.assertIsNone(gen_b.gi_yieldfrom)
+ yield
+ self.assertIsNone(gen_b.gi_yieldfrom)
+
+ gen_b = b()
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_CREATED)
+ self.assertIsNone(gen_b.gi_yieldfrom)
+
+ gen_b.send(None)
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_SUSPENDED)
+ self.assertEqual(gen_b.gi_yieldfrom.gi_code.co_name, 'a')
+
+ gen_b.send(None)
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_SUSPENDED)
+ self.assertIsNone(gen_b.gi_yieldfrom)
+
+ [] = gen_b # Exhaust generator
+ self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_CLOSED)
+ self.assertIsNone(gen_b.gi_yieldfrom)
+
tutorial_tests = """
Let's try a simple generator:
@@ -224,26 +339,7 @@ Let's try a simple generator:
File "<stdin>", line 1, in ?
StopIteration
-"raise StopIteration" stops the generator too:
-
- >>> def f():
- ... yield 1
- ... raise StopIteration
- ... yield 2 # never reached
- ...
- >>> g = f()
- >>> next(g)
- 1
- >>> next(g)
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- StopIteration
- >>> next(g)
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- StopIteration
-
-However, they are not exactly equivalent:
+However, "return" and StopIteration are not exactly equivalent:
>>> def g1():
... try:
@@ -563,7 +659,7 @@ From the Iterators list, about the types of these things.
>>> type(i)
<class 'generator'>
>>> [s for s in dir(i) if not s.startswith('_')]
-['close', 'gi_code', 'gi_frame', 'gi_running', 'send', 'throw']
+['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
>>> from test.support import HAVE_DOCSTRINGS
>>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
Implement next(self).