self.assertEqual(gc.garbage, old_garbage)
+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().
+
+ def test_except_throw(self):
+ def store_raise_exc_generator():
+ try:
+ self.assertEqual(sys.exc_info()[0], None)
+ yield
+ except Exception as exc:
+ # exception raised by gen.throw(exc)
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ self.assertIsNone(exc.__context__)
+ yield
+
+ # ensure that the exception is not lost
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ yield
+
+ # we should be able to raise back the ValueError
+ raise
+
+ make = store_raise_exc_generator()
+ next(make)
+
+ try:
+ raise ValueError()
+ except Exception as exc:
+ try:
+ make.throw(exc)
+ except Exception:
+ pass
+
+ next(make)
+ with self.assertRaises(ValueError) as cm:
+ next(make)
+ self.assertIsNone(cm.exception.__context__)
+
+ self.assertEqual(sys.exc_info(), (None, None, None))
+
+ def test_except_next(self):
+ def gen():
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ yield "done"
+
+ g = gen()
+ try:
+ raise ValueError
+ except Exception:
+ self.assertEqual(next(g), "done")
+ self.assertEqual(sys.exc_info(), (None, None, None))
+
+ def test_except_gen_except(self):
+ def gen():
+ try:
+ self.assertEqual(sys.exc_info()[0], None)
+ yield
+ # we are called from "except ValueError:", TypeError must
+ # inherit ValueError in its context
+ raise TypeError()
+ except TypeError as exc:
+ self.assertEqual(sys.exc_info()[0], TypeError)
+ self.assertEqual(type(exc.__context__), ValueError)
+ # here we are still called from the "except ValueError:"
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ yield
+ self.assertIsNone(sys.exc_info()[0])
+ yield "done"
+
+ g = gen()
+ next(g)
+ try:
+ raise ValueError
+ except Exception:
+ next(g)
+
+ self.assertEqual(next(g), "done")
+ self.assertEqual(sys.exc_info(), (None, None, None))
+
+ def test_except_throw_exception_context(self):
+ def gen():
+ try:
+ try:
+ self.assertEqual(sys.exc_info()[0], None)
+ yield
+ except ValueError:
+ # we are called from "except ValueError:"
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ raise TypeError()
+ except Exception as exc:
+ self.assertEqual(sys.exc_info()[0], TypeError)
+ self.assertEqual(type(exc.__context__), ValueError)
+ # we are still called from "except ValueError:"
+ self.assertEqual(sys.exc_info()[0], ValueError)
+ yield
+ self.assertIsNone(sys.exc_info()[0])
+ yield "done"
+
+ g = gen()
+ next(g)
+ try:
+ raise ValueError
+ except Exception as exc:
+ g.throw(exc)
+
+ self.assertEqual(next(g), "done")
+ self.assertEqual(sys.exc_info(), (None, None, None))
+
+
tutorial_tests = """
Let's try a simple generator: