blob: b310962f9fe19d6be0a509fde1c2ef3ee0a50276 [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
Victor Stinnerc16bace2017-11-28 00:35:33 +010068 _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):
93 if not self._log_traceback:
94 # 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
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700108 def cancel(self):
109 """Cancel the future and schedule callbacks.
110
111 If the future is already done or cancelled, return False. Otherwise,
112 change the future's state to cancelled, schedule the callbacks and
113 return True.
114 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000115 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700116 if self._state != _PENDING:
117 return False
118 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400119 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700120 return True
121
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400122 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700123 """Internal: Ask the event loop to call all callbacks.
124
125 The callbacks are scheduled to be called as soon as possible. Also
126 clears the callback list.
127 """
128 callbacks = self._callbacks[:]
129 if not callbacks:
130 return
131
132 self._callbacks[:] = []
133 for callback in callbacks:
134 self._loop.call_soon(callback, self)
135
136 def cancelled(self):
137 """Return True if the future was cancelled."""
138 return self._state == _CANCELLED
139
140 # Don't implement running(); see http://bugs.python.org/issue18699
141
142 def done(self):
143 """Return True if the future is done.
144
145 Done means either that a result / exception are available, or that the
146 future was cancelled.
147 """
148 return self._state != _PENDING
149
150 def result(self):
151 """Return the result this future represents.
152
153 If the future has been cancelled, raises CancelledError. If the
154 future's result isn't yet available, raises InvalidStateError. If
155 the future is done and has an exception set, this exception is raised.
156 """
157 if self._state == _CANCELLED:
158 raise CancelledError
159 if self._state != _FINISHED:
160 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100161 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700162 if self._exception is not None:
163 raise self._exception
164 return self._result
165
166 def exception(self):
167 """Return the exception that was set on this future.
168
169 The exception (or None if no exception was set) is returned only if
170 the future is done. If the future has been cancelled, raises
171 CancelledError. If the future isn't done yet, raises
172 InvalidStateError.
173 """
174 if self._state == _CANCELLED:
175 raise CancelledError
176 if self._state != _FINISHED:
177 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100178 self._log_traceback = False
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.
Andrew Svetlov88743422017-12-11 17:35:49 +0200242 assert self.done(), "await wasn't used with future"
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700243 return self.result() # May raise too.
244
Yury Selivanov6370f342017-12-10 18:36:12 -0500245 __await__ = __iter__ # make compatible with 'await' expression
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400246
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700247
Yury Selivanov01c521b2016-10-23 22:34:35 -0400248# Needed for testing purposes.
249_PyFuture = Future
250
251
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500252def _set_result_unless_cancelled(fut, result):
253 """Helper setting the result only if the future was not cancelled."""
254 if fut.cancelled():
255 return
256 fut.set_result(result)
257
258
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700259def _set_concurrent_future_state(concurrent, source):
260 """Copy state from a future to a concurrent.futures.Future."""
261 assert source.done()
262 if source.cancelled():
263 concurrent.cancel()
264 if not concurrent.set_running_or_notify_cancel():
265 return
266 exception = source.exception()
267 if exception is not None:
268 concurrent.set_exception(exception)
269 else:
270 result = source.result()
271 concurrent.set_result(result)
272
273
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500274def _copy_future_state(source, dest):
275 """Internal helper to copy state from another Future.
276
277 The other Future may be a concurrent.futures.Future.
278 """
279 assert source.done()
280 if dest.cancelled():
281 return
282 assert not dest.done()
283 if source.cancelled():
284 dest.cancel()
285 else:
286 exception = source.exception()
287 if exception is not None:
288 dest.set_exception(exception)
289 else:
290 result = source.result()
291 dest.set_result(result)
292
293
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700294def _chain_future(source, destination):
295 """Chain two futures so that when one completes, so does the other.
296
297 The result (or exception) of source will be copied to destination.
298 If destination is cancelled, source gets cancelled too.
299 Compatible with both asyncio.Future and concurrent.futures.Future.
300 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700301 if not isfuture(source) and not isinstance(source,
302 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700303 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700304 if not isfuture(destination) and not isinstance(destination,
305 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700306 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700307 source_loop = source._loop if isfuture(source) else None
308 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700309
310 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700311 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500312 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700313 else:
314 _set_concurrent_future_state(future, other)
315
316 def _call_check_cancel(destination):
317 if destination.cancelled():
318 if source_loop is None or source_loop is dest_loop:
319 source.cancel()
320 else:
321 source_loop.call_soon_threadsafe(source.cancel)
322
323 def _call_set_state(source):
324 if dest_loop is None or dest_loop is source_loop:
325 _set_state(destination, source)
326 else:
327 dest_loop.call_soon_threadsafe(_set_state, destination, source)
328
329 destination.add_done_callback(_call_check_cancel)
330 source.add_done_callback(_call_set_state)
331
332
333def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700334 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700335 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700336 return future
337 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500338 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400339 if loop is None:
340 loop = events.get_event_loop()
341 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700342 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700343 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900344
345
346try:
347 import _asyncio
348except ImportError:
349 pass
350else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400351 # _CFuture is needed for tests.
352 Future = _CFuture = _asyncio.Future