Add python-eventlet 0.16.1
[packages/trusty/python-eventlet.git] / eventlet / eventlet / event.py
1 from __future__ import print_function
2
3 from eventlet import hubs
4 from eventlet.support import greenlets as greenlet
5
6 __all__ = ['Event']
7
8
9 class NOT_USED:
10     def __repr__(self):
11         return 'NOT_USED'
12
13 NOT_USED = NOT_USED()
14
15
16 class Event(object):
17     """An abstraction where an arbitrary number of coroutines
18     can wait for one event from another.
19
20     Events are similar to a Queue that can only hold one item, but differ
21     in two important ways:
22
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.
25
26     They are good for communicating results between coroutines, and
27     are the basis for how
28     :meth:`GreenThread.wait() <eventlet.greenthread.GreenThread.wait>`
29     is implemented.
30
31     >>> from eventlet import event
32     >>> import eventlet
33     >>> evt = event.Event()
34     >>> def baz(b):
35     ...     evt.send(b + 1)
36     ...
37     >>> _ = eventlet.spawn_n(baz, 3)
38     >>> evt.wait()
39     4
40     """
41     _result = None
42     _exc = None
43
44     def __init__(self):
45         self._waiters = set()
46         self.reset()
47
48     def __str__(self):
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
52
53     def reset(self):
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
59         self._exc = None
60
61     def ready(self):
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
68
69     def has_exception(self):
70         return self._exc is not None
71
72     def has_result(self):
73         return self._result is not NOT_USED and self._exc is None
74
75     def poll(self, notready=None):
76         if self.ready():
77             return self.wait()
78         return notready
79
80     # QQQ make it return tuple (type, value, tb) instead of raising
81     # because
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():
87             return self.wait()
88         return notready
89
90     def poll_result(self, notready=None):
91         if self.has_result():
92             return self.wait()
93         return notready
94
95     def wait(self):
96         """Wait until another coroutine calls :meth:`send`.
97         Returns the value the other coroutine passed to
98         :meth:`send`.
99
100         >>> from eventlet import event
101         >>> import eventlet
102         >>> evt = event.Event()
103         >>> def wait_on():
104         ...    retval = evt.wait()
105         ...    print("waited for {0}".format(retval))
106         >>> _ = eventlet.spawn(wait_on)
107         >>> evt.send('result')
108         >>> eventlet.sleep(0)
109         waited for result
110
111         Returns immediately if the event has already
112         occured.
113
114         >>> evt.wait()
115         'result'
116         """
117         current = greenlet.getcurrent()
118         if self._result is NOT_USED:
119             self._waiters.add(current)
120             try:
121                 return hubs.get_hub().switch()
122             finally:
123                 self._waiters.discard(current)
124         if self._exc is not None:
125             current.throw(*self._exc)
126         return self._result
127
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.
131
132         >>> from eventlet import event
133         >>> import eventlet
134         >>> evt = event.Event()
135         >>> def waiter():
136         ...     print('about to wait')
137         ...     result = evt.wait()
138         ...     print('waited for {0}'.format(result))
139         >>> _ = eventlet.spawn(waiter)
140         >>> eventlet.sleep(0)
141         about to wait
142         >>> evt.send('a')
143         >>> eventlet.sleep(0)
144         waited for a
145
146         It is an error to call :meth:`send` multiple times on the same event.
147
148         >>> evt.send('whoops')
149         Traceback (most recent call last):
150         ...
151         AssertionError: Trying to re-send() an already-triggered event.
152
153         Use :meth:`reset` between :meth:`send` s to reuse an event object.
154         """
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):
158             exc = (exc, )
159         self._exc = exc
160         hub = hubs.get_hub()
161         for waiter in self._waiters:
162             hub.schedule_call_global(
163                 0, self._do_send, self._result, self._exc, waiter)
164
165     def _do_send(self, result, exc, waiter):
166         if waiter in self._waiters:
167             if exc is None:
168                 waiter.switch(result)
169             else:
170                 waiter.throw(*exc)
171
172     def send_exception(self, *args):
173         """Same as :meth:`send`, but sends an exception to waiters.
174
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
178         new stacktrace.
179
180            >>> from eventlet import event
181            >>> evt = event.Event()
182            >>> evt.send_exception(RuntimeError())
183            >>> evt.wait()
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)
188            RuntimeError
189
190         If it's important to preserve the entire original stack trace,
191         you must pass in the entire :func:`sys.exc_info` tuple.
192
193            >>> import sys
194            >>> evt = event.Event()
195            >>> try:
196            ...     raise RuntimeError()
197            ... except RuntimeError:
198            ...     evt.send_exception(*sys.exc_info())
199            ...
200            >>> evt.wait()
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>
206            RuntimeError
207
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.
211         """
212         # the arguments and the same as for greenlet.throw
213         return self.send(None, args)