blob: a3cf379ee81705e7fa22acc0e715571f534085ab [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""A Future class similar to the one in PEP 3148."""
2
Yury Selivanov6370f342017-12-10 18:36:12 -05003__all__ = (
Yury Selivanov6370f342017-12-10 18:36:12 -05004 'Future', 'wrap_future', 'isfuture',
5)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07006
Yury Selivanova0c1ba62016-10-28 12:52:37 -04007import concurrent.futures
Yury Selivanovf23746a2018-01-22 19:11:18 -05008import contextvars
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07009import logging
Victor Stinner4c3c6992013-12-19 22:42:40 +010010import sys
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070011
Yury Selivanova0c1ba62016-10-28 12:52:37 -040012from . import base_futures
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070013from . import events
Andrew Svetlov0baa72f2018-09-11 10:13:04 -070014from . import exceptions
Andrew Svetlovf74ef452017-12-15 07:04:38 +020015from . import format_helpers
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070016
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070017
Yury Selivanova0c1ba62016-10-28 12:52:37 -040018isfuture = base_futures.isfuture
19
20
21_PENDING = base_futures._PENDING
22_CANCELLED = base_futures._CANCELLED
23_FINISHED = base_futures._FINISHED
24
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070025
26STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
27
28
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070029class Future:
30 """This class is *almost* compatible with concurrent.futures.Future.
31
32 Differences:
33
Antoine Pitrou22b11282017-11-07 17:03:28 +010034 - This class is not thread-safe.
35
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070036 - result() and exception() do not take a timeout argument and
37 raise an exception when the future isn't done yet.
38
39 - Callbacks registered with add_done_callback() are always called
Antoine Pitrou22b11282017-11-07 17:03:28 +010040 via the event loop's call_soon().
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070041
42 - This class is not compatible with the wait() and as_completed()
43 methods in the concurrent.futures package.
44
45 (In Python 3.4 or later we may be able to unify the implementations.)
46 """
47
48 # Class variables serving as defaults for instance variables.
49 _state = _PENDING
50 _result = None
51 _exception = None
52 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +010053 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070054
Guido van Rossum1140a032016-09-09 12:54:54 -070055 # This field is used for a dual purpose:
56 # - Its presence is a marker to declare that a class implements
57 # the Future protocol (i.e. is intended to be duck-type compatible).
58 # The value must also be not-None, to enable a subclass to declare
59 # that it is not compatible by setting this to None.
60 # - It is set by __iter__() below so that Task._step() can tell
Andrew Svetlov88743422017-12-11 17:35:49 +020061 # the difference between
62 # `await Future()` or`yield from Future()` (correct) vs.
Guido van Rossum1140a032016-09-09 12:54:54 -070063 # `yield Future()` (incorrect).
64 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070065
Yury Selivanove0aef4f2017-12-25 16:16:10 -050066 __log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070067
68 def __init__(self, *, loop=None):
69 """Initialize the future.
70
Martin Panterc04fb562016-02-10 05:44:01 +000071 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070072 loop object used by the future. If it's not provided, the future uses
73 the default event loop.
74 """
75 if loop is None:
76 self._loop = events.get_event_loop()
77 else:
78 self._loop = loop
79 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020080 if self._loop.get_debug():
Andrew Svetlovf74ef452017-12-15 07:04:38 +020081 self._source_traceback = format_helpers.extract_stack(
82 sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070083
Yury Selivanova0c1ba62016-10-28 12:52:37 -040084 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020085
86 def __repr__(self):
Yury Selivanov6370f342017-12-10 18:36:12 -050087 return '<{} {}>'.format(self.__class__.__name__,
88 ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070089
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090090 def __del__(self):
Yury Selivanove0aef4f2017-12-25 16:16:10 -050091 if not self.__log_traceback:
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090092 # set_exception() was not called, or result() or exception()
93 # has consumed the exception
94 return
95 exc = self._exception
96 context = {
Yury Selivanov6370f342017-12-10 18:36:12 -050097 'message':
98 f'{self.__class__.__name__} exception was never retrieved',
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090099 'exception': exc,
100 'future': self,
101 }
102 if self._source_traceback:
103 context['source_traceback'] = self._source_traceback
104 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100105
Batuhan Taşkayadec36722019-12-07 14:05:07 +0300106 def __class_getitem__(cls, type):
107 return cls
108
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500109 @property
110 def _log_traceback(self):
111 return self.__log_traceback
112
113 @_log_traceback.setter
114 def _log_traceback(self, val):
115 if bool(val):
116 raise ValueError('_log_traceback can only be set to False')
117 self.__log_traceback = False
118
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500119 def get_loop(self):
120 """Return the event loop the Future is bound to."""
Andrew Svetlovdad6be52019-11-13 23:36:46 +0200121 loop = self._loop
122 if loop is None:
123 raise RuntimeError("Future object is not initialized.")
124 return loop
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500125
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700126 def cancel(self):
127 """Cancel the future and schedule callbacks.
128
129 If the future is already done or cancelled, return False. Otherwise,
130 change the future's state to cancelled, schedule the callbacks and
131 return True.
132 """
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500133 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700134 if self._state != _PENDING:
135 return False
136 self._state = _CANCELLED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500137 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700138 return True
139
Yury Selivanov22feeb82018-01-24 11:31:01 -0500140 def __schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700141 """Internal: Ask the event loop to call all callbacks.
142
143 The callbacks are scheduled to be called as soon as possible. Also
144 clears the callback list.
145 """
146 callbacks = self._callbacks[:]
147 if not callbacks:
148 return
149
150 self._callbacks[:] = []
Yury Selivanovf23746a2018-01-22 19:11:18 -0500151 for callback, ctx in callbacks:
152 self._loop.call_soon(callback, self, context=ctx)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700153
154 def cancelled(self):
155 """Return True if the future was cancelled."""
156 return self._state == _CANCELLED
157
158 # Don't implement running(); see http://bugs.python.org/issue18699
159
160 def done(self):
161 """Return True if the future is done.
162
163 Done means either that a result / exception are available, or that the
164 future was cancelled.
165 """
166 return self._state != _PENDING
167
168 def result(self):
169 """Return the result this future represents.
170
171 If the future has been cancelled, raises CancelledError. If the
172 future's result isn't yet available, raises InvalidStateError. If
173 the future is done and has an exception set, this exception is raised.
174 """
175 if self._state == _CANCELLED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700176 raise exceptions.CancelledError
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700177 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700178 raise exceptions.InvalidStateError('Result is not ready.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500179 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700180 if self._exception is not None:
181 raise self._exception
182 return self._result
183
184 def exception(self):
185 """Return the exception that was set on this future.
186
187 The exception (or None if no exception was set) is returned only if
188 the future is done. If the future has been cancelled, raises
189 CancelledError. If the future isn't done yet, raises
190 InvalidStateError.
191 """
192 if self._state == _CANCELLED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700193 raise exceptions.CancelledError
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700194 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700195 raise exceptions.InvalidStateError('Exception is not set.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500196 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700197 return self._exception
198
Yury Selivanovf23746a2018-01-22 19:11:18 -0500199 def add_done_callback(self, fn, *, context=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700200 """Add a callback to be run when the future becomes done.
201
202 The callback is called with a single argument - the future object. If
203 the future is already done when this is called, the callback is
204 scheduled with call_soon.
205 """
206 if self._state != _PENDING:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500207 self._loop.call_soon(fn, self, context=context)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700208 else:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500209 if context is None:
210 context = contextvars.copy_context()
211 self._callbacks.append((fn, context))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700212
213 # New method not in PEP 3148.
214
215 def remove_done_callback(self, fn):
216 """Remove all instances of a callback from the "call when done" list.
217
218 Returns the number of callbacks removed.
219 """
Yury Selivanovf23746a2018-01-22 19:11:18 -0500220 filtered_callbacks = [(f, ctx)
221 for (f, ctx) in self._callbacks
222 if f != fn]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700223 removed_count = len(self._callbacks) - len(filtered_callbacks)
224 if removed_count:
225 self._callbacks[:] = filtered_callbacks
226 return removed_count
227
228 # So-called internal methods (note: no set_running_or_notify_cancel()).
229
230 def set_result(self, result):
231 """Mark the future done and set its result.
232
233 If the future is already done when this method is called, raises
234 InvalidStateError.
235 """
236 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700237 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700238 self._result = result
239 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500240 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241
242 def set_exception(self, exception):
243 """Mark the future done and set an exception.
244
245 If the future is already done when this method is called, raises
246 InvalidStateError.
247 """
248 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700249 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Victor Stinner95728982014-01-30 16:01:54 -0800250 if isinstance(exception, type):
251 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500252 if type(exception) is StopIteration:
253 raise TypeError("StopIteration interacts badly with generators "
254 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700255 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700256 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500257 self.__schedule_callbacks()
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500258 self.__log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700259
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500260 def __await__(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700261 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700262 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700263 yield self # This tells Task to wait for completion.
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500264 if not self.done():
265 raise RuntimeError("await wasn't used with future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700266 return self.result() # May raise too.
267
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500268 __iter__ = __await__ # make compatible with 'yield from'.
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400269
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700270
Yury Selivanov01c521b2016-10-23 22:34:35 -0400271# Needed for testing purposes.
272_PyFuture = Future
273
274
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500275def _get_loop(fut):
276 # Tries to call Future.get_loop() if it's available.
277 # Otherwise fallbacks to using the old '_loop' property.
278 try:
279 get_loop = fut.get_loop
280 except AttributeError:
281 pass
282 else:
283 return get_loop()
284 return fut._loop
285
286
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500287def _set_result_unless_cancelled(fut, result):
288 """Helper setting the result only if the future was not cancelled."""
289 if fut.cancelled():
290 return
291 fut.set_result(result)
292
293
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700294def _convert_future_exc(exc):
295 exc_class = type(exc)
296 if exc_class is concurrent.futures.CancelledError:
297 return exceptions.CancelledError(*exc.args)
298 elif exc_class is concurrent.futures.TimeoutError:
299 return exceptions.TimeoutError(*exc.args)
300 elif exc_class is concurrent.futures.InvalidStateError:
301 return exceptions.InvalidStateError(*exc.args)
302 else:
303 return exc
304
305
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700306def _set_concurrent_future_state(concurrent, source):
307 """Copy state from a future to a concurrent.futures.Future."""
308 assert source.done()
309 if source.cancelled():
310 concurrent.cancel()
311 if not concurrent.set_running_or_notify_cancel():
312 return
313 exception = source.exception()
314 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700315 concurrent.set_exception(_convert_future_exc(exception))
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700316 else:
317 result = source.result()
318 concurrent.set_result(result)
319
320
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500321def _copy_future_state(source, dest):
322 """Internal helper to copy state from another Future.
323
324 The other Future may be a concurrent.futures.Future.
325 """
326 assert source.done()
327 if dest.cancelled():
328 return
329 assert not dest.done()
330 if source.cancelled():
331 dest.cancel()
332 else:
333 exception = source.exception()
334 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700335 dest.set_exception(_convert_future_exc(exception))
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500336 else:
337 result = source.result()
338 dest.set_result(result)
339
340
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700341def _chain_future(source, destination):
342 """Chain two futures so that when one completes, so does the other.
343
344 The result (or exception) of source will be copied to destination.
345 If destination is cancelled, source gets cancelled too.
346 Compatible with both asyncio.Future and concurrent.futures.Future.
347 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700348 if not isfuture(source) and not isinstance(source,
349 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700350 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700351 if not isfuture(destination) and not isinstance(destination,
352 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700353 raise TypeError('A future is required for destination argument')
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500354 source_loop = _get_loop(source) if isfuture(source) else None
355 dest_loop = _get_loop(destination) if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700356
357 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700358 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500359 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700360 else:
361 _set_concurrent_future_state(future, other)
362
363 def _call_check_cancel(destination):
364 if destination.cancelled():
365 if source_loop is None or source_loop is dest_loop:
366 source.cancel()
367 else:
368 source_loop.call_soon_threadsafe(source.cancel)
369
370 def _call_set_state(source):
Yury Selivanovfdccfe02018-05-28 17:10:20 -0400371 if (destination.cancelled() and
372 dest_loop is not None and dest_loop.is_closed()):
373 return
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700374 if dest_loop is None or dest_loop is source_loop:
375 _set_state(destination, source)
376 else:
377 dest_loop.call_soon_threadsafe(_set_state, destination, source)
378
379 destination.add_done_callback(_call_check_cancel)
380 source.add_done_callback(_call_set_state)
381
382
383def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700384 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700385 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700386 return future
387 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500388 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400389 if loop is None:
390 loop = events.get_event_loop()
391 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700392 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700393 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900394
395
396try:
397 import _asyncio
398except ImportError:
399 pass
400else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400401 # _CFuture is needed for tests.
402 Future = _CFuture = _asyncio.Future