1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
import sys
import _continuation
__version__ = "0.4.10"
# ____________________________________________________________
# Exceptions
class GreenletExit(BaseException):
"""This special exception does not propagate to the parent greenlet; it
can be used to kill a single greenlet."""
error = _continuation.error
# ____________________________________________________________
# Helper function
def getcurrent():
"Returns the current greenlet (i.e. the one which called this function)."
try:
return _tls.current
except AttributeError:
# first call in this thread: current == main
_green_create_main()
return _tls.current
# ____________________________________________________________
# The 'greenlet' class
_continulet = _continuation.continulet
class greenlet(_continulet):
getcurrent = staticmethod(getcurrent)
error = error
GreenletExit = GreenletExit
__main = False
__started = False
def __new__(cls, *args, **kwds):
self = _continulet.__new__(cls)
self.parent = getcurrent()
return self
def __init__(self, run=None, parent=None):
if run is not None:
self.run = run
if parent is not None:
self.parent = parent
def switch(self, *args, **kwds):
"Switch execution to this greenlet, optionally passing the values "
"given as argument(s). Returns the value passed when switching back."
return self.__switch('switch', (args, kwds))
def throw(self, typ=GreenletExit, val=None, tb=None):
"raise exception in greenlet, return value passed when switching back"
return self.__switch('throw', typ, val, tb)
def __switch(target, methodname, *baseargs):
current = getcurrent()
#
while not (target.__main or _continulet.is_pending(target)):
# inlined __nonzero__ ^^^ in case it's overridden
if not target.__started:
if methodname == 'switch':
greenlet_func = _greenlet_start
else:
greenlet_func = _greenlet_throw
_continulet.__init__(target, greenlet_func, *baseargs)
methodname = 'switch'
baseargs = ()
target.__started = True
break
# already done, go to the parent instead
# (NB. infinite loop possible, but unlikely, unless you mess
# up the 'parent' explicitly. Good enough, because a Ctrl-C
# will show that the program is caught in this loop here.)
target = target.parent
# convert a "raise GreenletExit" into "return GreenletExit"
if methodname == 'throw':
try:
raise baseargs[0], baseargs[1]
except GreenletExit, e:
methodname = 'switch'
baseargs = (((e,), {}),)
except:
baseargs = sys.exc_info()[:2] + baseargs[2:]
#
try:
unbound_method = getattr(_continulet, methodname)
_tls.leaving = current
args, kwds = unbound_method(current, *baseargs, to=target)
_tls.current = current
except:
_tls.current = current
if hasattr(_tls, 'trace'):
_run_trace_callback('throw')
_tls.leaving = None
raise
else:
if hasattr(_tls, 'trace'):
_run_trace_callback('switch')
_tls.leaving = None
#
if kwds:
if args:
return args, kwds
return kwds
elif len(args) == 1:
return args[0]
else:
return args
def __nonzero__(self):
return self.__main or _continulet.is_pending(self)
@property
def dead(self):
return self.__started and not self
@property
def gr_frame(self):
# xxx this doesn't work when called on either the current or
# the main greenlet of another thread
if self is getcurrent():
return None
if self.__main:
self = getcurrent()
f = _continulet.__reduce__(self)[2][0]
if not f:
return None
return f.f_back.f_back.f_back # go past start(), __switch(), switch()
# ____________________________________________________________
# Recent additions
GREENLET_USE_GC = True
GREENLET_USE_TRACING = True
def gettrace():
return getattr(_tls, 'trace', None)
def settrace(callback):
try:
prev = _tls.trace
del _tls.trace
except AttributeError:
prev = None
if callback is not None:
_tls.trace = callback
return prev
def _run_trace_callback(event):
try:
_tls.trace(event, (_tls.leaving, _tls.current))
except:
# In case of exceptions trace function is removed
if hasattr(_tls, 'trace'):
del _tls.trace
raise
# ____________________________________________________________
# Internal stuff
try:
from threading import local as _local
except ImportError:
class _local(object): # assume no threads
pass
_tls = _local()
def _green_create_main():
# create the main greenlet for this thread
_tls.current = None
gmain = greenlet.__new__(greenlet)
gmain._greenlet__main = True
gmain._greenlet__started = True
assert gmain.parent is None
_tls.main = gmain
_tls.current = gmain
def _greenlet_start(greenlet, args):
try:
args, kwds = args
_tls.current = greenlet
try:
if hasattr(_tls, 'trace'):
_run_trace_callback('switch')
res = greenlet.run(*args, **kwds)
except GreenletExit, e:
res = e
finally:
_continuation.permute(greenlet, greenlet.parent)
return ((res,), None)
finally:
_tls.leaving = greenlet
def _greenlet_throw(greenlet, exc, value, tb):
try:
_tls.current = greenlet
try:
if hasattr(_tls, 'trace'):
_run_trace_callback('throw')
raise exc, value, tb
except GreenletExit, e:
res = e
finally:
_continuation.permute(greenlet, greenlet.parent)
return ((res,), None)
finally:
_tls.leaving = greenlet
|