1 from collections import deque
4 from eventlet import event
5 from eventlet import hubs
6 from eventlet import timeout
7 from eventlet.hubs import timer
8 from eventlet.support import greenlets as greenlet, six
11 __all__ = ['getcurrent', 'sleep', 'spawn', 'spawn_n',
13 'spawn_after', 'spawn_after_local', 'GreenThread']
15 getcurrent = greenlet.getcurrent
19 """Yield control to another eligible coroutine until at least *seconds* have
22 *seconds* may be specified as an integer, or a float if fractional seconds
23 are desired. Calling :func:`~greenthread.sleep` with *seconds* of 0 is the
24 canonical way of expressing a cooperative yield. For example, if one is
25 looping over a large list performing an expensive calculation without
26 calling any socket methods, it's a good idea to call ``sleep(0)``
27 occasionally; otherwise nothing else will run.
30 current = getcurrent()
31 assert hub.greenlet is not current, 'do not call blocking functions from the mainloop'
32 timer = hub.schedule_call_global(seconds, current.switch)
39 def spawn(func, *args, **kwargs):
40 """Create a greenthread to run ``func(*args, **kwargs)``. Returns a
41 :class:`GreenThread` object which you can use to get the results of the
44 Execution control returns immediately to the caller; the created greenthread
45 is merely scheduled to be run at the next available opportunity.
46 Use :func:`spawn_after` to arrange for greenthreads to be spawned
50 g = GreenThread(hub.greenlet)
51 hub.schedule_call_global(0, g.switch, func, args, kwargs)
55 def spawn_n(func, *args, **kwargs):
56 """Same as :func:`spawn`, but returns a ``greenlet`` object from
57 which it is not possible to retrieve either a return value or
58 whether it raised any exceptions. This is faster than
59 :func:`spawn`; it is fastest if there are no keyword arguments.
61 If an exception is raised in the function, spawn_n prints a stack
62 trace; the print can be disabled by calling
63 :func:`eventlet.debug.hub_exceptions` with False.
65 return _spawn_n(0, func, args, kwargs)[1]
68 def spawn_after(seconds, func, *args, **kwargs):
69 """Spawns *func* after *seconds* have elapsed. It runs as scheduled even if
70 the current greenthread has completed.
72 *seconds* may be specified as an integer, or a float if fractional seconds
73 are desired. The *func* will be called with the given *args* and
74 keyword arguments *kwargs*, and will be executed within its own greenthread.
76 The return value of :func:`spawn_after` is a :class:`GreenThread` object,
77 which can be used to retrieve the results of the call.
79 To cancel the spawn and prevent *func* from being called,
80 call :meth:`GreenThread.cancel` on the return value of :func:`spawn_after`.
81 This will not abort the function if it's already started running, which is
82 generally the desired behavior. If terminating *func* regardless of whether
83 it's started or not is the desired behavior, call :meth:`GreenThread.kill`.
86 g = GreenThread(hub.greenlet)
87 hub.schedule_call_global(seconds, g.switch, func, args, kwargs)
91 def spawn_after_local(seconds, func, *args, **kwargs):
92 """Spawns *func* after *seconds* have elapsed. The function will NOT be
93 called if the current greenthread has exited.
95 *seconds* may be specified as an integer, or a float if fractional seconds
96 are desired. The *func* will be called with the given *args* and
97 keyword arguments *kwargs*, and will be executed within its own greenthread.
99 The return value of :func:`spawn_after` is a :class:`GreenThread` object,
100 which can be used to retrieve the results of the call.
102 To cancel the spawn and prevent *func* from being called,
103 call :meth:`GreenThread.cancel` on the return value. This will not abort the
104 function if it's already started running. If terminating *func* regardless
105 of whether it's started or not is the desired behavior, call
106 :meth:`GreenThread.kill`.
109 g = GreenThread(hub.greenlet)
110 hub.schedule_call_local(seconds, g.switch, func, args, kwargs)
114 def call_after_global(seconds, func, *args, **kwargs):
116 "call_after_global is renamed to spawn_after, which"
117 "has the same signature and semantics (plus a bit extra). Please do a"
118 " quick search-and-replace on your codebase, thanks!",
119 DeprecationWarning, stacklevel=2)
120 return _spawn_n(seconds, func, args, kwargs)[0]
123 def call_after_local(seconds, function, *args, **kwargs):
125 "call_after_local is renamed to spawn_after_local, which"
126 "has the same signature and semantics (plus a bit extra).",
127 DeprecationWarning, stacklevel=2)
129 g = greenlet.greenlet(function, parent=hub.greenlet)
130 t = hub.schedule_call_local(seconds, g.switch, *args, **kwargs)
134 call_after = call_after_local
137 def exc_after(seconds, *throw_args):
138 warnings.warn("Instead of exc_after, which is deprecated, use "
139 "Timeout(seconds, exception)",
140 DeprecationWarning, stacklevel=2)
141 if seconds is None: # dummy argument, do nothing
142 return timer.Timer(seconds, lambda: None)
144 return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args)
147 TimeoutError = timeout.Timeout
148 with_timeout = timeout.with_timeout
151 def _spawn_n(seconds, func, args, kwargs):
153 g = greenlet.greenlet(func, parent=hub.greenlet)
154 t = hub.schedule_call_global(seconds, g.switch, *args, **kwargs)
158 class GreenThread(greenlet.greenlet):
159 """The GreenThread class is a type of Greenlet which has the additional
160 property of being able to retrieve the return value of the main function.
161 Do not construct GreenThread objects directly; call :func:`spawn` to get one.
164 def __init__(self, parent):
165 greenlet.greenlet.__init__(self, self.main, parent)
166 self._exit_event = event.Event()
167 self._resolving_links = False
170 """ Returns the result of the main function of this GreenThread. If the
171 result is a normal return value, :meth:`wait` returns it. If it raised
172 an exception, :meth:`wait` will raise the same exception (though the
173 stack trace will unavoidably contain some frames from within the
174 greenthread module)."""
175 return self._exit_event.wait()
177 def link(self, func, *curried_args, **curried_kwargs):
178 """ Set up a function to be called with the results of the GreenThread.
180 The function must have the following signature::
182 def func(gt, [curried args/kwargs]):
184 When the GreenThread finishes its run, it calls *func* with itself
185 and with the `curried arguments <http://en.wikipedia.org/wiki/Currying>`_ supplied
186 at link-time. If the function wants to retrieve the result of the GreenThread,
187 it should call wait() on its first argument.
189 Note that *func* is called within execution context of
190 the GreenThread, so it is possible to interfere with other linked
191 functions by doing things like switching explicitly to another
194 self._exit_funcs = getattr(self, '_exit_funcs', deque())
195 self._exit_funcs.append((func, curried_args, curried_kwargs))
196 if self._exit_event.ready():
197 self._resolve_links()
199 def unlink(self, func, *curried_args, **curried_kwargs):
200 """ remove linked function set by :meth:`link`
202 Remove successfully return True, otherwise False
204 if not getattr(self, '_exit_funcs', None):
207 self._exit_funcs.remove((func, curried_args, curried_kwargs))
212 def main(self, function, args, kwargs):
214 result = function(*args, **kwargs)
216 self._exit_event.send_exception(*sys.exc_info())
217 self._resolve_links()
220 self._exit_event.send(result)
221 self._resolve_links()
223 def _resolve_links(self):
224 # ca and ckw are the curried function arguments
225 if self._resolving_links:
227 self._resolving_links = True
229 exit_funcs = getattr(self, '_exit_funcs', deque())
231 f, ca, ckw = exit_funcs.popleft()
234 self._resolving_links = False
236 def kill(self, *throw_args):
237 """Kills the greenthread using :func:`kill`. After being killed
238 all calls to :meth:`wait` will raise *throw_args* (which default
239 to :class:`greenlet.GreenletExit`)."""
240 return kill(self, *throw_args)
242 def cancel(self, *throw_args):
243 """Kills the greenthread using :func:`kill`, but only if it hasn't
244 already started running. After being canceled,
245 all calls to :meth:`wait` will raise *throw_args* (which default
246 to :class:`greenlet.GreenletExit`)."""
247 return cancel(self, *throw_args)
250 def cancel(g, *throw_args):
251 """Like :func:`kill`, but only terminates the greenthread if it hasn't
252 already started execution. If the grenthread has already started
253 execution, :func:`cancel` has no effect."""
258 def kill(g, *throw_args):
259 """Terminates the target greenthread by raising an exception into it.
260 Whatever that greenthread might be doing; be it waiting for I/O or another
261 primitive, it sees an exception right away.
263 By default, this exception is GreenletExit, but a specific exception
264 may be specified. *throw_args* should be the same as the arguments to
265 raise; either an exception instance or an exc_info tuple.
267 Calling :func:`kill` causes the calling greenthread to cooperatively yield.
273 # greenlet hasn't started yet and therefore throw won't work
274 # on its own; semantically we want it to be as though the main
275 # method never got called
276 def just_raise(*a, **kw):
278 six.reraise(throw_args[0], throw_args[1], throw_args[2])
280 raise greenlet.GreenletExit()
282 if isinstance(g, GreenThread):
283 # it's a GreenThread object, so we want to call its main
284 # method to take advantage of the notification
286 g.main(just_raise, (), {})
289 current = getcurrent()
290 if current is not hub.greenlet:
291 # arrange to wake the caller back up immediately
292 hub.ensure_greenlet()
293 hub.schedule_call_global(0, current.switch)