77from functools import wraps
88from inspect import isgenerator
99
10- from unpythonic .dynscope import dyn
11-
1210def gtco (generator ):
1311 """Low-level function: run a generator with TCO enabled.
1412
@@ -23,15 +21,19 @@ def gen():
2321 assert tuple(take(6, gtco(gen()))) == (1, 2, 1, 2, 1, 2)
2422 last(take(10000, gtco(gen()))) # no crash
2523 """
26- with dyn .let (_gtrampoline_active = True ):
27- while True : # trampoline
28- x = yield from generator # yield stuff, get final result (return ...)
29- if isgenerator (x ) or isinstance (x , _TrampolinedGenerator ):
30- generator = x
31- else :
32- if x : # usually this is None, but allow for an iterable
33- yield from x # the last batch!
34- break
24+ while True : # trampoline
25+ x = yield from generator # yield stuff, get final result (return ...)
26+ # don't let the TCO jump target bring along its trampoline if it has one
27+ if isinstance (x , _TrampolinedGenerator ):
28+ x = x .g
29+ if isgenerator (x ):
30+ generator = x
31+ else :
32+ # usually the return value is None, but allow for an iterable
33+ try :
34+ yield from x # the last batch!
35+ except TypeError :
36+ return x # passthrough
3537
3638def gtrampolined (gfunc ):
3739 """Decorator for generator functions (i.e. definitions of generators).
@@ -48,21 +50,17 @@ def ones():
4850 last(take(10000, ones())) # no crash
4951 """
5052 @wraps (gfunc )
51- def decorated (* args , ** kwargs ):
53+ def trampolining_gfunc (* args , ** kwargs ):
5254 generator = gfunc (* args , ** kwargs )
53- if "_gtrampoline_active" not in dyn : # start up the trampoline
54- return _TrampolinedGenerator (generator )
55- else : # avoid stacking when already running in the trampoline
56- # and a generator calls a gtrampolined gfunc (incl. its own!)
57- return generator
58- return decorated
55+ return _TrampolinedGenerator (generator ) # inject a trampoline
56+ return trampolining_gfunc
5957
6058class _TrampolinedGenerator :
6159 """Wrapper to inject the gtco() call to the generator g returned by gfunc."""
6260 def __init__ (self , g ):
6361 self .g = g
6462 def __iter__ (self ):
65- return gtco (iter (self .g ))
63+ return gtco (iter (self .g )) # start the trampoline
6664 # no __next__, because __iter__ redirects;
6765 # this wrapper is never actually iterated over.
6866
0 commit comments