1def gen():
2    yield 1
3    yield 2
4    yield 3
5    yield 4
6
7def gen2():
8    yield -1
9    print((yield from gen()))
10    yield 10
11    yield 11
12
13g = gen2()
14print(next(g))
15print(next(g))
16g.close()
17try:
18    print(next(g))
19except StopIteration:
20    print("StopIteration")
21
22
23# Now variation of same test, but with leaf generator
24# swallowing GeneratorExit exception - its upstream gen
25# generator should still receive one.
26def gen3():
27    yield 1
28    try:
29        yield 2
30    except GeneratorExit:
31        print("leaf caught GeneratorExit and swallowed it")
32        return
33    yield 3
34    yield 4
35
36def gen4():
37    yield -1
38    try:
39        print((yield from gen3()))
40    except GeneratorExit:
41        print("delegating caught GeneratorExit")
42        raise
43    yield 10
44    yield 11
45
46g = gen4()
47print(next(g))
48print(next(g))
49print(next(g))
50g.close()
51try:
52    print(next(g))
53except StopIteration:
54    print("StopIteration")
55
56
57# Yet another variation - leaf generator gets GeneratorExit,
58# and reraises a new GeneratorExit. This still should close chain properly.
59def gen5():
60    yield 1
61    try:
62        yield 2
63    except GeneratorExit:
64        print("leaf caught GeneratorExit and reraised GeneratorExit")
65        raise GeneratorExit(123)
66    yield 3
67    yield 4
68
69def gen6():
70    yield -1
71    try:
72        print((yield from gen5()))
73    except GeneratorExit:
74        print("delegating caught GeneratorExit")
75        raise
76    yield 10
77    yield 11
78
79g = gen6()
80print(next(g))
81print(next(g))
82print(next(g))
83g.close()
84try:
85    print(next(g))
86except StopIteration:
87    print("StopIteration")
88
89# case where generator ignores the close request and yields instead
90def gen7():
91    try:
92        yield 123
93    except GeneratorExit:
94        yield 456
95
96g = gen7()
97print(next(g))
98try:
99    g.close()
100except RuntimeError:
101    print('RuntimeError')
102
103# case where close is propagated up to a built-in iterator
104def gen8():
105    g = range(2)
106    yield from g
107g = gen8()
108print(next(g))
109g.close()
110
111# case with a user-defined close method
112class Iter:
113    def __iter__(self):
114        return self
115    def __next__(self):
116        return 1
117    def close(self):
118        print('close')
119def gen9():
120    yield from Iter()
121g = gen9()
122print(next(g))
123g.close()
124