blob: 889f3e6eb86b03e2e2cdbcc50a3d9ff9779dd6cc [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
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070055
Guido van Rossum1140a032016-09-09 12:54:54 -070056 # This field is used for a dual purpose:
57 # - Its presence is a marker to declare that a class implements
58 # the Future protocol (i.e. is intended to be duck-type compatible).
59 # The value must also be not-None, to enable a subclass to declare
60 # that it is not compatible by setting this to None.
61 # - It is set by __iter__() below so that Task._step() can tell
Andrew Svetlov88743422017-12-11 17:35:49 +020062 # the difference between
63 # `await Future()` or`yield from Future()` (correct) vs.
Guido van Rossum1140a032016-09-09 12:54:54 -070064 # `yield Future()` (incorrect).
65 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070066
Yury Selivanove0aef4f2017-12-25 16:16:10 -050067 __log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070068
69 def __init__(self, *, loop=None):
70 """Initialize the future.
71
Martin Panterc04fb562016-02-10 05:44:01 +000072 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070073 loop object used by the future. If it's not provided, the future uses
74 the default event loop.
75 """
76 if loop is None:
77 self._loop = events.get_event_loop()
78 else:
79 self._loop = loop
80 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020081 if self._loop.get_debug():
Andrew Svetlovf74ef452017-12-15 07:04:38 +020082 self._source_traceback = format_helpers.extract_stack(
83 sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070084
Yury Selivanova0c1ba62016-10-28 12:52:37 -040085 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020086
87 def __repr__(self):
Yury Selivanov6370f342017-12-10 18:36:12 -050088 return '<{} {}>'.format(self.__class__.__name__,
89 ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070090
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090091 def __del__(self):
Yury Selivanove0aef4f2017-12-25 16:16:10 -050092 if not self.__log_traceback:
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090093 # set_exception() was not called, or result() or exception()
94 # has consumed the exception
95 return
96 exc = self._exception
97 context = {
Yury Selivanov6370f342017-12-10 18:36:12 -050098 'message':
99 f'{self.__class__.__name__} exception was never retrieved',
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900100 'exception': exc,
101 'future': self,
102 }
103 if self._source_traceback:
104 context['source_traceback'] = self._source_traceback
105 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100106
Batuhan Taşkayadec36722019-12-07 14:05:07 +0300107 def __class_getitem__(cls, type):
108 return cls
109
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500110 @property
111 def _log_traceback(self):
112 return self.__log_traceback
113
114 @_log_traceback.setter
115 def _log_traceback(self, val):
116 if bool(val):
117 raise ValueError('_log_traceback can only be set to False')
118 self.__log_traceback = False
119
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500120 def get_loop(self):
121 """Return the event loop the Future is bound to."""
Andrew Svetlovdad6be52019-11-13 23:36:46 +0200122 loop = self._loop
123 if loop is None:
124 raise RuntimeError("Future object is not initialized.")
125 return loop
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500126
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700127 def cancel(self, msg=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700128 """Cancel the future and schedule callbacks.
129
130 If the future is already done or cancelled, return False. Otherwise,
131 change the future's state to cancelled, schedule the callbacks and
132 return True.
133 """
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500134 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700135 if self._state != _PENDING:
136 return False
137 self._state = _CANCELLED
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700138 self._cancel_message = msg
Yury Selivanov22feeb82018-01-24 11:31:01 -0500139 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700140 return True
141
Yury Selivanov22feeb82018-01-24 11:31:01 -0500142 def __schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700143 """Internal: Ask the event loop to call all callbacks.
144
145 The callbacks are scheduled to be called as soon as possible. Also
146 clears the callback list.
147 """
148 callbacks = self._callbacks[:]
149 if not callbacks:
150 return
151
152 self._callbacks[:] = []
Yury Selivanovf23746a2018-01-22 19:11:18 -0500153 for callback, ctx in callbacks:
154 self._loop.call_soon(callback, self, context=ctx)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700155
156 def cancelled(self):
157 """Return True if the future was cancelled."""
158 return self._state == _CANCELLED
159
160 # Don't implement running(); see http://bugs.python.org/issue18699
161
162 def done(self):
163 """Return True if the future is done.
164
165 Done means either that a result / exception are available, or that the
166 future was cancelled.
167 """
168 return self._state != _PENDING
169
170 def result(self):
171 """Return the result this future represents.
172
173 If the future has been cancelled, raises CancelledError. If the
174 future's result isn't yet available, raises InvalidStateError. If
175 the future is done and has an exception set, this exception is raised.
176 """
177 if self._state == _CANCELLED:
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700178 raise exceptions.CancelledError(
179 '' if self._cancel_message is None else self._cancel_message)
180
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700181 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700182 raise exceptions.InvalidStateError('Result is not ready.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500183 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700184 if self._exception is not None:
185 raise self._exception
186 return self._result
187
188 def exception(self):
189 """Return the exception that was set on this future.
190
191 The exception (or None if no exception was set) is returned only if
192 the future is done. If the future has been cancelled, raises
193 CancelledError. If the future isn't done yet, raises
194 InvalidStateError.
195 """
196 if self._state == _CANCELLED:
Chris Jerdonek1ce58412020-05-15 16:55:50 -0700197 raise exceptions.CancelledError(
198 '' if self._cancel_message is None else self._cancel_message)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700199 if self._state != _FINISHED:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700200 raise exceptions.InvalidStateError('Exception is not set.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500201 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700202 return self._exception
203
Yury Selivanovf23746a2018-01-22 19:11:18 -0500204 def add_done_callback(self, fn, *, context=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700205 """Add a callback to be run when the future becomes done.
206
207 The callback is called with a single argument - the future object. If
208 the future is already done when this is called, the callback is
209 scheduled with call_soon.
210 """
211 if self._state != _PENDING:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500212 self._loop.call_soon(fn, self, context=context)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700213 else:
Yury Selivanovf23746a2018-01-22 19:11:18 -0500214 if context is None:
215 context = contextvars.copy_context()
216 self._callbacks.append((fn, context))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700217
218 # New method not in PEP 3148.
219
220 def remove_done_callback(self, fn):
221 """Remove all instances of a callback from the "call when done" list.
222
223 Returns the number of callbacks removed.
224 """
Yury Selivanovf23746a2018-01-22 19:11:18 -0500225 filtered_callbacks = [(f, ctx)
226 for (f, ctx) in self._callbacks
227 if f != fn]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700228 removed_count = len(self._callbacks) - len(filtered_callbacks)
229 if removed_count:
230 self._callbacks[:] = filtered_callbacks
231 return removed_count
232
233 # So-called internal methods (note: no set_running_or_notify_cancel()).
234
235 def set_result(self, result):
236 """Mark the future done and set its result.
237
238 If the future is already done when this method is called, raises
239 InvalidStateError.
240 """
241 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700242 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700243 self._result = result
244 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500245 self.__schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700246
247 def set_exception(self, exception):
248 """Mark the future done and set an exception.
249
250 If the future is already done when this method is called, raises
251 InvalidStateError.
252 """
253 if self._state != _PENDING:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700254 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
Victor Stinner95728982014-01-30 16:01:54 -0800255 if isinstance(exception, type):
256 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500257 if type(exception) is StopIteration:
258 raise TypeError("StopIteration interacts badly with generators "
259 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700260 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700261 self._state = _FINISHED
Yury Selivanov22feeb82018-01-24 11:31:01 -0500262 self.__schedule_callbacks()
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500263 self.__log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700264
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500265 def __await__(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700266 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700267 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700268 yield self # This tells Task to wait for completion.
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500269 if not self.done():
270 raise RuntimeError("await wasn't used with future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700271 return self.result() # May raise too.
272
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500273 __iter__ = __await__ # make compatible with 'yield from'.
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400274
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700275
Yury Selivanov01c521b2016-10-23 22:34:35 -0400276# Needed for testing purposes.
277_PyFuture = Future
278
279
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500280def _get_loop(fut):
281 # Tries to call Future.get_loop() if it's available.
282 # Otherwise fallbacks to using the old '_loop' property.
283 try:
284 get_loop = fut.get_loop
285 except AttributeError:
286 pass
287 else:
288 return get_loop()
289 return fut._loop
290
291
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500292def _set_result_unless_cancelled(fut, result):
293 """Helper setting the result only if the future was not cancelled."""
294 if fut.cancelled():
295 return
296 fut.set_result(result)
297
298
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700299def _convert_future_exc(exc):
300 exc_class = type(exc)
301 if exc_class is concurrent.futures.CancelledError:
302 return exceptions.CancelledError(*exc.args)
303 elif exc_class is concurrent.futures.TimeoutError:
304 return exceptions.TimeoutError(*exc.args)
305 elif exc_class is concurrent.futures.InvalidStateError:
306 return exceptions.InvalidStateError(*exc.args)
307 else:
308 return exc
309
310
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700311def _set_concurrent_future_state(concurrent, source):
312 """Copy state from a future to a concurrent.futures.Future."""
313 assert source.done()
314 if source.cancelled():
315 concurrent.cancel()
316 if not concurrent.set_running_or_notify_cancel():
317 return
318 exception = source.exception()
319 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700320 concurrent.set_exception(_convert_future_exc(exception))
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700321 else:
322 result = source.result()
323 concurrent.set_result(result)
324
325
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500326def _copy_future_state(source, dest):
327 """Internal helper to copy state from another Future.
328
329 The other Future may be a concurrent.futures.Future.
330 """
331 assert source.done()
332 if dest.cancelled():
333 return
334 assert not dest.done()
335 if source.cancelled():
336 dest.cancel()
337 else:
338 exception = source.exception()
339 if exception is not None:
Andrew Svetlov0baa72f2018-09-11 10:13:04 -0700340 dest.set_exception(_convert_future_exc(exception))
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500341 else:
342 result = source.result()
343 dest.set_result(result)
344
345
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700346def _chain_future(source, destination):
347 """Chain two futures so that when one completes, so does the other.
348
349 The result (or exception) of source will be copied to destination.
350 If destination is cancelled, source gets cancelled too.
351 Compatible with both asyncio.Future and concurrent.futures.Future.
352 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700353 if not isfuture(source) and not isinstance(source,
354 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700355 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700356 if not isfuture(destination) and not isinstance(destination,
357 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700358 raise TypeError('A future is required for destination argument')
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500359 source_loop = _get_loop(source) if isfuture(source) else None
360 dest_loop = _get_loop(destination) if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700361
362 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700363 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500364 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700365 else:
366 _set_concurrent_future_state(future, other)
367
368 def _call_check_cancel(destination):
369 if destination.cancelled():
370 if source_loop is None or source_loop is dest_loop:
371 source.cancel()
372 else:
373 source_loop.call_soon_threadsafe(source.cancel)
374
375 def _call_set_state(source):
Yury Selivanovfdccfe02018-05-28 17:10:20 -0400376 if (destination.cancelled() and
377 dest_loop is not None and dest_loop.is_closed()):
378 return
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700379 if dest_loop is None or dest_loop is source_loop:
380 _set_state(destination, source)
381 else:
382 dest_loop.call_soon_threadsafe(_set_state, destination, source)
383
384 destination.add_done_callback(_call_check_cancel)
385 source.add_done_callback(_call_set_state)
386
387
388def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700389 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700390 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700391 return future
392 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500393 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400394 if loop is None:
395 loop = events.get_event_loop()
396 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700397 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700398 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900399
400
401try:
402 import _asyncio
403except ImportError:
404 pass
405else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400406 # _CFuture is needed for tests.
407 Future = _CFuture = _asyncio.Future