blob: 7b6204a626f864e42c315186bf4ea231ddb11019 [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""A Future class similar to the one in PEP 3148."""
2
Yury Selivanova0c1ba62016-10-28 12:52:37 -04003__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError',
4 'Future', 'wrap_future', 'isfuture']
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07005
Yury Selivanova0c1ba62016-10-28 12:52:37 -04006import concurrent.futures
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07007import logging
Victor Stinner4c3c6992013-12-19 22:42:40 +01008import sys
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07009import traceback
10
Yury Selivanova0c1ba62016-10-28 12:52:37 -040011from . import base_futures
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070012from . import events
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070013
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070014
Yury Selivanova0c1ba62016-10-28 12:52:37 -040015CancelledError = base_futures.CancelledError
16InvalidStateError = base_futures.InvalidStateError
17TimeoutError = base_futures.TimeoutError
18isfuture = 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
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070054
Guido van Rossum1140a032016-09-09 12:54:54 -070055 # This field is used for a dual purpose:
56 # - Its presence is a marker to declare that a class implements
57 # the Future protocol (i.e. is intended to be duck-type compatible).
58 # The value must also be not-None, to enable a subclass to declare
59 # that it is not compatible by setting this to None.
60 # - It is set by __iter__() below so that Task._step() can tell
61 # the difference between `yield from Future()` (correct) vs.
62 # `yield Future()` (incorrect).
63 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070064
Victor Stinnerc16bace2017-11-28 00:35:33 +010065 _log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070066
67 def __init__(self, *, loop=None):
68 """Initialize the future.
69
Martin Panterc04fb562016-02-10 05:44:01 +000070 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070071 loop object used by the future. If it's not provided, the future uses
72 the default event loop.
73 """
74 if loop is None:
75 self._loop = events.get_event_loop()
76 else:
77 self._loop = loop
78 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020079 if self._loop.get_debug():
Antoine Pitrou921e9432017-11-07 17:23:29 +010080 self._source_traceback = events.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070081
Yury Selivanova0c1ba62016-10-28 12:52:37 -040082 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020083
84 def __repr__(self):
INADA Naoki9e4e38e2016-10-09 14:44:47 +090085 return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070086
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090087 def __del__(self):
88 if not self._log_traceback:
89 # set_exception() was not called, or result() or exception()
90 # has consumed the exception
91 return
92 exc = self._exception
93 context = {
94 'message': ('%s exception was never retrieved'
95 % self.__class__.__name__),
96 'exception': exc,
97 'future': self,
98 }
99 if self._source_traceback:
100 context['source_traceback'] = self._source_traceback
101 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100102
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700103 def cancel(self):
104 """Cancel the future and schedule callbacks.
105
106 If the future is already done or cancelled, return False. Otherwise,
107 change the future's state to cancelled, schedule the callbacks and
108 return True.
109 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000110 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700111 if self._state != _PENDING:
112 return False
113 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400114 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700115 return True
116
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400117 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700118 """Internal: Ask the event loop to call all callbacks.
119
120 The callbacks are scheduled to be called as soon as possible. Also
121 clears the callback list.
122 """
123 callbacks = self._callbacks[:]
124 if not callbacks:
125 return
126
127 self._callbacks[:] = []
128 for callback in callbacks:
129 self._loop.call_soon(callback, self)
130
131 def cancelled(self):
132 """Return True if the future was cancelled."""
133 return self._state == _CANCELLED
134
135 # Don't implement running(); see http://bugs.python.org/issue18699
136
137 def done(self):
138 """Return True if the future is done.
139
140 Done means either that a result / exception are available, or that the
141 future was cancelled.
142 """
143 return self._state != _PENDING
144
145 def result(self):
146 """Return the result this future represents.
147
148 If the future has been cancelled, raises CancelledError. If the
149 future's result isn't yet available, raises InvalidStateError. If
150 the future is done and has an exception set, this exception is raised.
151 """
152 if self._state == _CANCELLED:
153 raise CancelledError
154 if self._state != _FINISHED:
155 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100156 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700157 if self._exception is not None:
158 raise self._exception
159 return self._result
160
161 def exception(self):
162 """Return the exception that was set on this future.
163
164 The exception (or None if no exception was set) is returned only if
165 the future is done. If the future has been cancelled, raises
166 CancelledError. If the future isn't done yet, raises
167 InvalidStateError.
168 """
169 if self._state == _CANCELLED:
170 raise CancelledError
171 if self._state != _FINISHED:
172 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100173 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700174 return self._exception
175
176 def add_done_callback(self, fn):
177 """Add a callback to be run when the future becomes done.
178
179 The callback is called with a single argument - the future object. If
180 the future is already done when this is called, the callback is
181 scheduled with call_soon.
182 """
183 if self._state != _PENDING:
184 self._loop.call_soon(fn, self)
185 else:
186 self._callbacks.append(fn)
187
188 # New method not in PEP 3148.
189
190 def remove_done_callback(self, fn):
191 """Remove all instances of a callback from the "call when done" list.
192
193 Returns the number of callbacks removed.
194 """
195 filtered_callbacks = [f for f in self._callbacks if f != fn]
196 removed_count = len(self._callbacks) - len(filtered_callbacks)
197 if removed_count:
198 self._callbacks[:] = filtered_callbacks
199 return removed_count
200
201 # So-called internal methods (note: no set_running_or_notify_cancel()).
202
203 def set_result(self, result):
204 """Mark the future done and set its result.
205
206 If the future is already done when this method is called, raises
207 InvalidStateError.
208 """
209 if self._state != _PENDING:
210 raise InvalidStateError('{}: {!r}'.format(self._state, self))
211 self._result = result
212 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400213 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700214
215 def set_exception(self, exception):
216 """Mark the future done and set an exception.
217
218 If the future is already done when this method is called, raises
219 InvalidStateError.
220 """
221 if self._state != _PENDING:
222 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800223 if isinstance(exception, type):
224 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500225 if type(exception) is StopIteration:
226 raise TypeError("StopIteration interacts badly with generators "
227 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700228 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700229 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400230 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900231 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700232
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700233 def __iter__(self):
234 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700235 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700236 yield self # This tells Task to wait for completion.
237 assert self.done(), "yield from wasn't used with future"
238 return self.result() # May raise too.
239
Victor Stinner3f438a92017-11-28 14:43:52 +0100240 __await__ = __iter__ # make compatible with 'await' expression
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400241
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700242
Yury Selivanov01c521b2016-10-23 22:34:35 -0400243# Needed for testing purposes.
244_PyFuture = Future
245
246
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500247def _set_result_unless_cancelled(fut, result):
248 """Helper setting the result only if the future was not cancelled."""
249 if fut.cancelled():
250 return
251 fut.set_result(result)
252
253
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700254def _set_concurrent_future_state(concurrent, source):
255 """Copy state from a future to a concurrent.futures.Future."""
256 assert source.done()
257 if source.cancelled():
258 concurrent.cancel()
259 if not concurrent.set_running_or_notify_cancel():
260 return
261 exception = source.exception()
262 if exception is not None:
263 concurrent.set_exception(exception)
264 else:
265 result = source.result()
266 concurrent.set_result(result)
267
268
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500269def _copy_future_state(source, dest):
270 """Internal helper to copy state from another Future.
271
272 The other Future may be a concurrent.futures.Future.
273 """
274 assert source.done()
275 if dest.cancelled():
276 return
277 assert not dest.done()
278 if source.cancelled():
279 dest.cancel()
280 else:
281 exception = source.exception()
282 if exception is not None:
283 dest.set_exception(exception)
284 else:
285 result = source.result()
286 dest.set_result(result)
287
288
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700289def _chain_future(source, destination):
290 """Chain two futures so that when one completes, so does the other.
291
292 The result (or exception) of source will be copied to destination.
293 If destination is cancelled, source gets cancelled too.
294 Compatible with both asyncio.Future and concurrent.futures.Future.
295 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700296 if not isfuture(source) and not isinstance(source,
297 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700298 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700299 if not isfuture(destination) and not isinstance(destination,
300 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700301 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700302 source_loop = source._loop if isfuture(source) else None
303 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700304
305 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700306 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500307 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700308 else:
309 _set_concurrent_future_state(future, other)
310
311 def _call_check_cancel(destination):
312 if destination.cancelled():
313 if source_loop is None or source_loop is dest_loop:
314 source.cancel()
315 else:
316 source_loop.call_soon_threadsafe(source.cancel)
317
318 def _call_set_state(source):
319 if dest_loop is None or dest_loop is source_loop:
320 _set_state(destination, source)
321 else:
322 dest_loop.call_soon_threadsafe(_set_state, destination, source)
323
324 destination.add_done_callback(_call_check_cancel)
325 source.add_done_callback(_call_set_state)
326
327
328def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700329 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700330 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700331 return future
332 assert isinstance(future, concurrent.futures.Future), \
333 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400334 if loop is None:
335 loop = events.get_event_loop()
336 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700337 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700338 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900339
340
341try:
342 import _asyncio
343except ImportError:
344 pass
345else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400346 # _CFuture is needed for tests.
347 Future = _CFuture = _asyncio.Future