python (3.11.7)
1 import copy
2 import gc
3 import pickle
4 import sys
5 import doctest
6 import unittest
7 import weakref
8 import inspect
9
10 from test import support
11
12 try:
13 import _testcapi
14 except ImportError:
15 _testcapi = None
16
17
18 # This tests to make sure that if a SIGINT arrives just before we send into a
19 # yield from chain, the KeyboardInterrupt is raised in the innermost
20 # generator (see bpo-30039).
21 @unittest.skipUnless(_testcapi is not None and
22 hasattr(_testcapi, "raise_SIGINT_then_send_None"),
23 "needs _testcapi.raise_SIGINT_then_send_None")
24 class ESC[4;38;5;81mSignalAndYieldFromTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
25
26 def generator1(self):
27 return (yield from self.generator2())
28
29 def generator2(self):
30 try:
31 yield
32 except KeyboardInterrupt:
33 return "PASSED"
34 else:
35 return "FAILED"
36
37 def test_raise_and_yield_from(self):
38 gen = self.generator1()
39 gen.send(None)
40 try:
41 _testcapi.raise_SIGINT_then_send_None(gen)
42 except BaseException as _exc:
43 exc = _exc
44 self.assertIs(type(exc), StopIteration)
45 self.assertEqual(exc.value, "PASSED")
46
47
48 class ESC[4;38;5;81mFinalizationTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
49
50 def test_frame_resurrect(self):
51 # A generator frame can be resurrected by a generator's finalization.
52 def gen():
53 nonlocal frame
54 try:
55 yield
56 finally:
57 frame = sys._getframe()
58
59 g = gen()
60 wr = weakref.ref(g)
61 next(g)
62 del g
63 support.gc_collect()
64 self.assertIs(wr(), None)
65 self.assertTrue(frame)
66 del frame
67 support.gc_collect()
68
69 def test_refcycle(self):
70 # A generator caught in a refcycle gets finalized anyway.
71 old_garbage = gc.garbage[:]
72 finalized = False
73 def gen():
74 nonlocal finalized
75 try:
76 g = yield
77 yield 1
78 finally:
79 finalized = True
80
81 g = gen()
82 next(g)
83 g.send(g)
84 self.assertGreater(sys.getrefcount(g), 2)
85 self.assertFalse(finalized)
86 del g
87 support.gc_collect()
88 self.assertTrue(finalized)
89 self.assertEqual(gc.garbage, old_garbage)
90
91 def test_lambda_generator(self):
92 # Issue #23192: Test that a lambda returning a generator behaves
93 # like the equivalent function
94 f = lambda: (yield 1)
95 def g(): return (yield 1)
96
97 # test 'yield from'
98 f2 = lambda: (yield from g())
99 def g2(): return (yield from g())
100
101 f3 = lambda: (yield from f())
102 def g3(): return (yield from f())
103
104 for gen_fun in (f, g, f2, g2, f3, g3):
105 gen = gen_fun()
106 self.assertEqual(next(gen), 1)
107 with self.assertRaises(StopIteration) as cm:
108 gen.send(2)
109 self.assertEqual(cm.exception.value, 2)
110
111
112 class ESC[4;38;5;81mGeneratorTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
113
114 def test_name(self):
115 def func():
116 yield 1
117
118 # check generator names
119 gen = func()
120 self.assertEqual(gen.__name__, "func")
121 self.assertEqual(gen.__qualname__,
122 "GeneratorTest.test_name.<locals>.func")
123
124 # modify generator names
125 gen.__name__ = "name"
126 gen.__qualname__ = "qualname"
127 self.assertEqual(gen.__name__, "name")
128 self.assertEqual(gen.__qualname__, "qualname")
129
130 # generator names must be a string and cannot be deleted
131 self.assertRaises(TypeError, setattr, gen, '__name__', 123)
132 self.assertRaises(TypeError, setattr, gen, '__qualname__', 123)
133 self.assertRaises(TypeError, delattr, gen, '__name__')
134 self.assertRaises(TypeError, delattr, gen, '__qualname__')
135
136 # modify names of the function creating the generator
137 func.__qualname__ = "func_qualname"
138 func.__name__ = "func_name"
139 gen = func()
140 self.assertEqual(gen.__name__, "func_name")
141 self.assertEqual(gen.__qualname__, "func_qualname")
142
143 # unnamed generator
144 gen = (x for x in range(10))
145 self.assertEqual(gen.__name__,
146 "<genexpr>")
147 self.assertEqual(gen.__qualname__,
148 "GeneratorTest.test_name.<locals>.<genexpr>")
149
150 def test_copy(self):
151 def f():
152 yield 1
153 g = f()
154 with self.assertRaises(TypeError):
155 copy.copy(g)
156
157 def test_pickle(self):
158 def f():
159 yield 1
160 g = f()
161 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
162 with self.assertRaises((TypeError, pickle.PicklingError)):
163 pickle.dumps(g, proto)
164
165 def test_send_non_none_to_new_gen(self):
166 def f():
167 yield 1
168 g = f()
169 with self.assertRaises(TypeError):
170 g.send(0)
171 self.assertEqual(next(g), 1)
172
173 def test_handle_frame_object_in_creation(self):
174
175 #Attempt to expose partially constructed frames
176 #See https://github.com/python/cpython/issues/94262
177
178 def cb(*args):
179 inspect.stack()
180
181 def gen():
182 yield 1
183
184 thresholds = gc.get_threshold()
185
186 gc.callbacks.append(cb)
187 gc.set_threshold(1, 0, 0)
188 try:
189 gen()
190 finally:
191 gc.set_threshold(*thresholds)
192 gc.callbacks.pop()
193
194 class ESC[4;38;5;81mSneaky:
195 def __del__(self):
196 inspect.stack()
197
198 sneaky = Sneaky()
199 sneaky._s = Sneaky()
200 sneaky._s._s = sneaky
201
202 gc.set_threshold(1, 0, 0)
203 try:
204 del sneaky
205 gen()
206 finally:
207 gc.set_threshold(*thresholds)
208
209 def test_ag_frame_f_back(self):
210 async def f():
211 yield
212 ag = f()
213 self.assertIsNone(ag.ag_frame.f_back)
214
215 def test_cr_frame_f_back(self):
216 async def f():
217 pass
218 cr = f()
219 self.assertIsNone(cr.cr_frame.f_back)
220 cr.close() # Suppress RuntimeWarning.
221
222 def test_gi_frame_f_back(self):
223 def f():
224 yield
225 gi = f()
226 self.assertIsNone(gi.gi_frame.f_back)
227
228
229
230 class ESC[4;38;5;81mExceptionTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
231 # Tests for the issue #23353: check that the currently handled exception
232 # is correctly saved/restored in PyEval_EvalFrameEx().
233
234 def test_except_throw(self):
235 def store_raise_exc_generator():
236 try:
237 self.assertEqual(sys.exc_info()[0], None)
238 yield
239 except Exception as exc:
240 # exception raised by gen.throw(exc)
241 self.assertEqual(sys.exc_info()[0], ValueError)
242 self.assertIsNone(exc.__context__)
243 yield
244
245 # ensure that the exception is not lost
246 self.assertEqual(sys.exc_info()[0], ValueError)
247 yield
248
249 # we should be able to raise back the ValueError
250 raise
251
252 make = store_raise_exc_generator()
253 next(make)
254
255 try:
256 raise ValueError()
257 except Exception as exc:
258 try:
259 make.throw(exc)
260 except Exception:
261 pass
262
263 next(make)
264 with self.assertRaises(ValueError) as cm:
265 next(make)
266 self.assertIsNone(cm.exception.__context__)
267
268 self.assertEqual(sys.exc_info(), (None, None, None))
269
270 def test_except_next(self):
271 def gen():
272 self.assertEqual(sys.exc_info()[0], ValueError)
273 yield "done"
274
275 g = gen()
276 try:
277 raise ValueError
278 except Exception:
279 self.assertEqual(next(g), "done")
280 self.assertEqual(sys.exc_info(), (None, None, None))
281
282 def test_except_gen_except(self):
283 def gen():
284 try:
285 self.assertEqual(sys.exc_info()[0], None)
286 yield
287 # we are called from "except ValueError:", TypeError must
288 # inherit ValueError in its context
289 raise TypeError()
290 except TypeError as exc:
291 self.assertEqual(sys.exc_info()[0], TypeError)
292 self.assertEqual(type(exc.__context__), ValueError)
293 # here we are still called from the "except ValueError:"
294 self.assertEqual(sys.exc_info()[0], ValueError)
295 yield
296 self.assertIsNone(sys.exc_info()[0])
297 yield "done"
298
299 g = gen()
300 next(g)
301 try:
302 raise ValueError
303 except Exception:
304 next(g)
305
306 self.assertEqual(next(g), "done")
307 self.assertEqual(sys.exc_info(), (None, None, None))
308
309 def test_except_throw_exception_context(self):
310 def gen():
311 try:
312 try:
313 self.assertEqual(sys.exc_info()[0], None)
314 yield
315 except ValueError:
316 # we are called from "except ValueError:"
317 self.assertEqual(sys.exc_info()[0], ValueError)
318 raise TypeError()
319 except Exception as exc:
320 self.assertEqual(sys.exc_info()[0], TypeError)
321 self.assertEqual(type(exc.__context__), ValueError)
322 # we are still called from "except ValueError:"
323 self.assertEqual(sys.exc_info()[0], ValueError)
324 yield
325 self.assertIsNone(sys.exc_info()[0])
326 yield "done"
327
328 g = gen()
329 next(g)
330 try:
331 raise ValueError
332 except Exception as exc:
333 g.throw(exc)
334
335 self.assertEqual(next(g), "done")
336 self.assertEqual(sys.exc_info(), (None, None, None))
337
338 def test_except_throw_bad_exception(self):
339 class ESC[4;38;5;81mE(ESC[4;38;5;149mException):
340 def __new__(cls, *args, **kwargs):
341 return cls
342
343 def boring_generator():
344 yield
345
346 gen = boring_generator()
347
348 err_msg = 'should have returned an instance of BaseException'
349
350 with self.assertRaisesRegex(TypeError, err_msg):
351 gen.throw(E)
352
353 self.assertRaises(StopIteration, next, gen)
354
355 def generator():
356 with self.assertRaisesRegex(TypeError, err_msg):
357 yield
358
359 gen = generator()
360 next(gen)
361 with self.assertRaises(StopIteration):
362 gen.throw(E)
363
364 def test_stopiteration_error(self):
365 # See also PEP 479.
366
367 def gen():
368 raise StopIteration
369 yield
370
371 with self.assertRaisesRegex(RuntimeError, 'raised StopIteration'):
372 next(gen())
373
374 def test_tutorial_stopiteration(self):
375 # Raise StopIteration" stops the generator too:
376
377 def f():
378 yield 1
379 raise StopIteration
380 yield 2 # never reached
381
382 g = f()
383 self.assertEqual(next(g), 1)
384
385 with self.assertRaisesRegex(RuntimeError, 'raised StopIteration'):
386 next(g)
387
388 def test_return_tuple(self):
389 def g():
390 return (yield 1)
391
392 gen = g()
393 self.assertEqual(next(gen), 1)
394 with self.assertRaises(StopIteration) as cm:
395 gen.send((2,))
396 self.assertEqual(cm.exception.value, (2,))
397
398 def test_return_stopiteration(self):
399 def g():
400 return (yield 1)
401
402 gen = g()
403 self.assertEqual(next(gen), 1)
404 with self.assertRaises(StopIteration) as cm:
405 gen.send(StopIteration(2))
406 self.assertIsInstance(cm.exception.value, StopIteration)
407 self.assertEqual(cm.exception.value.value, 2)
408
409
410 class ESC[4;38;5;81mGeneratorThrowTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
411
412 def test_exception_context_with_yield(self):
413 def f():
414 try:
415 raise KeyError('a')
416 except Exception:
417 yield
418
419 gen = f()
420 gen.send(None)
421 with self.assertRaises(ValueError) as cm:
422 gen.throw(ValueError)
423 context = cm.exception.__context__
424 self.assertEqual((type(context), context.args), (KeyError, ('a',)))
425
426 def test_exception_context_with_yield_inside_generator(self):
427 # Check that the context is also available from inside the generator
428 # with yield, as opposed to outside.
429 def f():
430 try:
431 raise KeyError('a')
432 except Exception:
433 try:
434 yield
435 except Exception as exc:
436 self.assertEqual(type(exc), ValueError)
437 context = exc.__context__
438 self.assertEqual((type(context), context.args),
439 (KeyError, ('a',)))
440 yield 'b'
441
442 gen = f()
443 gen.send(None)
444 actual = gen.throw(ValueError)
445 # This ensures that the assertions inside were executed.
446 self.assertEqual(actual, 'b')
447
448 def test_exception_context_with_yield_from(self):
449 def f():
450 yield
451
452 def g():
453 try:
454 raise KeyError('a')
455 except Exception:
456 yield from f()
457
458 gen = g()
459 gen.send(None)
460 with self.assertRaises(ValueError) as cm:
461 gen.throw(ValueError)
462 context = cm.exception.__context__
463 self.assertEqual((type(context), context.args), (KeyError, ('a',)))
464
465 def test_exception_context_with_yield_from_with_context_cycle(self):
466 # Check trying to create an exception context cycle:
467 # https://bugs.python.org/issue40696
468 has_cycle = None
469
470 def f():
471 yield
472
473 def g(exc):
474 nonlocal has_cycle
475 try:
476 raise exc
477 except Exception:
478 try:
479 yield from f()
480 except Exception as exc:
481 has_cycle = (exc is exc.__context__)
482 yield
483
484 exc = KeyError('a')
485 gen = g(exc)
486 gen.send(None)
487 gen.throw(exc)
488 # This also distinguishes from the initial has_cycle=None.
489 self.assertEqual(has_cycle, False)
490
491 def test_throw_after_none_exc_type(self):
492 def g():
493 try:
494 raise KeyError
495 except KeyError:
496 pass
497
498 try:
499 yield
500 except Exception:
501 raise RuntimeError
502
503 gen = g()
504 gen.send(None)
505 with self.assertRaises(RuntimeError) as cm:
506 gen.throw(ValueError)
507
508
509 class ESC[4;38;5;81mGeneratorStackTraceTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
510
511 def check_stack_names(self, frame, expected):
512 names = []
513 while frame:
514 name = frame.f_code.co_name
515 # Stop checking frames when we get to our test helper.
516 if name.startswith('check_') or name.startswith('call_'):
517 break
518
519 names.append(name)
520 frame = frame.f_back
521
522 self.assertEqual(names, expected)
523
524 def check_yield_from_example(self, call_method):
525 def f():
526 self.check_stack_names(sys._getframe(), ['f', 'g'])
527 try:
528 yield
529 except Exception:
530 pass
531 self.check_stack_names(sys._getframe(), ['f', 'g'])
532
533 def g():
534 self.check_stack_names(sys._getframe(), ['g'])
535 yield from f()
536 self.check_stack_names(sys._getframe(), ['g'])
537
538 gen = g()
539 gen.send(None)
540 try:
541 call_method(gen)
542 except StopIteration:
543 pass
544
545 def test_send_with_yield_from(self):
546 def call_send(gen):
547 gen.send(None)
548
549 self.check_yield_from_example(call_send)
550
551 def test_throw_with_yield_from(self):
552 def call_throw(gen):
553 gen.throw(RuntimeError)
554
555 self.check_yield_from_example(call_throw)
556
557
558 class ESC[4;38;5;81mYieldFromTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
559 def test_generator_gi_yieldfrom(self):
560 def a():
561 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_RUNNING)
562 self.assertIsNone(gen_b.gi_yieldfrom)
563 yield
564 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_RUNNING)
565 self.assertIsNone(gen_b.gi_yieldfrom)
566
567 def b():
568 self.assertIsNone(gen_b.gi_yieldfrom)
569 yield from a()
570 self.assertIsNone(gen_b.gi_yieldfrom)
571 yield
572 self.assertIsNone(gen_b.gi_yieldfrom)
573
574 gen_b = b()
575 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_CREATED)
576 self.assertIsNone(gen_b.gi_yieldfrom)
577
578 gen_b.send(None)
579 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_SUSPENDED)
580 self.assertEqual(gen_b.gi_yieldfrom.gi_code.co_name, 'a')
581
582 gen_b.send(None)
583 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_SUSPENDED)
584 self.assertIsNone(gen_b.gi_yieldfrom)
585
586 [] = gen_b # Exhaust generator
587 self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_CLOSED)
588 self.assertIsNone(gen_b.gi_yieldfrom)
589
590
591 tutorial_tests = """
592 Let's try a simple generator:
593
594 >>> def f():
595 ... yield 1
596 ... yield 2
597
598 >>> for i in f():
599 ... print(i)
600 1
601 2
602 >>> g = f()
603 >>> next(g)
604 1
605 >>> next(g)
606 2
607
608 "Falling off the end" stops the generator:
609
610 >>> next(g)
611 Traceback (most recent call last):
612 File "<stdin>", line 1, in ?
613 File "<stdin>", line 2, in g
614 StopIteration
615
616 "return" also stops the generator:
617
618 >>> def f():
619 ... yield 1
620 ... return
621 ... yield 2 # never reached
622 ...
623 >>> g = f()
624 >>> next(g)
625 1
626 >>> next(g)
627 Traceback (most recent call last):
628 File "<stdin>", line 1, in ?
629 File "<stdin>", line 3, in f
630 StopIteration
631 >>> next(g) # once stopped, can't be resumed
632 Traceback (most recent call last):
633 File "<stdin>", line 1, in ?
634 StopIteration
635
636 However, "return" and StopIteration are not exactly equivalent:
637
638 >>> def g1():
639 ... try:
640 ... return
641 ... except:
642 ... yield 1
643 ...
644 >>> list(g1())
645 []
646
647 >>> def g2():
648 ... try:
649 ... raise StopIteration
650 ... except:
651 ... yield 42
652 >>> print(list(g2()))
653 [42]
654
655 This may be surprising at first:
656
657 >>> def g3():
658 ... try:
659 ... return
660 ... finally:
661 ... yield 1
662 ...
663 >>> list(g3())
664 [1]
665
666 Let's create an alternate range() function implemented as a generator:
667
668 >>> def yrange(n):
669 ... for i in range(n):
670 ... yield i
671 ...
672 >>> list(yrange(5))
673 [0, 1, 2, 3, 4]
674
675 Generators always return to the most recent caller:
676
677 >>> def creator():
678 ... r = yrange(5)
679 ... print("creator", next(r))
680 ... return r
681 ...
682 >>> def caller():
683 ... r = creator()
684 ... for i in r:
685 ... print("caller", i)
686 ...
687 >>> caller()
688 creator 0
689 caller 1
690 caller 2
691 caller 3
692 caller 4
693
694 Generators can call other generators:
695
696 >>> def zrange(n):
697 ... for i in yrange(n):
698 ... yield i
699 ...
700 >>> list(zrange(5))
701 [0, 1, 2, 3, 4]
702
703 """
704
705 # The examples from PEP 255.
706
707 pep_tests = """
708
709 Specification: Yield
710
711 Restriction: A generator cannot be resumed while it is actively
712 running:
713
714 >>> def g():
715 ... i = next(me)
716 ... yield i
717 >>> me = g()
718 >>> next(me)
719 Traceback (most recent call last):
720 ...
721 File "<string>", line 2, in g
722 ValueError: generator already executing
723
724 Specification: Return
725
726 Note that return isn't always equivalent to raising StopIteration: the
727 difference lies in how enclosing try/except constructs are treated.
728 For example,
729
730 >>> def f1():
731 ... try:
732 ... return
733 ... except:
734 ... yield 1
735 >>> print(list(f1()))
736 []
737
738 because, as in any function, return simply exits, but
739
740 >>> def f2():
741 ... try:
742 ... raise StopIteration
743 ... except:
744 ... yield 42
745 >>> print(list(f2()))
746 [42]
747
748 because StopIteration is captured by a bare "except", as is any
749 exception.
750
751 Specification: Generators and Exception Propagation
752
753 >>> def f():
754 ... return 1//0
755 >>> def g():
756 ... yield f() # the zero division exception propagates
757 ... yield 42 # and we'll never get here
758 >>> k = g()
759 >>> next(k)
760 Traceback (most recent call last):
761 File "<stdin>", line 1, in ?
762 File "<stdin>", line 2, in g
763 File "<stdin>", line 2, in f
764 ZeroDivisionError: integer division or modulo by zero
765 >>> next(k) # and the generator cannot be resumed
766 Traceback (most recent call last):
767 File "<stdin>", line 1, in ?
768 StopIteration
769 >>>
770
771 Specification: Try/Except/Finally
772
773 >>> def f():
774 ... try:
775 ... yield 1
776 ... try:
777 ... yield 2
778 ... 1//0
779 ... yield 3 # never get here
780 ... except ZeroDivisionError:
781 ... yield 4
782 ... yield 5
783 ... raise
784 ... except:
785 ... yield 6
786 ... yield 7 # the "raise" above stops this
787 ... except:
788 ... yield 8
789 ... yield 9
790 ... try:
791 ... x = 12
792 ... finally:
793 ... yield 10
794 ... yield 11
795 >>> print(list(f()))
796 [1, 2, 4, 5, 8, 9, 10, 11]
797 >>>
798
799 Guido's binary tree example.
800
801 >>> # A binary tree class.
802 >>> class Tree:
803 ...
804 ... def __init__(self, label, left=None, right=None):
805 ... self.label = label
806 ... self.left = left
807 ... self.right = right
808 ...
809 ... def __repr__(self, level=0, indent=" "):
810 ... s = level*indent + repr(self.label)
811 ... if self.left:
812 ... s = s + "\\n" + self.left.__repr__(level+1, indent)
813 ... if self.right:
814 ... s = s + "\\n" + self.right.__repr__(level+1, indent)
815 ... return s
816 ...
817 ... def __iter__(self):
818 ... return inorder(self)
819
820 >>> # Create a Tree from a list.
821 >>> def tree(list):
822 ... n = len(list)
823 ... if n == 0:
824 ... return []
825 ... i = n // 2
826 ... return Tree(list[i], tree(list[:i]), tree(list[i+1:]))
827
828 >>> # Show it off: create a tree.
829 >>> t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
830
831 >>> # A recursive generator that generates Tree labels in in-order.
832 >>> def inorder(t):
833 ... if t:
834 ... for x in inorder(t.left):
835 ... yield x
836 ... yield t.label
837 ... for x in inorder(t.right):
838 ... yield x
839
840 >>> # Show it off: create a tree.
841 >>> t = tree("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
842 >>> # Print the nodes of the tree in in-order.
843 >>> for x in t:
844 ... print(' '+x, end='')
845 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
846
847 >>> # A non-recursive generator.
848 >>> def inorder(node):
849 ... stack = []
850 ... while node:
851 ... while node.left:
852 ... stack.append(node)
853 ... node = node.left
854 ... yield node.label
855 ... while not node.right:
856 ... try:
857 ... node = stack.pop()
858 ... except IndexError:
859 ... return
860 ... yield node.label
861 ... node = node.right
862
863 >>> # Exercise the non-recursive generator.
864 >>> for x in t:
865 ... print(' '+x, end='')
866 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
867
868 """
869
870 # Examples from Iterator-List and Python-Dev and c.l.py.
871
872 email_tests = """
873
874 The difference between yielding None and returning it.
875
876 >>> def g():
877 ... for i in range(3):
878 ... yield None
879 ... yield None
880 ... return
881 >>> list(g())
882 [None, None, None, None]
883
884 Ensure that explicitly raising StopIteration acts like any other exception
885 in try/except, not like a return.
886
887 >>> def g():
888 ... yield 1
889 ... try:
890 ... raise StopIteration
891 ... except:
892 ... yield 2
893 ... yield 3
894 >>> list(g())
895 [1, 2, 3]
896
897 Next one was posted to c.l.py.
898
899 >>> def gcomb(x, k):
900 ... "Generate all combinations of k elements from list x."
901 ...
902 ... if k > len(x):
903 ... return
904 ... if k == 0:
905 ... yield []
906 ... else:
907 ... first, rest = x[0], x[1:]
908 ... # A combination does or doesn't contain first.
909 ... # If it does, the remainder is a k-1 comb of rest.
910 ... for c in gcomb(rest, k-1):
911 ... c.insert(0, first)
912 ... yield c
913 ... # If it doesn't contain first, it's a k comb of rest.
914 ... for c in gcomb(rest, k):
915 ... yield c
916
917 >>> seq = list(range(1, 5))
918 >>> for k in range(len(seq) + 2):
919 ... print("%d-combs of %s:" % (k, seq))
920 ... for c in gcomb(seq, k):
921 ... print(" ", c)
922 0-combs of [1, 2, 3, 4]:
923 []
924 1-combs of [1, 2, 3, 4]:
925 [1]
926 [2]
927 [3]
928 [4]
929 2-combs of [1, 2, 3, 4]:
930 [1, 2]
931 [1, 3]
932 [1, 4]
933 [2, 3]
934 [2, 4]
935 [3, 4]
936 3-combs of [1, 2, 3, 4]:
937 [1, 2, 3]
938 [1, 2, 4]
939 [1, 3, 4]
940 [2, 3, 4]
941 4-combs of [1, 2, 3, 4]:
942 [1, 2, 3, 4]
943 5-combs of [1, 2, 3, 4]:
944
945 From the Iterators list, about the types of these things.
946
947 >>> def g():
948 ... yield 1
949 ...
950 >>> type(g)
951 <class 'function'>
952 >>> i = g()
953 >>> type(i)
954 <class 'generator'>
955 >>> [s for s in dir(i) if not s.startswith('_')]
956 ['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw']
957 >>> from test.support import HAVE_DOCSTRINGS
958 >>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
959 Implement next(self).
960 >>> iter(i) is i
961 True
962 >>> import types
963 >>> isinstance(i, types.GeneratorType)
964 True
965
966 And more, added later.
967
968 >>> i.gi_running
969 0
970 >>> type(i.gi_frame)
971 <class 'frame'>
972 >>> i.gi_running = 42
973 Traceback (most recent call last):
974 ...
975 AttributeError: attribute 'gi_running' of 'generator' objects is not writable
976 >>> def g():
977 ... yield me.gi_running
978 >>> me = g()
979 >>> me.gi_running
980 0
981 >>> next(me)
982 1
983 >>> me.gi_running
984 0
985
986 A clever union-find implementation from c.l.py, due to David Eppstein.
987 Sent: Friday, June 29, 2001 12:16 PM
988 To: python-list@python.org
989 Subject: Re: PEP 255: Simple Generators
990
991 >>> class disjointSet:
992 ... def __init__(self, name):
993 ... self.name = name
994 ... self.parent = None
995 ... self.generator = self.generate()
996 ...
997 ... def generate(self):
998 ... while not self.parent:
999 ... yield self
1000 ... for x in self.parent.generator:
1001 ... yield x
1002 ...
1003 ... def find(self):
1004 ... return next(self.generator)
1005 ...
1006 ... def union(self, parent):
1007 ... if self.parent:
1008 ... raise ValueError("Sorry, I'm not a root!")
1009 ... self.parent = parent
1010 ...
1011 ... def __str__(self):
1012 ... return self.name
1013
1014 >>> names = "ABCDEFGHIJKLM"
1015 >>> sets = [disjointSet(name) for name in names]
1016 >>> roots = sets[:]
1017
1018 >>> import random
1019 >>> gen = random.Random(42)
1020 >>> while 1:
1021 ... for s in sets:
1022 ... print(" %s->%s" % (s, s.find()), end='')
1023 ... print()
1024 ... if len(roots) > 1:
1025 ... s1 = gen.choice(roots)
1026 ... roots.remove(s1)
1027 ... s2 = gen.choice(roots)
1028 ... s1.union(s2)
1029 ... print("merged", s1, "into", s2)
1030 ... else:
1031 ... break
1032 A->A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->K L->L M->M
1033 merged K into B
1034 A->A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->B L->L M->M
1035 merged A into F
1036 A->F B->B C->C D->D E->E F->F G->G H->H I->I J->J K->B L->L M->M
1037 merged E into F
1038 A->F B->B C->C D->D E->F F->F G->G H->H I->I J->J K->B L->L M->M
1039 merged D into C
1040 A->F B->B C->C D->C E->F F->F G->G H->H I->I J->J K->B L->L M->M
1041 merged M into C
1042 A->F B->B C->C D->C E->F F->F G->G H->H I->I J->J K->B L->L M->C
1043 merged J into B
1044 A->F B->B C->C D->C E->F F->F G->G H->H I->I J->B K->B L->L M->C
1045 merged B into C
1046 A->F B->C C->C D->C E->F F->F G->G H->H I->I J->C K->C L->L M->C
1047 merged F into G
1048 A->G B->C C->C D->C E->G F->G G->G H->H I->I J->C K->C L->L M->C
1049 merged L into C
1050 A->G B->C C->C D->C E->G F->G G->G H->H I->I J->C K->C L->C M->C
1051 merged G into I
1052 A->I B->C C->C D->C E->I F->I G->I H->H I->I J->C K->C L->C M->C
1053 merged I into H
1054 A->H B->C C->C D->C E->H F->H G->H H->H I->H J->C K->C L->C M->C
1055 merged C into H
1056 A->H B->H C->H D->H E->H F->H G->H H->H I->H J->H K->H L->H M->H
1057
1058 """
1059 # Emacs turd '
1060
1061 # Fun tests (for sufficiently warped notions of "fun").
1062
1063 fun_tests = """
1064
1065 Build up to a recursive Sieve of Eratosthenes generator.
1066
1067 >>> def firstn(g, n):
1068 ... return [next(g) for i in range(n)]
1069
1070 >>> def intsfrom(i):
1071 ... while 1:
1072 ... yield i
1073 ... i += 1
1074
1075 >>> firstn(intsfrom(5), 7)
1076 [5, 6, 7, 8, 9, 10, 11]
1077
1078 >>> def exclude_multiples(n, ints):
1079 ... for i in ints:
1080 ... if i % n:
1081 ... yield i
1082
1083 >>> firstn(exclude_multiples(3, intsfrom(1)), 6)
1084 [1, 2, 4, 5, 7, 8]
1085
1086 >>> def sieve(ints):
1087 ... prime = next(ints)
1088 ... yield prime
1089 ... not_divisible_by_prime = exclude_multiples(prime, ints)
1090 ... for p in sieve(not_divisible_by_prime):
1091 ... yield p
1092
1093 >>> primes = sieve(intsfrom(2))
1094 >>> firstn(primes, 20)
1095 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
1096
1097
1098 Another famous problem: generate all integers of the form
1099 2**i * 3**j * 5**k
1100 in increasing order, where i,j,k >= 0. Trickier than it may look at first!
1101 Try writing it without generators, and correctly, and without generating
1102 3 internal results for each result output.
1103
1104 >>> def times(n, g):
1105 ... for i in g:
1106 ... yield n * i
1107 >>> firstn(times(10, intsfrom(1)), 10)
1108 [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
1109
1110 >>> def merge(g, h):
1111 ... ng = next(g)
1112 ... nh = next(h)
1113 ... while 1:
1114 ... if ng < nh:
1115 ... yield ng
1116 ... ng = next(g)
1117 ... elif ng > nh:
1118 ... yield nh
1119 ... nh = next(h)
1120 ... else:
1121 ... yield ng
1122 ... ng = next(g)
1123 ... nh = next(h)
1124
1125 The following works, but is doing a whale of a lot of redundant work --
1126 it's not clear how to get the internal uses of m235 to share a single
1127 generator. Note that me_times2 (etc) each need to see every element in the
1128 result sequence. So this is an example where lazy lists are more natural
1129 (you can look at the head of a lazy list any number of times).
1130
1131 >>> def m235():
1132 ... yield 1
1133 ... me_times2 = times(2, m235())
1134 ... me_times3 = times(3, m235())
1135 ... me_times5 = times(5, m235())
1136 ... for i in merge(merge(me_times2,
1137 ... me_times3),
1138 ... me_times5):
1139 ... yield i
1140
1141 Don't print "too many" of these -- the implementation above is extremely
1142 inefficient: each call of m235() leads to 3 recursive calls, and in
1143 turn each of those 3 more, and so on, and so on, until we've descended
1144 enough levels to satisfy the print stmts. Very odd: when I printed 5
1145 lines of results below, this managed to screw up Win98's malloc in "the
1146 usual" way, i.e. the heap grew over 4Mb so Win98 started fragmenting
1147 address space, and it *looked* like a very slow leak.
1148
1149 >>> result = m235()
1150 >>> for i in range(3):
1151 ... print(firstn(result, 15))
1152 [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]
1153 [25, 27, 30, 32, 36, 40, 45, 48, 50, 54, 60, 64, 72, 75, 80]
1154 [81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 162, 180, 192]
1155
1156 Heh. Here's one way to get a shared list, complete with an excruciating
1157 namespace renaming trick. The *pretty* part is that the times() and merge()
1158 functions can be reused as-is, because they only assume their stream
1159 arguments are iterable -- a LazyList is the same as a generator to times().
1160
1161 >>> class LazyList:
1162 ... def __init__(self, g):
1163 ... self.sofar = []
1164 ... self.fetch = g.__next__
1165 ...
1166 ... def __getitem__(self, i):
1167 ... sofar, fetch = self.sofar, self.fetch
1168 ... while i >= len(sofar):
1169 ... sofar.append(fetch())
1170 ... return sofar[i]
1171
1172 >>> def m235():
1173 ... yield 1
1174 ... # Gack: m235 below actually refers to a LazyList.
1175 ... me_times2 = times(2, m235)
1176 ... me_times3 = times(3, m235)
1177 ... me_times5 = times(5, m235)
1178 ... for i in merge(merge(me_times2,
1179 ... me_times3),
1180 ... me_times5):
1181 ... yield i
1182
1183 Print as many of these as you like -- *this* implementation is memory-
1184 efficient.
1185
1186 >>> m235 = LazyList(m235())
1187 >>> for i in range(5):
1188 ... print([m235[j] for j in range(15*i, 15*(i+1))])
1189 [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]
1190 [25, 27, 30, 32, 36, 40, 45, 48, 50, 54, 60, 64, 72, 75, 80]
1191 [81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 162, 180, 192]
1192 [200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375, 384]
1193 [400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675]
1194
1195 Ye olde Fibonacci generator, LazyList style.
1196
1197 >>> def fibgen(a, b):
1198 ...
1199 ... def sum(g, h):
1200 ... while 1:
1201 ... yield next(g) + next(h)
1202 ...
1203 ... def tail(g):
1204 ... next(g) # throw first away
1205 ... for x in g:
1206 ... yield x
1207 ...
1208 ... yield a
1209 ... yield b
1210 ... for s in sum(iter(fib),
1211 ... tail(iter(fib))):
1212 ... yield s
1213
1214 >>> fib = LazyList(fibgen(1, 2))
1215 >>> firstn(iter(fib), 17)
1216 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
1217
1218
1219 Running after your tail with itertools.tee (new in version 2.4)
1220
1221 The algorithms "m235" (Hamming) and Fibonacci presented above are both
1222 examples of a whole family of FP (functional programming) algorithms
1223 where a function produces and returns a list while the production algorithm
1224 suppose the list as already produced by recursively calling itself.
1225 For these algorithms to work, they must:
1226
1227 - produce at least a first element without presupposing the existence of
1228 the rest of the list
1229 - produce their elements in a lazy manner
1230
1231 To work efficiently, the beginning of the list must not be recomputed over
1232 and over again. This is ensured in most FP languages as a built-in feature.
1233 In python, we have to explicitly maintain a list of already computed results
1234 and abandon genuine recursivity.
1235
1236 This is what had been attempted above with the LazyList class. One problem
1237 with that class is that it keeps a list of all of the generated results and
1238 therefore continually grows. This partially defeats the goal of the generator
1239 concept, viz. produce the results only as needed instead of producing them
1240 all and thereby wasting memory.
1241
1242 Thanks to itertools.tee, it is now clear "how to get the internal uses of
1243 m235 to share a single generator".
1244
1245 >>> from itertools import tee
1246 >>> def m235():
1247 ... def _m235():
1248 ... yield 1
1249 ... for n in merge(times(2, m2),
1250 ... merge(times(3, m3),
1251 ... times(5, m5))):
1252 ... yield n
1253 ... m1 = _m235()
1254 ... m2, m3, m5, mRes = tee(m1, 4)
1255 ... return mRes
1256
1257 >>> it = m235()
1258 >>> for i in range(5):
1259 ... print(firstn(it, 15))
1260 [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]
1261 [25, 27, 30, 32, 36, 40, 45, 48, 50, 54, 60, 64, 72, 75, 80]
1262 [81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 162, 180, 192]
1263 [200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375, 384]
1264 [400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675]
1265
1266 The "tee" function does just what we want. It internally keeps a generated
1267 result for as long as it has not been "consumed" from all of the duplicated
1268 iterators, whereupon it is deleted. You can therefore print the hamming
1269 sequence during hours without increasing memory usage, or very little.
1270
1271 The beauty of it is that recursive running-after-their-tail FP algorithms
1272 are quite straightforwardly expressed with this Python idiom.
1273
1274 Ye olde Fibonacci generator, tee style.
1275
1276 >>> def fib():
1277 ...
1278 ... def _isum(g, h):
1279 ... while 1:
1280 ... yield next(g) + next(h)
1281 ...
1282 ... def _fib():
1283 ... yield 1
1284 ... yield 2
1285 ... next(fibTail) # throw first away
1286 ... for res in _isum(fibHead, fibTail):
1287 ... yield res
1288 ...
1289 ... realfib = _fib()
1290 ... fibHead, fibTail, fibRes = tee(realfib, 3)
1291 ... return fibRes
1292
1293 >>> firstn(fib(), 17)
1294 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
1295
1296 """
1297
1298 # syntax_tests mostly provokes SyntaxErrors. Also fiddling with #if 0
1299 # hackery.
1300
1301 syntax_tests = """
1302
1303 These are fine:
1304
1305 >>> def f():
1306 ... yield 1
1307 ... return
1308
1309 >>> def f():
1310 ... try:
1311 ... yield 1
1312 ... finally:
1313 ... pass
1314
1315 >>> def f():
1316 ... try:
1317 ... try:
1318 ... 1//0
1319 ... except ZeroDivisionError:
1320 ... yield 666
1321 ... except:
1322 ... pass
1323 ... finally:
1324 ... pass
1325
1326 >>> def f():
1327 ... try:
1328 ... try:
1329 ... yield 12
1330 ... 1//0
1331 ... except ZeroDivisionError:
1332 ... yield 666
1333 ... except:
1334 ... try:
1335 ... x = 12
1336 ... finally:
1337 ... yield 12
1338 ... except:
1339 ... return
1340 >>> list(f())
1341 [12, 666]
1342
1343 >>> def f():
1344 ... yield
1345 >>> type(f())
1346 <class 'generator'>
1347
1348
1349 >>> def f():
1350 ... if 0:
1351 ... yield
1352 >>> type(f())
1353 <class 'generator'>
1354
1355
1356 >>> def f():
1357 ... if 0:
1358 ... yield 1
1359 >>> type(f())
1360 <class 'generator'>
1361
1362 >>> def f():
1363 ... if "":
1364 ... yield None
1365 >>> type(f())
1366 <class 'generator'>
1367
1368 >>> def f():
1369 ... return
1370 ... try:
1371 ... if x==4:
1372 ... pass
1373 ... elif 0:
1374 ... try:
1375 ... 1//0
1376 ... except SyntaxError:
1377 ... pass
1378 ... else:
1379 ... if 0:
1380 ... while 12:
1381 ... x += 1
1382 ... yield 2 # don't blink
1383 ... f(a, b, c, d, e)
1384 ... else:
1385 ... pass
1386 ... except:
1387 ... x = 1
1388 ... return
1389 >>> type(f())
1390 <class 'generator'>
1391
1392 >>> def f():
1393 ... if 0:
1394 ... def g():
1395 ... yield 1
1396 ...
1397 >>> type(f())
1398 <class 'NoneType'>
1399
1400 >>> def f():
1401 ... if 0:
1402 ... class C:
1403 ... def __init__(self):
1404 ... yield 1
1405 ... def f(self):
1406 ... yield 2
1407 >>> type(f())
1408 <class 'NoneType'>
1409
1410 >>> def f():
1411 ... if 0:
1412 ... return
1413 ... if 0:
1414 ... yield 2
1415 >>> type(f())
1416 <class 'generator'>
1417
1418 This one caused a crash (see SF bug 567538):
1419
1420 >>> def f():
1421 ... for i in range(3):
1422 ... try:
1423 ... continue
1424 ... finally:
1425 ... yield i
1426 ...
1427 >>> g = f()
1428 >>> print(next(g))
1429 0
1430 >>> print(next(g))
1431 1
1432 >>> print(next(g))
1433 2
1434 >>> print(next(g))
1435 Traceback (most recent call last):
1436 StopIteration
1437
1438
1439 Test the gi_code attribute
1440
1441 >>> def f():
1442 ... yield 5
1443 ...
1444 >>> g = f()
1445 >>> g.gi_code is f.__code__
1446 True
1447 >>> next(g)
1448 5
1449 >>> next(g)
1450 Traceback (most recent call last):
1451 StopIteration
1452 >>> g.gi_code is f.__code__
1453 True
1454
1455
1456 Test the __name__ attribute and the repr()
1457
1458 >>> def f():
1459 ... yield 5
1460 ...
1461 >>> g = f()
1462 >>> g.__name__
1463 'f'
1464 >>> repr(g) # doctest: +ELLIPSIS
1465 '<generator object f at ...>'
1466
1467 Lambdas shouldn't have their usual return behavior.
1468
1469 >>> x = lambda: (yield 1)
1470 >>> list(x())
1471 [1]
1472
1473 >>> x = lambda: ((yield 1), (yield 2))
1474 >>> list(x())
1475 [1, 2]
1476 """
1477
1478 # conjoin is a simple backtracking generator, named in honor of Icon's
1479 # "conjunction" control structure. Pass a list of no-argument functions
1480 # that return iterable objects. Easiest to explain by example: assume the
1481 # function list [x, y, z] is passed. Then conjoin acts like:
1482 #
1483 # def g():
1484 # values = [None] * 3
1485 # for values[0] in x():
1486 # for values[1] in y():
1487 # for values[2] in z():
1488 # yield values
1489 #
1490 # So some 3-lists of values *may* be generated, each time we successfully
1491 # get into the innermost loop. If an iterator fails (is exhausted) before
1492 # then, it "backtracks" to get the next value from the nearest enclosing
1493 # iterator (the one "to the left"), and starts all over again at the next
1494 # slot (pumps a fresh iterator). Of course this is most useful when the
1495 # iterators have side-effects, so that which values *can* be generated at
1496 # each slot depend on the values iterated at previous slots.
1497
1498 def simple_conjoin(gs):
1499
1500 values = [None] * len(gs)
1501
1502 def gen(i):
1503 if i >= len(gs):
1504 yield values
1505 else:
1506 for values[i] in gs[i]():
1507 for x in gen(i+1):
1508 yield x
1509
1510 for x in gen(0):
1511 yield x
1512
1513 # That works fine, but recursing a level and checking i against len(gs) for
1514 # each item produced is inefficient. By doing manual loop unrolling across
1515 # generator boundaries, it's possible to eliminate most of that overhead.
1516 # This isn't worth the bother *in general* for generators, but conjoin() is
1517 # a core building block for some CPU-intensive generator applications.
1518
1519 def conjoin(gs):
1520
1521 n = len(gs)
1522 values = [None] * n
1523
1524 # Do one loop nest at time recursively, until the # of loop nests
1525 # remaining is divisible by 3.
1526
1527 def gen(i):
1528 if i >= n:
1529 yield values
1530
1531 elif (n-i) % 3:
1532 ip1 = i+1
1533 for values[i] in gs[i]():
1534 for x in gen(ip1):
1535 yield x
1536
1537 else:
1538 for x in _gen3(i):
1539 yield x
1540
1541 # Do three loop nests at a time, recursing only if at least three more
1542 # remain. Don't call directly: this is an internal optimization for
1543 # gen's use.
1544
1545 def _gen3(i):
1546 assert i < n and (n-i) % 3 == 0
1547 ip1, ip2, ip3 = i+1, i+2, i+3
1548 g, g1, g2 = gs[i : ip3]
1549
1550 if ip3 >= n:
1551 # These are the last three, so we can yield values directly.
1552 for values[i] in g():
1553 for values[ip1] in g1():
1554 for values[ip2] in g2():
1555 yield values
1556
1557 else:
1558 # At least 6 loop nests remain; peel off 3 and recurse for the
1559 # rest.
1560 for values[i] in g():
1561 for values[ip1] in g1():
1562 for values[ip2] in g2():
1563 for x in _gen3(ip3):
1564 yield x
1565
1566 for x in gen(0):
1567 yield x
1568
1569 # And one more approach: For backtracking apps like the Knight's Tour
1570 # solver below, the number of backtracking levels can be enormous (one
1571 # level per square, for the Knight's Tour, so that e.g. a 100x100 board
1572 # needs 10,000 levels). In such cases Python is likely to run out of
1573 # stack space due to recursion. So here's a recursion-free version of
1574 # conjoin too.
1575 # NOTE WELL: This allows large problems to be solved with only trivial
1576 # demands on stack space. Without explicitly resumable generators, this is
1577 # much harder to achieve. OTOH, this is much slower (up to a factor of 2)
1578 # than the fancy unrolled recursive conjoin.
1579
1580 def flat_conjoin(gs): # rename to conjoin to run tests with this instead
1581 n = len(gs)
1582 values = [None] * n
1583 iters = [None] * n
1584 _StopIteration = StopIteration # make local because caught a *lot*
1585 i = 0
1586 while 1:
1587 # Descend.
1588 try:
1589 while i < n:
1590 it = iters[i] = gs[i]().__next__
1591 values[i] = it()
1592 i += 1
1593 except _StopIteration:
1594 pass
1595 else:
1596 assert i == n
1597 yield values
1598
1599 # Backtrack until an older iterator can be resumed.
1600 i -= 1
1601 while i >= 0:
1602 try:
1603 values[i] = iters[i]()
1604 # Success! Start fresh at next level.
1605 i += 1
1606 break
1607 except _StopIteration:
1608 # Continue backtracking.
1609 i -= 1
1610 else:
1611 assert i < 0
1612 break
1613
1614 # A conjoin-based N-Queens solver.
1615
1616 class ESC[4;38;5;81mQueens:
1617 def __init__(self, n):
1618 self.n = n
1619 rangen = range(n)
1620
1621 # Assign a unique int to each column and diagonal.
1622 # columns: n of those, range(n).
1623 # NW-SE diagonals: 2n-1 of these, i-j unique and invariant along
1624 # each, smallest i-j is 0-(n-1) = 1-n, so add n-1 to shift to 0-
1625 # based.
1626 # NE-SW diagonals: 2n-1 of these, i+j unique and invariant along
1627 # each, smallest i+j is 0, largest is 2n-2.
1628
1629 # For each square, compute a bit vector of the columns and
1630 # diagonals it covers, and for each row compute a function that
1631 # generates the possibilities for the columns in that row.
1632 self.rowgenerators = []
1633 for i in rangen:
1634 rowuses = [(1 << j) | # column ordinal
1635 (1 << (n + i-j + n-1)) | # NW-SE ordinal
1636 (1 << (n + 2*n-1 + i+j)) # NE-SW ordinal
1637 for j in rangen]
1638
1639 def rowgen(rowuses=rowuses):
1640 for j in rangen:
1641 uses = rowuses[j]
1642 if uses & self.used == 0:
1643 self.used |= uses
1644 yield j
1645 self.used &= ~uses
1646
1647 self.rowgenerators.append(rowgen)
1648
1649 # Generate solutions.
1650 def solve(self):
1651 self.used = 0
1652 for row2col in conjoin(self.rowgenerators):
1653 yield row2col
1654
1655 def printsolution(self, row2col):
1656 n = self.n
1657 assert n == len(row2col)
1658 sep = "+" + "-+" * n
1659 print(sep)
1660 for i in range(n):
1661 squares = [" " for j in range(n)]
1662 squares[row2col[i]] = "Q"
1663 print("|" + "|".join(squares) + "|")
1664 print(sep)
1665
1666 # A conjoin-based Knight's Tour solver. This is pretty sophisticated
1667 # (e.g., when used with flat_conjoin above, and passing hard=1 to the
1668 # constructor, a 200x200 Knight's Tour was found quickly -- note that we're
1669 # creating 10s of thousands of generators then!), and is lengthy.
1670
1671 class ESC[4;38;5;81mKnights:
1672 def __init__(self, m, n, hard=0):
1673 self.m, self.n = m, n
1674
1675 # solve() will set up succs[i] to be a list of square #i's
1676 # successors.
1677 succs = self.succs = []
1678
1679 # Remove i0 from each of its successor's successor lists, i.e.
1680 # successors can't go back to i0 again. Return 0 if we can
1681 # detect this makes a solution impossible, else return 1.
1682
1683 def remove_from_successors(i0, len=len):
1684 # If we remove all exits from a free square, we're dead:
1685 # even if we move to it next, we can't leave it again.
1686 # If we create a square with one exit, we must visit it next;
1687 # else somebody else will have to visit it, and since there's
1688 # only one adjacent, there won't be a way to leave it again.
1689 # Finally, if we create more than one free square with a
1690 # single exit, we can only move to one of them next, leaving
1691 # the other one a dead end.
1692 ne0 = ne1 = 0
1693 for i in succs[i0]:
1694 s = succs[i]
1695 s.remove(i0)
1696 e = len(s)
1697 if e == 0:
1698 ne0 += 1
1699 elif e == 1:
1700 ne1 += 1
1701 return ne0 == 0 and ne1 < 2
1702
1703 # Put i0 back in each of its successor's successor lists.
1704
1705 def add_to_successors(i0):
1706 for i in succs[i0]:
1707 succs[i].append(i0)
1708
1709 # Generate the first move.
1710 def first():
1711 if m < 1 or n < 1:
1712 return
1713
1714 # Since we're looking for a cycle, it doesn't matter where we
1715 # start. Starting in a corner makes the 2nd move easy.
1716 corner = self.coords2index(0, 0)
1717 remove_from_successors(corner)
1718 self.lastij = corner
1719 yield corner
1720 add_to_successors(corner)
1721
1722 # Generate the second moves.
1723 def second():
1724 corner = self.coords2index(0, 0)
1725 assert self.lastij == corner # i.e., we started in the corner
1726 if m < 3 or n < 3:
1727 return
1728 assert len(succs[corner]) == 2
1729 assert self.coords2index(1, 2) in succs[corner]
1730 assert self.coords2index(2, 1) in succs[corner]
1731 # Only two choices. Whichever we pick, the other must be the
1732 # square picked on move m*n, as it's the only way to get back
1733 # to (0, 0). Save its index in self.final so that moves before
1734 # the last know it must be kept free.
1735 for i, j in (1, 2), (2, 1):
1736 this = self.coords2index(i, j)
1737 final = self.coords2index(3-i, 3-j)
1738 self.final = final
1739
1740 remove_from_successors(this)
1741 succs[final].append(corner)
1742 self.lastij = this
1743 yield this
1744 succs[final].remove(corner)
1745 add_to_successors(this)
1746
1747 # Generate moves 3 through m*n-1.
1748 def advance(len=len):
1749 # If some successor has only one exit, must take it.
1750 # Else favor successors with fewer exits.
1751 candidates = []
1752 for i in succs[self.lastij]:
1753 e = len(succs[i])
1754 assert e > 0, "else remove_from_successors() pruning flawed"
1755 if e == 1:
1756 candidates = [(e, i)]
1757 break
1758 candidates.append((e, i))
1759 else:
1760 candidates.sort()
1761
1762 for e, i in candidates:
1763 if i != self.final:
1764 if remove_from_successors(i):
1765 self.lastij = i
1766 yield i
1767 add_to_successors(i)
1768
1769 # Generate moves 3 through m*n-1. Alternative version using a
1770 # stronger (but more expensive) heuristic to order successors.
1771 # Since the # of backtracking levels is m*n, a poor move early on
1772 # can take eons to undo. Smallest square board for which this
1773 # matters a lot is 52x52.
1774 def advance_hard(vmid=(m-1)/2.0, hmid=(n-1)/2.0, len=len):
1775 # If some successor has only one exit, must take it.
1776 # Else favor successors with fewer exits.
1777 # Break ties via max distance from board centerpoint (favor
1778 # corners and edges whenever possible).
1779 candidates = []
1780 for i in succs[self.lastij]:
1781 e = len(succs[i])
1782 assert e > 0, "else remove_from_successors() pruning flawed"
1783 if e == 1:
1784 candidates = [(e, 0, i)]
1785 break
1786 i1, j1 = self.index2coords(i)
1787 d = (i1 - vmid)**2 + (j1 - hmid)**2
1788 candidates.append((e, -d, i))
1789 else:
1790 candidates.sort()
1791
1792 for e, d, i in candidates:
1793 if i != self.final:
1794 if remove_from_successors(i):
1795 self.lastij = i
1796 yield i
1797 add_to_successors(i)
1798
1799 # Generate the last move.
1800 def last():
1801 assert self.final in succs[self.lastij]
1802 yield self.final
1803
1804 if m*n < 4:
1805 self.squaregenerators = [first]
1806 else:
1807 self.squaregenerators = [first, second] + \
1808 [hard and advance_hard or advance] * (m*n - 3) + \
1809 [last]
1810
1811 def coords2index(self, i, j):
1812 assert 0 <= i < self.m
1813 assert 0 <= j < self.n
1814 return i * self.n + j
1815
1816 def index2coords(self, index):
1817 assert 0 <= index < self.m * self.n
1818 return divmod(index, self.n)
1819
1820 def _init_board(self):
1821 succs = self.succs
1822 del succs[:]
1823 m, n = self.m, self.n
1824 c2i = self.coords2index
1825
1826 offsets = [( 1, 2), ( 2, 1), ( 2, -1), ( 1, -2),
1827 (-1, -2), (-2, -1), (-2, 1), (-1, 2)]
1828 rangen = range(n)
1829 for i in range(m):
1830 for j in rangen:
1831 s = [c2i(i+io, j+jo) for io, jo in offsets
1832 if 0 <= i+io < m and
1833 0 <= j+jo < n]
1834 succs.append(s)
1835
1836 # Generate solutions.
1837 def solve(self):
1838 self._init_board()
1839 for x in conjoin(self.squaregenerators):
1840 yield x
1841
1842 def printsolution(self, x):
1843 m, n = self.m, self.n
1844 assert len(x) == m*n
1845 w = len(str(m*n))
1846 format = "%" + str(w) + "d"
1847
1848 squares = [[None] * n for i in range(m)]
1849 k = 1
1850 for i in x:
1851 i1, j1 = self.index2coords(i)
1852 squares[i1][j1] = format % k
1853 k += 1
1854
1855 sep = "+" + ("-" * w + "+") * n
1856 print(sep)
1857 for i in range(m):
1858 row = squares[i]
1859 print("|" + "|".join(row) + "|")
1860 print(sep)
1861
1862 conjoin_tests = """
1863
1864 Generate the 3-bit binary numbers in order. This illustrates dumbest-
1865 possible use of conjoin, just to generate the full cross-product.
1866
1867 >>> for c in conjoin([lambda: iter((0, 1))] * 3):
1868 ... print(c)
1869 [0, 0, 0]
1870 [0, 0, 1]
1871 [0, 1, 0]
1872 [0, 1, 1]
1873 [1, 0, 0]
1874 [1, 0, 1]
1875 [1, 1, 0]
1876 [1, 1, 1]
1877
1878 For efficiency in typical backtracking apps, conjoin() yields the same list
1879 object each time. So if you want to save away a full account of its
1880 generated sequence, you need to copy its results.
1881
1882 >>> def gencopy(iterator):
1883 ... for x in iterator:
1884 ... yield x[:]
1885
1886 >>> for n in range(10):
1887 ... all = list(gencopy(conjoin([lambda: iter((0, 1))] * n)))
1888 ... print(n, len(all), all[0] == [0] * n, all[-1] == [1] * n)
1889 0 1 True True
1890 1 2 True True
1891 2 4 True True
1892 3 8 True True
1893 4 16 True True
1894 5 32 True True
1895 6 64 True True
1896 7 128 True True
1897 8 256 True True
1898 9 512 True True
1899
1900 And run an 8-queens solver.
1901
1902 >>> q = Queens(8)
1903 >>> LIMIT = 2
1904 >>> count = 0
1905 >>> for row2col in q.solve():
1906 ... count += 1
1907 ... if count <= LIMIT:
1908 ... print("Solution", count)
1909 ... q.printsolution(row2col)
1910 Solution 1
1911 +-+-+-+-+-+-+-+-+
1912 |Q| | | | | | | |
1913 +-+-+-+-+-+-+-+-+
1914 | | | | |Q| | | |
1915 +-+-+-+-+-+-+-+-+
1916 | | | | | | | |Q|
1917 +-+-+-+-+-+-+-+-+
1918 | | | | | |Q| | |
1919 +-+-+-+-+-+-+-+-+
1920 | | |Q| | | | | |
1921 +-+-+-+-+-+-+-+-+
1922 | | | | | | |Q| |
1923 +-+-+-+-+-+-+-+-+
1924 | |Q| | | | | | |
1925 +-+-+-+-+-+-+-+-+
1926 | | | |Q| | | | |
1927 +-+-+-+-+-+-+-+-+
1928 Solution 2
1929 +-+-+-+-+-+-+-+-+
1930 |Q| | | | | | | |
1931 +-+-+-+-+-+-+-+-+
1932 | | | | | |Q| | |
1933 +-+-+-+-+-+-+-+-+
1934 | | | | | | | |Q|
1935 +-+-+-+-+-+-+-+-+
1936 | | |Q| | | | | |
1937 +-+-+-+-+-+-+-+-+
1938 | | | | | | |Q| |
1939 +-+-+-+-+-+-+-+-+
1940 | | | |Q| | | | |
1941 +-+-+-+-+-+-+-+-+
1942 | |Q| | | | | | |
1943 +-+-+-+-+-+-+-+-+
1944 | | | | |Q| | | |
1945 +-+-+-+-+-+-+-+-+
1946
1947 >>> print(count, "solutions in all.")
1948 92 solutions in all.
1949
1950 And run a Knight's Tour on a 10x10 board. Note that there are about
1951 20,000 solutions even on a 6x6 board, so don't dare run this to exhaustion.
1952
1953 >>> k = Knights(10, 10)
1954 >>> LIMIT = 2
1955 >>> count = 0
1956 >>> for x in k.solve():
1957 ... count += 1
1958 ... if count <= LIMIT:
1959 ... print("Solution", count)
1960 ... k.printsolution(x)
1961 ... else:
1962 ... break
1963 Solution 1
1964 +---+---+---+---+---+---+---+---+---+---+
1965 | 1| 58| 27| 34| 3| 40| 29| 10| 5| 8|
1966 +---+---+---+---+---+---+---+---+---+---+
1967 | 26| 35| 2| 57| 28| 33| 4| 7| 30| 11|
1968 +---+---+---+---+---+---+---+---+---+---+
1969 | 59|100| 73| 36| 41| 56| 39| 32| 9| 6|
1970 +---+---+---+---+---+---+---+---+---+---+
1971 | 74| 25| 60| 55| 72| 37| 42| 49| 12| 31|
1972 +---+---+---+---+---+---+---+---+---+---+
1973 | 61| 86| 99| 76| 63| 52| 47| 38| 43| 50|
1974 +---+---+---+---+---+---+---+---+---+---+
1975 | 24| 75| 62| 85| 54| 71| 64| 51| 48| 13|
1976 +---+---+---+---+---+---+---+---+---+---+
1977 | 87| 98| 91| 80| 77| 84| 53| 46| 65| 44|
1978 +---+---+---+---+---+---+---+---+---+---+
1979 | 90| 23| 88| 95| 70| 79| 68| 83| 14| 17|
1980 +---+---+---+---+---+---+---+---+---+---+
1981 | 97| 92| 21| 78| 81| 94| 19| 16| 45| 66|
1982 +---+---+---+---+---+---+---+---+---+---+
1983 | 22| 89| 96| 93| 20| 69| 82| 67| 18| 15|
1984 +---+---+---+---+---+---+---+---+---+---+
1985 Solution 2
1986 +---+---+---+---+---+---+---+---+---+---+
1987 | 1| 58| 27| 34| 3| 40| 29| 10| 5| 8|
1988 +---+---+---+---+---+---+---+---+---+---+
1989 | 26| 35| 2| 57| 28| 33| 4| 7| 30| 11|
1990 +---+---+---+---+---+---+---+---+---+---+
1991 | 59|100| 73| 36| 41| 56| 39| 32| 9| 6|
1992 +---+---+---+---+---+---+---+---+---+---+
1993 | 74| 25| 60| 55| 72| 37| 42| 49| 12| 31|
1994 +---+---+---+---+---+---+---+---+---+---+
1995 | 61| 86| 99| 76| 63| 52| 47| 38| 43| 50|
1996 +---+---+---+---+---+---+---+---+---+---+
1997 | 24| 75| 62| 85| 54| 71| 64| 51| 48| 13|
1998 +---+---+---+---+---+---+---+---+---+---+
1999 | 87| 98| 89| 80| 77| 84| 53| 46| 65| 44|
2000 +---+---+---+---+---+---+---+---+---+---+
2001 | 90| 23| 92| 95| 70| 79| 68| 83| 14| 17|
2002 +---+---+---+---+---+---+---+---+---+---+
2003 | 97| 88| 21| 78| 81| 94| 19| 16| 45| 66|
2004 +---+---+---+---+---+---+---+---+---+---+
2005 | 22| 91| 96| 93| 20| 69| 82| 67| 18| 15|
2006 +---+---+---+---+---+---+---+---+---+---+
2007 """
2008
2009 weakref_tests = """\
2010 Generators are weakly referencable:
2011
2012 >>> import weakref
2013 >>> def gen():
2014 ... yield 'foo!'
2015 ...
2016 >>> wr = weakref.ref(gen)
2017 >>> wr() is gen
2018 True
2019 >>> p = weakref.proxy(gen)
2020
2021 Generator-iterators are weakly referencable as well:
2022
2023 >>> gi = gen()
2024 >>> wr = weakref.ref(gi)
2025 >>> wr() is gi
2026 True
2027 >>> p = weakref.proxy(gi)
2028 >>> list(p)
2029 ['foo!']
2030
2031 """
2032
2033 coroutine_tests = """\
2034 >>> from test.support import gc_collect
2035
2036 Sending a value into a started generator:
2037
2038 >>> def f():
2039 ... print((yield 1))
2040 ... yield 2
2041 >>> g = f()
2042 >>> next(g)
2043 1
2044 >>> g.send(42)
2045 42
2046 2
2047
2048 Sending a value into a new generator produces a TypeError:
2049
2050 >>> f().send("foo")
2051 Traceback (most recent call last):
2052 ...
2053 TypeError: can't send non-None value to a just-started generator
2054
2055
2056 Yield by itself yields None:
2057
2058 >>> def f(): yield
2059 >>> list(f())
2060 [None]
2061
2062
2063 Yield is allowed only in the outermost iterable in generator expression:
2064
2065 >>> def f(): list(i for i in [(yield 26)])
2066 >>> type(f())
2067 <class 'generator'>
2068
2069
2070 A yield expression with augmented assignment.
2071
2072 >>> def coroutine(seq):
2073 ... count = 0
2074 ... while count < 200:
2075 ... count += yield
2076 ... seq.append(count)
2077 >>> seq = []
2078 >>> c = coroutine(seq)
2079 >>> next(c)
2080 >>> print(seq)
2081 []
2082 >>> c.send(10)
2083 >>> print(seq)
2084 [10]
2085 >>> c.send(10)
2086 >>> print(seq)
2087 [10, 20]
2088 >>> c.send(10)
2089 >>> print(seq)
2090 [10, 20, 30]
2091
2092
2093 Check some syntax errors for yield expressions:
2094
2095 >>> f=lambda: (yield 1),(yield 2)
2096 Traceback (most recent call last):
2097 ...
2098 SyntaxError: 'yield' outside function
2099
2100 >>> def f(): x = yield = y
2101 Traceback (most recent call last):
2102 ...
2103 SyntaxError: assignment to yield expression not possible
2104
2105 >>> def f(): (yield bar) = y
2106 Traceback (most recent call last):
2107 ...
2108 SyntaxError: cannot assign to yield expression here. Maybe you meant '==' instead of '='?
2109
2110 >>> def f(): (yield bar) += y
2111 Traceback (most recent call last):
2112 ...
2113 SyntaxError: 'yield expression' is an illegal expression for augmented assignment
2114
2115
2116 Now check some throw() conditions:
2117
2118 >>> def f():
2119 ... while True:
2120 ... try:
2121 ... print((yield))
2122 ... except ValueError as v:
2123 ... print("caught ValueError (%s)" % (v))
2124 >>> import sys
2125 >>> g = f()
2126 >>> next(g)
2127
2128 >>> g.throw(ValueError) # type only
2129 caught ValueError ()
2130
2131 >>> g.throw(ValueError("xyz")) # value only
2132 caught ValueError (xyz)
2133
2134 >>> g.throw(ValueError, ValueError(1)) # value+matching type
2135 caught ValueError (1)
2136
2137 >>> g.throw(ValueError, TypeError(1)) # mismatched type, rewrapped
2138 caught ValueError (1)
2139
2140 >>> g.throw(ValueError, ValueError(1), None) # explicit None traceback
2141 caught ValueError (1)
2142
2143 >>> g.throw(ValueError(1), "foo") # bad args
2144 Traceback (most recent call last):
2145 ...
2146 TypeError: instance exception may not have a separate value
2147
2148 >>> g.throw(ValueError, "foo", 23) # bad args
2149 Traceback (most recent call last):
2150 ...
2151 TypeError: throw() third argument must be a traceback object
2152
2153 >>> g.throw("abc")
2154 Traceback (most recent call last):
2155 ...
2156 TypeError: exceptions must be classes or instances deriving from BaseException, not str
2157
2158 >>> g.throw(0)
2159 Traceback (most recent call last):
2160 ...
2161 TypeError: exceptions must be classes or instances deriving from BaseException, not int
2162
2163 >>> g.throw(list)
2164 Traceback (most recent call last):
2165 ...
2166 TypeError: exceptions must be classes or instances deriving from BaseException, not type
2167
2168 >>> def throw(g,exc):
2169 ... try:
2170 ... raise exc
2171 ... except:
2172 ... g.throw(*sys.exc_info())
2173 >>> throw(g,ValueError) # do it with traceback included
2174 caught ValueError ()
2175
2176 >>> g.send(1)
2177 1
2178
2179 >>> throw(g,TypeError) # terminate the generator
2180 Traceback (most recent call last):
2181 ...
2182 TypeError
2183
2184 >>> print(g.gi_frame)
2185 None
2186
2187 >>> g.send(2)
2188 Traceback (most recent call last):
2189 ...
2190 StopIteration
2191
2192 >>> g.throw(ValueError,6) # throw on closed generator
2193 Traceback (most recent call last):
2194 ...
2195 ValueError: 6
2196
2197 >>> f().throw(ValueError,7) # throw on just-opened generator
2198 Traceback (most recent call last):
2199 ...
2200 ValueError: 7
2201
2202 Plain "raise" inside a generator should preserve the traceback (#13188).
2203 The traceback should have 3 levels:
2204 - g.throw()
2205 - f()
2206 - 1/0
2207
2208 >>> def f():
2209 ... try:
2210 ... yield
2211 ... except:
2212 ... raise
2213 >>> g = f()
2214 >>> try:
2215 ... 1/0
2216 ... except ZeroDivisionError as v:
2217 ... try:
2218 ... g.throw(v)
2219 ... except Exception as w:
2220 ... tb = w.__traceback__
2221 >>> levels = 0
2222 >>> while tb:
2223 ... levels += 1
2224 ... tb = tb.tb_next
2225 >>> levels
2226 3
2227
2228 Now let's try closing a generator:
2229
2230 >>> def f():
2231 ... try: yield
2232 ... except GeneratorExit:
2233 ... print("exiting")
2234
2235 >>> g = f()
2236 >>> next(g)
2237 >>> g.close()
2238 exiting
2239 >>> g.close() # should be no-op now
2240
2241 >>> f().close() # close on just-opened generator should be fine
2242
2243 >>> def f(): yield # an even simpler generator
2244 >>> f().close() # close before opening
2245 >>> g = f()
2246 >>> next(g)
2247 >>> g.close() # close normally
2248
2249 And finalization:
2250
2251 >>> def f():
2252 ... try: yield
2253 ... finally:
2254 ... print("exiting")
2255
2256 >>> g = f()
2257 >>> next(g)
2258 >>> del g; gc_collect() # For PyPy or other GCs.
2259 exiting
2260
2261
2262 GeneratorExit is not caught by except Exception:
2263
2264 >>> def f():
2265 ... try: yield
2266 ... except Exception:
2267 ... print('except')
2268 ... finally:
2269 ... print('finally')
2270
2271 >>> g = f()
2272 >>> next(g)
2273 >>> del g; gc_collect() # For PyPy or other GCs.
2274 finally
2275
2276
2277 Now let's try some ill-behaved generators:
2278
2279 >>> def f():
2280 ... try: yield
2281 ... except GeneratorExit:
2282 ... yield "foo!"
2283 >>> g = f()
2284 >>> next(g)
2285 >>> g.close()
2286 Traceback (most recent call last):
2287 ...
2288 RuntimeError: generator ignored GeneratorExit
2289 >>> g.close()
2290
2291
2292 Our ill-behaved code should be invoked during GC:
2293
2294 >>> with support.catch_unraisable_exception() as cm:
2295 ... g = f()
2296 ... next(g)
2297 ... del g
2298 ...
2299 ... cm.unraisable.exc_type == RuntimeError
2300 ... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value)
2301 ... cm.unraisable.exc_traceback is not None
2302 True
2303 True
2304 True
2305
2306 And errors thrown during closing should propagate:
2307
2308 >>> def f():
2309 ... try: yield
2310 ... except GeneratorExit:
2311 ... raise TypeError("fie!")
2312 >>> g = f()
2313 >>> next(g)
2314 >>> g.close()
2315 Traceback (most recent call last):
2316 ...
2317 TypeError: fie!
2318
2319
2320 Ensure that various yield expression constructs make their
2321 enclosing function a generator:
2322
2323 >>> def f(): x += yield
2324 >>> type(f())
2325 <class 'generator'>
2326
2327 >>> def f(): x = yield
2328 >>> type(f())
2329 <class 'generator'>
2330
2331 >>> def f(): lambda x=(yield): 1
2332 >>> type(f())
2333 <class 'generator'>
2334
2335 >>> def f(d): d[(yield "a")] = d[(yield "b")] = 27
2336 >>> data = [1,2]
2337 >>> g = f(data)
2338 >>> type(g)
2339 <class 'generator'>
2340 >>> g.send(None)
2341 'a'
2342 >>> data
2343 [1, 2]
2344 >>> g.send(0)
2345 'b'
2346 >>> data
2347 [27, 2]
2348 >>> try: g.send(1)
2349 ... except StopIteration: pass
2350 >>> data
2351 [27, 27]
2352
2353 """
2354
2355 refleaks_tests = """
2356 Prior to adding cycle-GC support to itertools.tee, this code would leak
2357 references. We add it to the standard suite so the routine refleak-tests
2358 would trigger if it starts being uncleanable again.
2359
2360 >>> import itertools
2361 >>> def leak():
2362 ... class gen:
2363 ... def __iter__(self):
2364 ... return self
2365 ... def __next__(self):
2366 ... return self.item
2367 ... g = gen()
2368 ... head, tail = itertools.tee(g)
2369 ... g.item = head
2370 ... return head
2371 >>> it = leak()
2372
2373 Make sure to also test the involvement of the tee-internal teedataobject,
2374 which stores returned items.
2375
2376 >>> item = next(it)
2377
2378
2379
2380 This test leaked at one point due to generator finalization/destruction.
2381 It was copied from Lib/test/leakers/test_generator_cycle.py before the file
2382 was removed.
2383
2384 >>> def leak():
2385 ... def gen():
2386 ... while True:
2387 ... yield g
2388 ... g = gen()
2389
2390 >>> leak()
2391
2392
2393
2394 This test isn't really generator related, but rather exception-in-cleanup
2395 related. The coroutine tests (above) just happen to cause an exception in
2396 the generator's __del__ (tp_del) method. We can also test for this
2397 explicitly, without generators. We do have to redirect stderr to avoid
2398 printing warnings and to doublecheck that we actually tested what we wanted
2399 to test.
2400
2401 >>> from test import support
2402 >>> class Leaker:
2403 ... def __del__(self):
2404 ... def invoke(message):
2405 ... raise RuntimeError(message)
2406 ... invoke("del failed")
2407 ...
2408 >>> with support.catch_unraisable_exception() as cm:
2409 ... l = Leaker()
2410 ... del l
2411 ...
2412 ... cm.unraisable.object == Leaker.__del__
2413 ... cm.unraisable.exc_type == RuntimeError
2414 ... str(cm.unraisable.exc_value) == "del failed"
2415 ... cm.unraisable.exc_traceback is not None
2416 True
2417 True
2418 True
2419 True
2420
2421
2422 These refleak tests should perhaps be in a testfile of their own,
2423 test_generators just happened to be the test that drew these out.
2424
2425 """
2426
2427 __test__ = {"tut": tutorial_tests,
2428 "pep": pep_tests,
2429 "email": email_tests,
2430 "fun": fun_tests,
2431 "syntax": syntax_tests,
2432 "conjoin": conjoin_tests,
2433 "weakref": weakref_tests,
2434 "coroutine": coroutine_tests,
2435 "refleaks": refleaks_tests,
2436 }
2437
2438 def load_tests(loader, tests, pattern):
2439 tests.addTest(doctest.DocTestSuite())
2440 return tests
2441
2442
2443 if __name__ == "__main__":
2444 unittest.main()