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', 'spawn_after', 'spawn_after_local', 'GreenThread']
13 getcurrent = greenlet.getcurrent
17 """Yield control to another eligible coroutine until at least *seconds* have
20 *seconds* may be specified as an integer, or a float if fractional seconds
21 are desired. Calling :func:`~greenthread.sleep` with *seconds* of 0 is the
22 canonical way of expressing a cooperative yield. For example, if one is
23 looping over a large list performing an expensive calculation without
24 calling any socket methods, it's a good idea to call ``sleep(0)``
25 occasionally; otherwise nothing else will run.
28 current = getcurrent()
29 assert hub.greenlet is not current, 'do not call blocking functions from the mainloop'
30 timer = hub.schedule_call_global(seconds, current.switch)
37 def spawn(func, *args, **kwargs):
38 """Create a greenthread to run ``func(*args, **kwargs)``. Returns a
39 :class:`GreenThread` object which you can use to get the results of the
42 Execution control returns immediately to the caller; the created greenthread
43 is merely scheduled to be run at the next available opportunity.
44 Use :func:`spawn_after` to arrange for greenthreads to be spawned
48 g = GreenThread(hub.greenlet)
49 hub.schedule_call_global(0, g.switch, func, args, kwargs)
53 def spawn_n(func, *args, **kwargs):
54 """Same as :func:`spawn`, but returns a ``greenlet`` object from
55 which it is not possible to retrieve either a return value or
56 whether it raised any exceptions. This is faster than
57 :func:`spawn`; it is fastest if there are no keyword arguments.
59 If an exception is raised in the function, spawn_n prints a stack
60 trace; the print can be disabled by calling
61 :func:`eventlet.debug.hub_exceptions` with False.
63 return _spawn_n(0, func, args, kwargs)[1]
66 def spawn_after(seconds, func, *args, **kwargs):
67 """Spawns *func* after *seconds* have elapsed. It runs as scheduled even if
68 the current greenthread has completed.
70 *seconds* may be specified as an integer, or a float if fractional seconds
71 are desired. The *func* will be called with the given *args* and
72 keyword arguments *kwargs*, and will be executed within its own greenthread.
74 The return value of :func:`spawn_after` is a :class:`GreenThread` object,
75 which can be used to retrieve the results of the call.
77 To cancel the spawn and prevent *func* from being called,
78 call :meth:`GreenThread.cancel` on the return value of :func:`spawn_after`.
79 This will not abort the function if it's already started running, which is
80 generally the desired behavior. If terminating *func* regardless of whether
81 it's started or not is the desired behavior, call :meth:`GreenThread.kill`.
84 g = GreenThread(hub.greenlet)
85 hub.schedule_call_global(seconds, g.switch, func, args, kwargs)
89 def spawn_after_local(seconds, func, *args, **kwargs):
90 """Spawns *func* after *seconds* have elapsed. The function will NOT be
91 called if the current greenthread has exited.
93 *seconds* may be specified as an integer, or a float if fractional seconds
94 are desired. The *func* will be called with the given *args* and
95 keyword arguments *kwargs*, and will be executed within its own greenthread.
97 The return value of :func:`spawn_after` is a :class:`GreenThread` object,
98 which can be used to retrieve the results of the call.
100 To cancel the spawn and prevent *func* from being called,
101 call :meth:`GreenThread.cancel` on the return value. This will not abort the
102 function if it's already started running. If terminating *func* regardless
103 of whether it's started or not is the desired behavior, call
104 :meth:`GreenThread.kill`.
107 g = GreenThread(hub.greenlet)
108 hub.schedule_call_local(seconds, g.switch, func, args, kwargs)
112 def call_after_global(seconds, func, *args, **kwargs):
114 "call_after_global is renamed to spawn_after, which"
115 "has the same signature and semantics (plus a bit extra). Please do a"
116 " quick search-and-replace on your codebase, thanks!",
117 DeprecationWarning, stacklevel=2)
118 return _spawn_n(seconds, func, args, kwargs)[0]
121 def call_after_local(seconds, function, *args, **kwargs):
123 "call_after_local is renamed to spawn_after_local, which"
124 "has the same signature and semantics (plus a bit extra).",
125 DeprecationWarning, stacklevel=2)
127 g = greenlet.greenlet(function, parent=hub.greenlet)
128 t = hub.schedule_call_local(seconds, g.switch, *args, **kwargs)
132 call_after = call_after_local
135 def exc_after(seconds, *throw_args):
136 warnings.warn("Instead of exc_after, which is deprecated, use "
137 "Timeout(seconds, exception)",
138 DeprecationWarning, stacklevel=2)
139 if seconds is None: # dummy argument, do nothing
140 return timer.Timer(seconds, lambda: None)
142 return hub.schedule_call_local(seconds, getcurrent().throw, *throw_args)
145 TimeoutError = timeout.Timeout
146 with_timeout = timeout.with_timeout
149 def _spawn_n(seconds, func, args, kwargs):
151 g = greenlet.greenlet(func, parent=hub.greenlet)
152 t = hub.schedule_call_global(seconds, g.switch, *args, **kwargs)
156 class GreenThread(greenlet.greenlet):
157 """The GreenThread class is a type of Greenlet which has the additional
158 property of being able to retrieve the return value of the main function.
159 Do not construct GreenThread objects directly; call :func:`spawn` to get one.
162 def __init__(self, parent):
163 greenlet.greenlet.__init__(self, self.main, parent)
164 self._exit_event = event.Event()
165 self._resolving_links = False
168 """ Returns the result of the main function of this GreenThread. If the
169 result is a normal return value, :meth:`wait` returns it. If it raised
170 an exception, :meth:`wait` will raise the same exception (though the
171 stack trace will unavoidably contain some frames from within the
172 greenthread module)."""
173 return self._exit_event.wait()
175 def link(self, func, *curried_args, **curried_kwargs):
176 """ Set up a function to be called with the results of the GreenThread.
178 The function must have the following signature::
180 def func(gt, [curried args/kwargs]):
182 When the GreenThread finishes its run, it calls *func* with itself
183 and with the `curried arguments <http://en.wikipedia.org/wiki/Currying>`_ supplied at link-time. If the function wants
184 to retrieve the result of the GreenThread, it should call wait()
185 on its first argument.
187 Note that *func* is called within execution context of
188 the GreenThread, so it is possible to interfere with other linked
189 functions by doing things like switching explicitly to another
192 self._exit_funcs = getattr(self, '_exit_funcs', deque())
193 self._exit_funcs.append((func, curried_args, curried_kwargs))
194 if self._exit_event.ready():
195 self._resolve_links()
197 def unlink(self, func, *curried_args, **curried_kwargs):
198 """ remove linked function set by :meth:`link`
200 Remove successfully return True, otherwise False
202 if not getattr(self, '_exit_funcs', None):
205 self._exit_funcs.remove((func, curried_args, curried_kwargs))
210 def main(self, function, args, kwargs):
212 result = function(*args, **kwargs)
214 self._exit_event.send_exception(*sys.exc_info())
215 self._resolve_links()
218 self._exit_event.send(result)
219 self._resolve_links()
221 def _resolve_links(self):
222 # ca and ckw are the curried function arguments
223 if self._resolving_links:
225 self._resolving_links = True
227 exit_funcs = getattr(self, '_exit_funcs', deque())
229 f, ca, ckw = exit_funcs.popleft()
232 self._resolving_links = False
234 def kill(self, *throw_args):
235 """Kills the greenthread using :func:`kill`. After being killed
236 all calls to :meth:`wait` will raise *throw_args* (which default
237 to :class:`greenlet.GreenletExit`)."""
238 return kill(self, *throw_args)
240 def cancel(self, *throw_args):
241 """Kills the greenthread using :func:`kill`, but only if it hasn't
242 already started running. After being canceled,
243 all calls to :meth:`wait` will raise *throw_args* (which default
244 to :class:`greenlet.GreenletExit`)."""
245 return cancel(self, *throw_args)
248 def cancel(g, *throw_args):
249 """Like :func:`kill`, but only terminates the greenthread if it hasn't
250 already started execution. If the grenthread has already started
251 execution, :func:`cancel` has no effect."""
256 def kill(g, *throw_args):
257 """Terminates the target greenthread by raising an exception into it.
258 Whatever that greenthread might be doing; be it waiting for I/O or another
259 primitive, it sees an exception right away.
261 By default, this exception is GreenletExit, but a specific exception
262 may be specified. *throw_args* should be the same as the arguments to
263 raise; either an exception instance or an exc_info tuple.
265 Calling :func:`kill` causes the calling greenthread to cooperatively yield.
271 # greenlet hasn't started yet and therefore throw won't work
272 # on its own; semantically we want it to be as though the main
273 # method never got called
274 def just_raise(*a, **kw):
276 six.reraise(throw_args[0], throw_args[1], throw_args[2])
278 raise greenlet.GreenletExit()
280 if isinstance(g, GreenThread):
281 # it's a GreenThread object, so we want to call its main
282 # method to take advantage of the notification
284 g.main(just_raise, (), {})
287 current = getcurrent()
288 if current is not hub.greenlet:
289 # arrange to wake the caller back up immediately
290 hub.ensure_greenlet()
291 hub.schedule_call_global(0, current.switch)