blob: 1c05b2231c14df79c5a210af8cae190c35e2af05 [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__ = (
4 'CancelledError', 'TimeoutError', 'InvalidStateError',
5 'Future', 'wrap_future', 'isfuture',
6)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07007
Yury Selivanova0c1ba62016-10-28 12:52:37 -04008import concurrent.futures
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 Svetlovf74ef452017-12-15 07:04:38 +020014from . import format_helpers
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070015
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070016
Yury Selivanova0c1ba62016-10-28 12:52:37 -040017CancelledError = base_futures.CancelledError
18InvalidStateError = base_futures.InvalidStateError
19TimeoutError = base_futures.TimeoutError
20isfuture = base_futures.isfuture
21
22
23_PENDING = base_futures._PENDING
24_CANCELLED = base_futures._CANCELLED
25_FINISHED = base_futures._FINISHED
26
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070027
28STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
29
30
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070031class Future:
32 """This class is *almost* compatible with concurrent.futures.Future.
33
34 Differences:
35
Antoine Pitrou22b11282017-11-07 17:03:28 +010036 - This class is not thread-safe.
37
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070038 - result() and exception() do not take a timeout argument and
39 raise an exception when the future isn't done yet.
40
41 - Callbacks registered with add_done_callback() are always called
Antoine Pitrou22b11282017-11-07 17:03:28 +010042 via the event loop's call_soon().
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070043
44 - This class is not compatible with the wait() and as_completed()
45 methods in the concurrent.futures package.
46
47 (In Python 3.4 or later we may be able to unify the implementations.)
48 """
49
50 # Class variables serving as defaults for instance variables.
51 _state = _PENDING
52 _result = None
53 _exception = None
54 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +010055 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070056
Guido van Rossum1140a032016-09-09 12:54:54 -070057 # This field is used for a dual purpose:
58 # - Its presence is a marker to declare that a class implements
59 # the Future protocol (i.e. is intended to be duck-type compatible).
60 # The value must also be not-None, to enable a subclass to declare
61 # that it is not compatible by setting this to None.
62 # - It is set by __iter__() below so that Task._step() can tell
Andrew Svetlov88743422017-12-11 17:35:49 +020063 # the difference between
64 # `await Future()` or`yield from Future()` (correct) vs.
Guido van Rossum1140a032016-09-09 12:54:54 -070065 # `yield Future()` (incorrect).
66 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070067
Yury Selivanove0aef4f2017-12-25 16:16:10 -050068 __log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070069
70 def __init__(self, *, loop=None):
71 """Initialize the future.
72
Martin Panterc04fb562016-02-10 05:44:01 +000073 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070074 loop object used by the future. If it's not provided, the future uses
75 the default event loop.
76 """
77 if loop is None:
78 self._loop = events.get_event_loop()
79 else:
80 self._loop = loop
81 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020082 if self._loop.get_debug():
Andrew Svetlovf74ef452017-12-15 07:04:38 +020083 self._source_traceback = format_helpers.extract_stack(
84 sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070085
Yury Selivanova0c1ba62016-10-28 12:52:37 -040086 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020087
88 def __repr__(self):
Yury Selivanov6370f342017-12-10 18:36:12 -050089 return '<{} {}>'.format(self.__class__.__name__,
90 ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070091
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090092 def __del__(self):
Yury Selivanove0aef4f2017-12-25 16:16:10 -050093 if not self.__log_traceback:
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090094 # set_exception() was not called, or result() or exception()
95 # has consumed the exception
96 return
97 exc = self._exception
98 context = {
Yury Selivanov6370f342017-12-10 18:36:12 -050099 'message':
100 f'{self.__class__.__name__} exception was never retrieved',
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900101 'exception': exc,
102 'future': self,
103 }
104 if self._source_traceback:
105 context['source_traceback'] = self._source_traceback
106 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100107
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500108 @property
109 def _log_traceback(self):
110 return self.__log_traceback
111
112 @_log_traceback.setter
113 def _log_traceback(self, val):
114 if bool(val):
115 raise ValueError('_log_traceback can only be set to False')
116 self.__log_traceback = False
117
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500118 def get_loop(self):
119 """Return the event loop the Future is bound to."""
120 return self._loop
121
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700122 def cancel(self):
123 """Cancel the future and schedule callbacks.
124
125 If the future is already done or cancelled, return False. Otherwise,
126 change the future's state to cancelled, schedule the callbacks and
127 return True.
128 """
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500129 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700130 if self._state != _PENDING:
131 return False
132 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400133 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700134 return True
135
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400136 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700137 """Internal: Ask the event loop to call all callbacks.
138
139 The callbacks are scheduled to be called as soon as possible. Also
140 clears the callback list.
141 """
142 callbacks = self._callbacks[:]
143 if not callbacks:
144 return
145
146 self._callbacks[:] = []
147 for callback in callbacks:
148 self._loop.call_soon(callback, self)
149
150 def cancelled(self):
151 """Return True if the future was cancelled."""
152 return self._state == _CANCELLED
153
154 # Don't implement running(); see http://bugs.python.org/issue18699
155
156 def done(self):
157 """Return True if the future is done.
158
159 Done means either that a result / exception are available, or that the
160 future was cancelled.
161 """
162 return self._state != _PENDING
163
164 def result(self):
165 """Return the result this future represents.
166
167 If the future has been cancelled, raises CancelledError. If the
168 future's result isn't yet available, raises InvalidStateError. If
169 the future is done and has an exception set, this exception is raised.
170 """
171 if self._state == _CANCELLED:
172 raise CancelledError
173 if self._state != _FINISHED:
174 raise InvalidStateError('Result is not ready.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500175 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700176 if self._exception is not None:
177 raise self._exception
178 return self._result
179
180 def exception(self):
181 """Return the exception that was set on this future.
182
183 The exception (or None if no exception was set) is returned only if
184 the future is done. If the future has been cancelled, raises
185 CancelledError. If the future isn't done yet, raises
186 InvalidStateError.
187 """
188 if self._state == _CANCELLED:
189 raise CancelledError
190 if self._state != _FINISHED:
191 raise InvalidStateError('Exception is not set.')
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500192 self.__log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700193 return self._exception
194
195 def add_done_callback(self, fn):
196 """Add a callback to be run when the future becomes done.
197
198 The callback is called with a single argument - the future object. If
199 the future is already done when this is called, the callback is
200 scheduled with call_soon.
201 """
202 if self._state != _PENDING:
203 self._loop.call_soon(fn, self)
204 else:
205 self._callbacks.append(fn)
206
207 # New method not in PEP 3148.
208
209 def remove_done_callback(self, fn):
210 """Remove all instances of a callback from the "call when done" list.
211
212 Returns the number of callbacks removed.
213 """
214 filtered_callbacks = [f for f in self._callbacks if f != fn]
215 removed_count = len(self._callbacks) - len(filtered_callbacks)
216 if removed_count:
217 self._callbacks[:] = filtered_callbacks
218 return removed_count
219
220 # So-called internal methods (note: no set_running_or_notify_cancel()).
221
222 def set_result(self, result):
223 """Mark the future done and set its result.
224
225 If the future is already done when this method is called, raises
226 InvalidStateError.
227 """
228 if self._state != _PENDING:
229 raise InvalidStateError('{}: {!r}'.format(self._state, self))
230 self._result = result
231 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400232 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700233
234 def set_exception(self, exception):
235 """Mark the future done and set an exception.
236
237 If the future is already done when this method is called, raises
238 InvalidStateError.
239 """
240 if self._state != _PENDING:
241 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800242 if isinstance(exception, type):
243 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500244 if type(exception) is StopIteration:
245 raise TypeError("StopIteration interacts badly with generators "
246 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700247 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700248 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400249 self._schedule_callbacks()
Yury Selivanove0aef4f2017-12-25 16:16:10 -0500250 self.__log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700251
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500252 def __await__(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700253 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700254 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700255 yield self # This tells Task to wait for completion.
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500256 if not self.done():
257 raise RuntimeError("await wasn't used with future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700258 return self.result() # May raise too.
259
Yury Selivanov0cf16f92017-12-25 10:48:15 -0500260 __iter__ = __await__ # make compatible with 'yield from'.
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400261
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700262
Yury Selivanov01c521b2016-10-23 22:34:35 -0400263# Needed for testing purposes.
264_PyFuture = Future
265
266
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500267def _get_loop(fut):
268 # Tries to call Future.get_loop() if it's available.
269 # Otherwise fallbacks to using the old '_loop' property.
270 try:
271 get_loop = fut.get_loop
272 except AttributeError:
273 pass
274 else:
275 return get_loop()
276 return fut._loop
277
278
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500279def _set_result_unless_cancelled(fut, result):
280 """Helper setting the result only if the future was not cancelled."""
281 if fut.cancelled():
282 return
283 fut.set_result(result)
284
285
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700286def _set_concurrent_future_state(concurrent, source):
287 """Copy state from a future to a concurrent.futures.Future."""
288 assert source.done()
289 if source.cancelled():
290 concurrent.cancel()
291 if not concurrent.set_running_or_notify_cancel():
292 return
293 exception = source.exception()
294 if exception is not None:
295 concurrent.set_exception(exception)
296 else:
297 result = source.result()
298 concurrent.set_result(result)
299
300
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500301def _copy_future_state(source, dest):
302 """Internal helper to copy state from another Future.
303
304 The other Future may be a concurrent.futures.Future.
305 """
306 assert source.done()
307 if dest.cancelled():
308 return
309 assert not dest.done()
310 if source.cancelled():
311 dest.cancel()
312 else:
313 exception = source.exception()
314 if exception is not None:
315 dest.set_exception(exception)
316 else:
317 result = source.result()
318 dest.set_result(result)
319
320
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700321def _chain_future(source, destination):
322 """Chain two futures so that when one completes, so does the other.
323
324 The result (or exception) of source will be copied to destination.
325 If destination is cancelled, source gets cancelled too.
326 Compatible with both asyncio.Future and concurrent.futures.Future.
327 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700328 if not isfuture(source) and not isinstance(source,
329 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700330 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700331 if not isfuture(destination) and not isinstance(destination,
332 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700333 raise TypeError('A future is required for destination argument')
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500334 source_loop = _get_loop(source) if isfuture(source) else None
335 dest_loop = _get_loop(destination) if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700336
337 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700338 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500339 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700340 else:
341 _set_concurrent_future_state(future, other)
342
343 def _call_check_cancel(destination):
344 if destination.cancelled():
345 if source_loop is None or source_loop is dest_loop:
346 source.cancel()
347 else:
348 source_loop.call_soon_threadsafe(source.cancel)
349
350 def _call_set_state(source):
351 if dest_loop is None or dest_loop is source_loop:
352 _set_state(destination, source)
353 else:
354 dest_loop.call_soon_threadsafe(_set_state, destination, source)
355
356 destination.add_done_callback(_call_check_cancel)
357 source.add_done_callback(_call_set_state)
358
359
360def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700361 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700362 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700363 return future
364 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500365 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400366 if loop is None:
367 loop = events.get_event_loop()
368 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700369 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700370 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900371
372
373try:
374 import _asyncio
375except ImportError:
376 pass
377else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400378 # _CFuture is needed for tests.
379 Future = _CFuture = _asyncio.Future