blob: 39721eaf00fd42a8ea24af21f9f9c82cbcaa2596 [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""A Future class similar to the one in PEP 3148."""
2
Yury Selivanova0c1ba62016-10-28 12:52:37 -04003__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError',
4 'Future', 'wrap_future', 'isfuture']
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07005
Yury Selivanova0c1ba62016-10-28 12:52:37 -04006import concurrent.futures
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07007import logging
Victor Stinner4c3c6992013-12-19 22:42:40 +01008import sys
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07009import traceback
10
Yury Selivanova0c1ba62016-10-28 12:52:37 -040011from . import base_futures
Victor Stinner71080fc2015-07-25 02:23:21 +020012from . import compat
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070013from . import events
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070014
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070015
Yury Selivanova0c1ba62016-10-28 12:52:37 -040016CancelledError = base_futures.CancelledError
17InvalidStateError = base_futures.InvalidStateError
18TimeoutError = base_futures.TimeoutError
19isfuture = base_futures.isfuture
20
21
22_PENDING = base_futures._PENDING
23_CANCELLED = base_futures._CANCELLED
24_FINISHED = base_futures._FINISHED
25
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070026
27STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
28
29
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070030class Future:
31 """This class is *almost* compatible with concurrent.futures.Future.
32
33 Differences:
34
35 - result() and exception() do not take a timeout argument and
36 raise an exception when the future isn't done yet.
37
38 - Callbacks registered with add_done_callback() are always called
39 via the event loop's call_soon_threadsafe().
40
41 - This class is not compatible with the wait() and as_completed()
42 methods in the concurrent.futures package.
43
44 (In Python 3.4 or later we may be able to unify the implementations.)
45 """
46
47 # Class variables serving as defaults for instance variables.
48 _state = _PENDING
49 _result = None
50 _exception = None
51 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +010052 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070053
Guido van Rossum1140a032016-09-09 12:54:54 -070054 # This field is used for a dual purpose:
55 # - Its presence is a marker to declare that a class implements
56 # the Future protocol (i.e. is intended to be duck-type compatible).
57 # The value must also be not-None, to enable a subclass to declare
58 # that it is not compatible by setting this to None.
59 # - It is set by __iter__() below so that Task._step() can tell
60 # the difference between `yield from Future()` (correct) vs.
61 # `yield Future()` (incorrect).
62 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070063
Victor Stinnere40c0782013-12-21 00:19:33 +010064 _log_traceback = False # Used for Python 3.4 and later
65 _tb_logger = None # Used for Python 3.3 only
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070066
67 def __init__(self, *, loop=None):
68 """Initialize the future.
69
Martin Panterc04fb562016-02-10 05:44:01 +000070 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070071 loop object used by the future. If it's not provided, the future uses
72 the default event loop.
73 """
74 if loop is None:
75 self._loop = events.get_event_loop()
76 else:
77 self._loop = loop
78 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020079 if self._loop.get_debug():
80 self._source_traceback = traceback.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070081
Yury Selivanova0c1ba62016-10-28 12:52:37 -040082 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020083
84 def __repr__(self):
INADA Naoki9e4e38e2016-10-09 14:44:47 +090085 return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070086
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090087 def __del__(self):
88 if not self._log_traceback:
89 # set_exception() was not called, or result() or exception()
90 # has consumed the exception
91 return
92 exc = self._exception
93 context = {
94 'message': ('%s exception was never retrieved'
95 % self.__class__.__name__),
96 'exception': exc,
97 'future': self,
98 }
99 if self._source_traceback:
100 context['source_traceback'] = self._source_traceback
101 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100102
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700103 def cancel(self):
104 """Cancel the future and schedule callbacks.
105
106 If the future is already done or cancelled, return False. Otherwise,
107 change the future's state to cancelled, schedule the callbacks and
108 return True.
109 """
110 if self._state != _PENDING:
111 return False
112 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400113 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700114 return True
115
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400116 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700117 """Internal: Ask the event loop to call all callbacks.
118
119 The callbacks are scheduled to be called as soon as possible. Also
120 clears the callback list.
121 """
122 callbacks = self._callbacks[:]
123 if not callbacks:
124 return
125
126 self._callbacks[:] = []
127 for callback in callbacks:
128 self._loop.call_soon(callback, self)
129
130 def cancelled(self):
131 """Return True if the future was cancelled."""
132 return self._state == _CANCELLED
133
134 # Don't implement running(); see http://bugs.python.org/issue18699
135
136 def done(self):
137 """Return True if the future is done.
138
139 Done means either that a result / exception are available, or that the
140 future was cancelled.
141 """
142 return self._state != _PENDING
143
144 def result(self):
145 """Return the result this future represents.
146
147 If the future has been cancelled, raises CancelledError. If the
148 future's result isn't yet available, raises InvalidStateError. If
149 the future is done and has an exception set, this exception is raised.
150 """
151 if self._state == _CANCELLED:
152 raise CancelledError
153 if self._state != _FINISHED:
154 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100155 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700156 if self._tb_logger is not None:
157 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100158 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700159 if self._exception is not None:
160 raise self._exception
161 return self._result
162
163 def exception(self):
164 """Return the exception that was set on this future.
165
166 The exception (or None if no exception was set) is returned only if
167 the future is done. If the future has been cancelled, raises
168 CancelledError. If the future isn't done yet, raises
169 InvalidStateError.
170 """
171 if self._state == _CANCELLED:
172 raise CancelledError
173 if self._state != _FINISHED:
174 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100175 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700176 if self._tb_logger is not None:
177 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100178 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700179 return self._exception
180
181 def add_done_callback(self, fn):
182 """Add a callback to be run when the future becomes done.
183
184 The callback is called with a single argument - the future object. If
185 the future is already done when this is called, the callback is
186 scheduled with call_soon.
187 """
188 if self._state != _PENDING:
189 self._loop.call_soon(fn, self)
190 else:
191 self._callbacks.append(fn)
192
193 # New method not in PEP 3148.
194
195 def remove_done_callback(self, fn):
196 """Remove all instances of a callback from the "call when done" list.
197
198 Returns the number of callbacks removed.
199 """
200 filtered_callbacks = [f for f in self._callbacks if f != fn]
201 removed_count = len(self._callbacks) - len(filtered_callbacks)
202 if removed_count:
203 self._callbacks[:] = filtered_callbacks
204 return removed_count
205
206 # So-called internal methods (note: no set_running_or_notify_cancel()).
207
208 def set_result(self, result):
209 """Mark the future done and set its result.
210
211 If the future is already done when this method is called, raises
212 InvalidStateError.
213 """
214 if self._state != _PENDING:
215 raise InvalidStateError('{}: {!r}'.format(self._state, self))
216 self._result = result
217 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400218 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700219
220 def set_exception(self, exception):
221 """Mark the future done and set an exception.
222
223 If the future is already done when this method is called, raises
224 InvalidStateError.
225 """
226 if self._state != _PENDING:
227 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800228 if isinstance(exception, type):
229 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500230 if type(exception) is StopIteration:
231 raise TypeError("StopIteration interacts badly with generators "
232 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700233 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700234 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400235 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900236 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700237
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700238 def __iter__(self):
239 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700240 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241 yield self # This tells Task to wait for completion.
242 assert self.done(), "yield from wasn't used with future"
243 return self.result() # May raise too.
244
Victor Stinner71080fc2015-07-25 02:23:21 +0200245 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400246 __await__ = __iter__ # make compatible with 'await' expression
247
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700248
Yury Selivanov01c521b2016-10-23 22:34:35 -0400249# Needed for testing purposes.
250_PyFuture = Future
251
252
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500253def _set_result_unless_cancelled(fut, result):
254 """Helper setting the result only if the future was not cancelled."""
255 if fut.cancelled():
256 return
257 fut.set_result(result)
258
259
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700260def _set_concurrent_future_state(concurrent, source):
261 """Copy state from a future to a concurrent.futures.Future."""
262 assert source.done()
263 if source.cancelled():
264 concurrent.cancel()
265 if not concurrent.set_running_or_notify_cancel():
266 return
267 exception = source.exception()
268 if exception is not None:
269 concurrent.set_exception(exception)
270 else:
271 result = source.result()
272 concurrent.set_result(result)
273
274
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500275def _copy_future_state(source, dest):
276 """Internal helper to copy state from another Future.
277
278 The other Future may be a concurrent.futures.Future.
279 """
280 assert source.done()
281 if dest.cancelled():
282 return
283 assert not dest.done()
284 if source.cancelled():
285 dest.cancel()
286 else:
287 exception = source.exception()
288 if exception is not None:
289 dest.set_exception(exception)
290 else:
291 result = source.result()
292 dest.set_result(result)
293
294
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700295def _chain_future(source, destination):
296 """Chain two futures so that when one completes, so does the other.
297
298 The result (or exception) of source will be copied to destination.
299 If destination is cancelled, source gets cancelled too.
300 Compatible with both asyncio.Future and concurrent.futures.Future.
301 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700302 if not isfuture(source) and not isinstance(source,
303 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700304 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700305 if not isfuture(destination) and not isinstance(destination,
306 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700307 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700308 source_loop = source._loop if isfuture(source) else None
309 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700310
311 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700312 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500313 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700314 else:
315 _set_concurrent_future_state(future, other)
316
317 def _call_check_cancel(destination):
318 if destination.cancelled():
319 if source_loop is None or source_loop is dest_loop:
320 source.cancel()
321 else:
322 source_loop.call_soon_threadsafe(source.cancel)
323
324 def _call_set_state(source):
325 if dest_loop is None or dest_loop is source_loop:
326 _set_state(destination, source)
327 else:
328 dest_loop.call_soon_threadsafe(_set_state, destination, source)
329
330 destination.add_done_callback(_call_check_cancel)
331 source.add_done_callback(_call_set_state)
332
333
334def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700335 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700336 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700337 return future
338 assert isinstance(future, concurrent.futures.Future), \
339 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400340 if loop is None:
341 loop = events.get_event_loop()
342 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700343 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700344 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900345
346
347try:
348 import _asyncio
349except ImportError:
350 pass
351else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400352 # _CFuture is needed for tests.
353 Future = _CFuture = _asyncio.Future