blob: 24843c016a7e07e501d9fbb0892de33cc7fb7740 [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
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500108 def get_loop(self):
109 """Return the event loop the Future is bound to."""
110 return self._loop
111
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700112 def cancel(self):
113 """Cancel the future and schedule callbacks.
114
115 If the future is already done or cancelled, return False. Otherwise,
116 change the future's state to cancelled, schedule the callbacks and
117 return True.
118 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000119 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700120 if self._state != _PENDING:
121 return False
122 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400123 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700124 return True
125
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400126 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700127 """Internal: Ask the event loop to call all callbacks.
128
129 The callbacks are scheduled to be called as soon as possible. Also
130 clears the callback list.
131 """
132 callbacks = self._callbacks[:]
133 if not callbacks:
134 return
135
136 self._callbacks[:] = []
137 for callback in callbacks:
138 self._loop.call_soon(callback, self)
139
140 def cancelled(self):
141 """Return True if the future was cancelled."""
142 return self._state == _CANCELLED
143
144 # Don't implement running(); see http://bugs.python.org/issue18699
145
146 def done(self):
147 """Return True if the future is done.
148
149 Done means either that a result / exception are available, or that the
150 future was cancelled.
151 """
152 return self._state != _PENDING
153
154 def result(self):
155 """Return the result this future represents.
156
157 If the future has been cancelled, raises CancelledError. If the
158 future's result isn't yet available, raises InvalidStateError. If
159 the future is done and has an exception set, this exception is raised.
160 """
161 if self._state == _CANCELLED:
162 raise CancelledError
163 if self._state != _FINISHED:
164 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100165 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700166 if self._exception is not None:
167 raise self._exception
168 return self._result
169
170 def exception(self):
171 """Return the exception that was set on this future.
172
173 The exception (or None if no exception was set) is returned only if
174 the future is done. If the future has been cancelled, raises
175 CancelledError. If the future isn't done yet, raises
176 InvalidStateError.
177 """
178 if self._state == _CANCELLED:
179 raise CancelledError
180 if self._state != _FINISHED:
181 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100182 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700183 return self._exception
184
185 def add_done_callback(self, fn):
186 """Add a callback to be run when the future becomes done.
187
188 The callback is called with a single argument - the future object. If
189 the future is already done when this is called, the callback is
190 scheduled with call_soon.
191 """
192 if self._state != _PENDING:
193 self._loop.call_soon(fn, self)
194 else:
195 self._callbacks.append(fn)
196
197 # New method not in PEP 3148.
198
199 def remove_done_callback(self, fn):
200 """Remove all instances of a callback from the "call when done" list.
201
202 Returns the number of callbacks removed.
203 """
204 filtered_callbacks = [f for f in self._callbacks if f != fn]
205 removed_count = len(self._callbacks) - len(filtered_callbacks)
206 if removed_count:
207 self._callbacks[:] = filtered_callbacks
208 return removed_count
209
210 # So-called internal methods (note: no set_running_or_notify_cancel()).
211
212 def set_result(self, result):
213 """Mark the future done and set its result.
214
215 If the future is already done when this method is called, raises
216 InvalidStateError.
217 """
218 if self._state != _PENDING:
219 raise InvalidStateError('{}: {!r}'.format(self._state, self))
220 self._result = result
221 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400222 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700223
224 def set_exception(self, exception):
225 """Mark the future done and set an exception.
226
227 If the future is already done when this method is called, raises
228 InvalidStateError.
229 """
230 if self._state != _PENDING:
231 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800232 if isinstance(exception, type):
233 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500234 if type(exception) is StopIteration:
235 raise TypeError("StopIteration interacts badly with generators "
236 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700237 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700238 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400239 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900240 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700242 def __iter__(self):
243 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700244 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700245 yield self # This tells Task to wait for completion.
Andrew Svetlov88743422017-12-11 17:35:49 +0200246 assert self.done(), "await wasn't used with future"
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700247 return self.result() # May raise too.
248
Yury Selivanov6370f342017-12-10 18:36:12 -0500249 __await__ = __iter__ # make compatible with 'await' expression
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400250
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700251
Yury Selivanov01c521b2016-10-23 22:34:35 -0400252# Needed for testing purposes.
253_PyFuture = Future
254
255
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500256def _get_loop(fut):
257 # Tries to call Future.get_loop() if it's available.
258 # Otherwise fallbacks to using the old '_loop' property.
259 try:
260 get_loop = fut.get_loop
261 except AttributeError:
262 pass
263 else:
264 return get_loop()
265 return fut._loop
266
267
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500268def _set_result_unless_cancelled(fut, result):
269 """Helper setting the result only if the future was not cancelled."""
270 if fut.cancelled():
271 return
272 fut.set_result(result)
273
274
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700275def _set_concurrent_future_state(concurrent, source):
276 """Copy state from a future to a concurrent.futures.Future."""
277 assert source.done()
278 if source.cancelled():
279 concurrent.cancel()
280 if not concurrent.set_running_or_notify_cancel():
281 return
282 exception = source.exception()
283 if exception is not None:
284 concurrent.set_exception(exception)
285 else:
286 result = source.result()
287 concurrent.set_result(result)
288
289
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500290def _copy_future_state(source, dest):
291 """Internal helper to copy state from another Future.
292
293 The other Future may be a concurrent.futures.Future.
294 """
295 assert source.done()
296 if dest.cancelled():
297 return
298 assert not dest.done()
299 if source.cancelled():
300 dest.cancel()
301 else:
302 exception = source.exception()
303 if exception is not None:
304 dest.set_exception(exception)
305 else:
306 result = source.result()
307 dest.set_result(result)
308
309
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700310def _chain_future(source, destination):
311 """Chain two futures so that when one completes, so does the other.
312
313 The result (or exception) of source will be copied to destination.
314 If destination is cancelled, source gets cancelled too.
315 Compatible with both asyncio.Future and concurrent.futures.Future.
316 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700317 if not isfuture(source) and not isinstance(source,
318 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700319 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700320 if not isfuture(destination) and not isinstance(destination,
321 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700322 raise TypeError('A future is required for destination argument')
Yury Selivanovca9b36c2017-12-23 15:04:15 -0500323 source_loop = _get_loop(source) if isfuture(source) else None
324 dest_loop = _get_loop(destination) if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700325
326 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700327 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500328 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700329 else:
330 _set_concurrent_future_state(future, other)
331
332 def _call_check_cancel(destination):
333 if destination.cancelled():
334 if source_loop is None or source_loop is dest_loop:
335 source.cancel()
336 else:
337 source_loop.call_soon_threadsafe(source.cancel)
338
339 def _call_set_state(source):
340 if dest_loop is None or dest_loop is source_loop:
341 _set_state(destination, source)
342 else:
343 dest_loop.call_soon_threadsafe(_set_state, destination, source)
344
345 destination.add_done_callback(_call_check_cancel)
346 source.add_done_callback(_call_set_state)
347
348
349def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700350 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700351 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700352 return future
353 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500354 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400355 if loop is None:
356 loop = events.get_event_loop()
357 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700358 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700359 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900360
361
362try:
363 import _asyncio
364except ImportError:
365 pass
366else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400367 # _CFuture is needed for tests.
368 Future = _CFuture = _asyncio.Future