blob: bed4da52fd4d98b9413be0f92541d41f86cead46 [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
Chris Jerdonek1ce58412020-05-15 16:55:50 -070054 _cancel_message = None
Chris Jerdonekda742ba2020-05-17 22:47:31 -070055 # A saved CancelledError for later chaining as an exception context.
56 _cancelled_exc = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070057
Guido van Rossum1140a032016-09-09 12:54:54 -070058 # This field is used for a dual purpose:
59 # - Its presence is a marker to declare that a class implements
60 # the Future protocol (i.e. is intended to be duck-type compatible).
61 # The value must also be not-None, to enable a subclass to declare
62 # that it is not compatible by setting this to None.
63 # - It is set by __iter__() below so that Task._step() can tell
Andrew Svetlov88743422017-12-11 17:35:49 +020064 # the difference between
65 # `await Future()` or`yield from Future()` (correct) vs.
Guido van Rossum1140a032016-09-09 12:54:54 -070066 # `yield Future()` (incorrect).
67 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070068
Yury Selivanove0aef4f2017-12-25 16:16:10 -050069 __log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070070
71 def __init__(self, *, loop=None):
72 """Initialize the future.
73
Martin Panterc04fb562016-02-10 05:44:01 +000074 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070075 loop object used by the future. If it's not provided, the future uses
76 the default event loop.
77 """
78 if loop is None:
79 self._loop = events.get_event_loop()
80 else:
81 self._loop = loop
82 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020083 if self._loop.get_debug():
Andrew Svetlovf74ef452017-12-15 07:04:38 +020084 self._source_traceback = format_helpers.extract_stack(
85 sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070086
Yury Selivanova0c1ba62016-10-28 12:52:37 -040087 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020088
89 def __repr__(self):
Yury Selivanov6370f342017-12-10 18:36:12 -050090 return '<{} {}>'.format(self.__class__.__name__,
91 ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070092
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090093 def __del__(self):
Yury Selivanove0aef4f2017-12-25 16:16:10 -050094 if not self.__log_traceback:
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090095 # set_exception() was not called, or result() or exception()
96 # has consumed the exception
97 return
98 exc = self._exception
99 context = {
Yury Selivanov6370f342017-12-10 18:36:12 -0500100 'message':
101 f'{self.__class__.__name__} exception was never retrieved',
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900102 'exception': exc,
103 'future': self,
104 }
105 if self._source_traceback:
106 context['source_traceback'] = self._source_traceback
107 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100108
Batuhan Taşkayadec36722019-12-07 14:05:07 +0300109 def __class_getitem__(cls, type):
110 return cls
111
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500112 @property
113 def _log_traceback(self):
114 return self.__log_traceback
115
116 @_log_traceback.setter
117 def _log_traceback(self, val):
118 if bool(val):
119 raise ValueError('_log_traceback can only be set to False')
120 self.__log_traceback = False
121
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500122 def get_loop(self):
123 """Return the event loop the Future is bound to."""
Andrew Svetlovdad6be52019-11-13 23:36:46 +0200124 loop = self._loop
125 if loop is None:
126 raise RuntimeError("Future object is not initialized.")
127 return loop
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500128
Chris Jerdonekda742ba2020-05-17 22:47:31 -0700129 def _make_cancelled_error(self):
130 """Create the CancelledError to raise if the Future is cancelled.
131
132 This should only be called once when handling a cancellation since
133 it erases the saved context exception value.
134 """
135 if self._cancel_message is None:
136 exc = exceptions.CancelledError()
137 else:
138 exc = exceptions.CancelledError(self._cancel_message)
139 exc.__context__ = self._cancelled_exc
140 # Remove the reference since we don't need this anymore.
141 self._cancelled_exc = None
142 return exc
143
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700144 def cancel(self, msg=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700145 """Cancel the future and schedule callbacks.
146
147 If the future is already done or cancelled, return False. Otherwise,
148 change the future's state to cancelled, schedule the callbacks and
149 return True.
150 """
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500151 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700152 if self._state != _PENDING:
153 return False
154 self._state = _CANCELLED
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700155 self._cancel_message = msg
Yury Selivanov22feeb82018-01-24 11:31:01 -0500156 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700157 return True
158
Yury Selivanov22feeb82018-01-24 11:31:01 -0500159 def __schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700160 """Internal: Ask the event loop to call all callbacks.
161
162 The callbacks are scheduled to be called as soon as possible. Also
163 clears the callback list.
164 """
165 callbacks = self._callbacks[:]
166 if not callbacks:
167 return
168
169 self._callbacks[:] = []
Yury Selivanovf23746a2018-01-22 19:11:18 -0500170 for callback, ctx in callbacks:
171 self._loop.call_soon(callback, self, context=ctx)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700172
173 def cancelled(self):
174 """Return True if the future was cancelled."""
175 return self._state == _CANCELLED
176
177 # Don't implement running(); see http://bugs.python.org/issue18699
178
179 def done(self):
180 """Return True if the future is done.
181
182 Done means either that a result / exception are available, or that the
183 future was cancelled.
184 """
185 return self._state != _PENDING
186
187 def result(self):
188 """Return the result this future represents.
189
190 If the future has been cancelled, raises CancelledError. If the
191 future's result isn't yet available, raises InvalidStateError. If
192 the future is done and has an exception set, this exception is raised.
193 """
194 if self._state == _CANCELLED:
Chris Jerdonekda742ba2020-05-17 22:47:31 -0700195 exc = self._make_cancelled_error()
196 raise exc
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700197 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700198 raise exceptions.InvalidStateError('Result is not ready.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500199 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700200 if self._exception is not None:
201 raise self._exception
202 return self._result
203
204 def exception(self):
205 """Return the exception that was set on this future.
206
207 The exception (or None if no exception was set) is returned only if
208 the future is done. If the future has been cancelled, raises
209 CancelledError. If the future isn't done yet, raises
210 InvalidStateError.
211 """
212 if self._state == _CANCELLED:
Chris Jerdonekda742ba2020-05-17 22:47:31 -0700213 exc = self._make_cancelled_error()
214 raise exc
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700215 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700216 raise exceptions.InvalidStateError('Exception is not set.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500217 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700218 return self._exception
219
Yury Selivanovf23746a2018-01-22 19:11:18 -0500220 def add_done_callback(self, fn, *, context=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700221 """Add a callback to be run when the future becomes done.
222
223 The callback is called with a single argument - the future object. If
224 the future is already done when this is called, the callback is
225 scheduled with call_soon.
226 """
227 if self._state != _PENDING:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500228 self._loop.call_soon(fn, self, context=context)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700229 else:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500230 if context is None:
231 context = contextvars.copy_context()
232 self._callbacks.append((fn, context))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700233
234 # New method not in PEP 3148.
235
236 def remove_done_callback(self, fn):
237 """Remove all instances of a callback from the "call when done" list.
238
239 Returns the number of callbacks removed.
240 """
Yury Selivanovf23746a2018-01-22 19:11:18 -0500241 filtered_callbacks = [(f, ctx)
242 for (f, ctx) in self._callbacks
243 if f != fn]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700244 removed_count = len(self._callbacks) - len(filtered_callbacks)
245 if removed_count:
246 self._callbacks[:] = filtered_callbacks
247 return removed_count
248
249 # So-called internal methods (note: no set_running_or_notify_cancel()).
250
251 def set_result(self, result):
252 """Mark the future done and set its result.
253
254 If the future is already done when this method is called, raises
255 InvalidStateError.
256 """
257 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700258 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700259 self._result = result
260 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500261 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700262
263 def set_exception(self, exception):
264 """Mark the future done and set an exception.
265
266 If the future is already done when this method is called, raises
267 InvalidStateError.
268 """
269 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700270 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Victor Stinner95728982014-01-30 16:01:54 -0800271 if isinstance(exception, type):
272 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500273 if type(exception) is StopIteration:
274 raise TypeError("StopIteration interacts badly with generators "
275 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700276 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700277 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500278 self.__schedule_callbacks()
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500279 self.__log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700280
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500281 def __await__(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700282 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700283 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700284 yield self # This tells Task to wait for completion.
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500285 if not self.done():
286 raise RuntimeError("await wasn't used with future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700287 return self.result() # May raise too.
288
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500289 __iter__ = __await__ # make compatible with 'yield from'.
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400290
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700291
Yury Selivanov01c521b2016-10-23 22:34:35 -0400292# Needed for testing purposes.
293_PyFuture = Future
294
295
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500296def _get_loop(fut):
297 # Tries to call Future.get_loop() if it's available.
298 # Otherwise fallbacks to using the old '_loop' property.
299 try:
300 get_loop = fut.get_loop
301 except AttributeError:
302 pass
303 else:
304 return get_loop()
305 return fut._loop
306
307
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500308def _set_result_unless_cancelled(fut, result):
309 """Helper setting the result only if the future was not cancelled."""
310 if fut.cancelled():
311 return
312 fut.set_result(result)
313
314
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700315def _convert_future_exc(exc):
316 exc_class = type(exc)
317 if exc_class is concurrent.futures.CancelledError:
318 return exceptions.CancelledError(*exc.args)
319 elif exc_class is concurrent.futures.TimeoutError:
320 return exceptions.TimeoutError(*exc.args)
321 elif exc_class is concurrent.futures.InvalidStateError:
322 return exceptions.InvalidStateError(*exc.args)
323 else:
324 return exc
325
326
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700327def _set_concurrent_future_state(concurrent, source):
328 """Copy state from a future to a concurrent.futures.Future."""
329 assert source.done()
330 if source.cancelled():
331 concurrent.cancel()
332 if not concurrent.set_running_or_notify_cancel():
333 return
334 exception = source.exception()
335 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700336 concurrent.set_exception(_convert_future_exc(exception))
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700337 else:
338 result = source.result()
339 concurrent.set_result(result)
340
341
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500342def _copy_future_state(source, dest):
343 """Internal helper to copy state from another Future.
344
345 The other Future may be a concurrent.futures.Future.
346 """
347 assert source.done()
348 if dest.cancelled():
349 return
350 assert not dest.done()
351 if source.cancelled():
352 dest.cancel()
353 else:
354 exception = source.exception()
355 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700356 dest.set_exception(_convert_future_exc(exception))
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500357 else:
358 result = source.result()
359 dest.set_result(result)
360
361
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700362def _chain_future(source, destination):
363 """Chain two futures so that when one completes, so does the other.
364
365 The result (or exception) of source will be copied to destination.
366 If destination is cancelled, source gets cancelled too.
367 Compatible with both asyncio.Future and concurrent.futures.Future.
368 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700369 if not isfuture(source) and not isinstance(source,
370 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700371 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700372 if not isfuture(destination) and not isinstance(destination,
373 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700374 raise TypeError('A future is required for destination argument')
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500375 source_loop = _get_loop(source) if isfuture(source) else None
376 dest_loop = _get_loop(destination) if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700377
378 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700379 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500380 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700381 else:
382 _set_concurrent_future_state(future, other)
383
384 def _call_check_cancel(destination):
385 if destination.cancelled():
386 if source_loop is None or source_loop is dest_loop:
387 source.cancel()
388 else:
389 source_loop.call_soon_threadsafe(source.cancel)
390
391 def _call_set_state(source):
Yury Selivanovfdccfe02018-05-28 17:10:20 -0400392 if (destination.cancelled() and
393 dest_loop is not None and dest_loop.is_closed()):
394 return
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700395 if dest_loop is None or dest_loop is source_loop:
396 _set_state(destination, source)
397 else:
398 dest_loop.call_soon_threadsafe(_set_state, destination, source)
399
400 destination.add_done_callback(_call_check_cancel)
401 source.add_done_callback(_call_set_state)
402
403
404def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700405 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700406 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700407 return future
408 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500409 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400410 if loop is None:
411 loop = events.get_event_loop()
412 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700413 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700414 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900415
416
417try:
418 import _asyncio
419except ImportError:
420 pass
421else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400422 # _CFuture is needed for tests.
423 Future = _CFuture = _asyncio.Future