1 from __future__ import print_function
3 from eventlet import hubs
4 from eventlet.support import greenlets as greenlet
17 """An abstraction where an arbitrary number of coroutines
18 can wait for one event from another.
20 Events are similar to a Queue that can only hold one item, but differ
21 in two important ways:
23 1. calling :meth:`send` never unschedules the current greenthread
24 2. :meth:`send` can only be called once; create a new event to send again.
26 They are good for communicating results between coroutines, and
28 :meth:`GreenThread.wait() <eventlet.greenthread.GreenThread.wait>`
31 >>> from eventlet import event
33 >>> evt = event.Event()
37 >>> _ = eventlet.spawn_n(baz, 3)
49 params = (self.__class__.__name__, hex(id(self)),
50 self._result, self._exc, len(self._waiters))
51 return '<%s at %s result=%r _exc=%r _waiters[%d]>' % params
54 # this is kind of a misfeature and doesn't work perfectly well,
55 # it's better to create a new event rather than reset an old one
56 # removing documentation so that we don't get new use cases for it
57 assert self._result is not NOT_USED, 'Trying to re-reset() a fresh event.'
58 self._result = NOT_USED
62 """ Return true if the :meth:`wait` call will return immediately.
63 Used to avoid waiting for things that might take a while to time out.
64 For example, you can put a bunch of events into a list, and then visit
65 them all repeatedly, calling :meth:`ready` until one returns ``True``,
66 and then you can :meth:`wait` on that one."""
67 return self._result is not NOT_USED
69 def has_exception(self):
70 return self._exc is not None
73 return self._result is not NOT_USED and self._exc is None
75 def poll(self, notready=None):
80 # QQQ make it return tuple (type, value, tb) instead of raising
82 # 1) "poll" does not imply raising
83 # 2) it's better not to screw up caller's sys.exc_info() by default
84 # (e.g. if caller wants to calls the function in except or finally)
85 def poll_exception(self, notready=None):
86 if self.has_exception():
90 def poll_result(self, notready=None):
96 """Wait until another coroutine calls :meth:`send`.
97 Returns the value the other coroutine passed to
100 >>> from eventlet import event
102 >>> evt = event.Event()
104 ... retval = evt.wait()
105 ... print("waited for {0}".format(retval))
106 >>> _ = eventlet.spawn(wait_on)
107 >>> evt.send('result')
108 >>> eventlet.sleep(0)
111 Returns immediately if the event has already
117 current = greenlet.getcurrent()
118 if self._result is NOT_USED:
119 self._waiters.add(current)
121 return hubs.get_hub().switch()
123 self._waiters.discard(current)
124 if self._exc is not None:
125 current.throw(*self._exc)
128 def send(self, result=None, exc=None):
129 """Makes arrangements for the waiters to be woken with the
130 result and then returns immediately to the parent.
132 >>> from eventlet import event
134 >>> evt = event.Event()
136 ... print('about to wait')
137 ... result = evt.wait()
138 ... print('waited for {0}'.format(result))
139 >>> _ = eventlet.spawn(waiter)
140 >>> eventlet.sleep(0)
143 >>> eventlet.sleep(0)
146 It is an error to call :meth:`send` multiple times on the same event.
148 >>> evt.send('whoops')
149 Traceback (most recent call last):
151 AssertionError: Trying to re-send() an already-triggered event.
153 Use :meth:`reset` between :meth:`send` s to reuse an event object.
155 assert self._result is NOT_USED, 'Trying to re-send() an already-triggered event.'
156 self._result = result
157 if exc is not None and not isinstance(exc, tuple):
161 for waiter in self._waiters:
162 hub.schedule_call_global(
163 0, self._do_send, self._result, self._exc, waiter)
165 def _do_send(self, result, exc, waiter):
166 if waiter in self._waiters:
168 waiter.switch(result)
172 def send_exception(self, *args):
173 """Same as :meth:`send`, but sends an exception to waiters.
175 The arguments to send_exception are the same as the arguments
176 to ``raise``. If a single exception object is passed in, it
177 will be re-raised when :meth:`wait` is called, generating a
180 >>> from eventlet import event
181 >>> evt = event.Event()
182 >>> evt.send_exception(RuntimeError())
184 Traceback (most recent call last):
185 File "<stdin>", line 1, in <module>
186 File "eventlet/event.py", line 120, in wait
187 current.throw(*self._exc)
190 If it's important to preserve the entire original stack trace,
191 you must pass in the entire :func:`sys.exc_info` tuple.
194 >>> evt = event.Event()
196 ... raise RuntimeError()
197 ... except RuntimeError:
198 ... evt.send_exception(*sys.exc_info())
201 Traceback (most recent call last):
202 File "<stdin>", line 1, in <module>
203 File "eventlet/event.py", line 120, in wait
204 current.throw(*self._exc)
205 File "<stdin>", line 2, in <module>
208 Note that doing so stores a traceback object directly on the
209 Event object, which may cause reference cycles. See the
210 :func:`sys.exc_info` documentation.
212 # the arguments and the same as for greenlet.throw
213 return self.send(None, args)