blob: 215f72d1910ecf7bbbd26af1e8784ae01b1bee4f [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
Victor Stinner71080fc2015-07-25 02:23:21 +020012from . import compat
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070013from . import events
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070014
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070015
Yury Selivanova0c1ba62016-10-28 12:52:37 -040016CancelledError = base_futures.CancelledError
17InvalidStateError = base_futures.InvalidStateError
18TimeoutError = base_futures.TimeoutError
19isfuture = base_futures.isfuture
20
21
22_PENDING = base_futures._PENDING
23_CANCELLED = base_futures._CANCELLED
24_FINISHED = base_futures._FINISHED
25
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070026
27STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
28
29
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070030class Future:
31 """This class is *almost* compatible with concurrent.futures.Future.
32
33 Differences:
34
35 - result() and exception() do not take a timeout argument and
36 raise an exception when the future isn't done yet.
37
38 - Callbacks registered with add_done_callback() are always called
39 via the event loop's call_soon_threadsafe().
40
41 - This class is not compatible with the wait() and as_completed()
42 methods in the concurrent.futures package.
43
44 (In Python 3.4 or later we may be able to unify the implementations.)
45 """
46
47 # Class variables serving as defaults for instance variables.
48 _state = _PENDING
49 _result = None
50 _exception = None
51 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +010052 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070053
Guido van Rossum1140a032016-09-09 12:54:54 -070054 # This field is used for a dual purpose:
55 # - Its presence is a marker to declare that a class implements
56 # the Future protocol (i.e. is intended to be duck-type compatible).
57 # The value must also be not-None, to enable a subclass to declare
58 # that it is not compatible by setting this to None.
59 # - It is set by __iter__() below so that Task._step() can tell
60 # the difference between `yield from Future()` (correct) vs.
61 # `yield Future()` (incorrect).
62 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070063
Victor Stinnere40c0782013-12-21 00:19:33 +010064 _log_traceback = False # Used for Python 3.4 and later
65 _tb_logger = None # Used for Python 3.3 only
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():
80 self._source_traceback = traceback.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._tb_logger is not None:
158 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100159 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700160 if self._exception is not None:
161 raise self._exception
162 return self._result
163
164 def exception(self):
165 """Return the exception that was set on this future.
166
167 The exception (or None if no exception was set) is returned only if
168 the future is done. If the future has been cancelled, raises
169 CancelledError. If the future isn't done yet, raises
170 InvalidStateError.
171 """
172 if self._state == _CANCELLED:
173 raise CancelledError
174 if self._state != _FINISHED:
175 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100176 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700177 if self._tb_logger is not None:
178 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100179 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700180 return self._exception
181
182 def add_done_callback(self, fn):
183 """Add a callback to be run when the future becomes done.
184
185 The callback is called with a single argument - the future object. If
186 the future is already done when this is called, the callback is
187 scheduled with call_soon.
188 """
189 if self._state != _PENDING:
190 self._loop.call_soon(fn, self)
191 else:
192 self._callbacks.append(fn)
193
194 # New method not in PEP 3148.
195
196 def remove_done_callback(self, fn):
197 """Remove all instances of a callback from the "call when done" list.
198
199 Returns the number of callbacks removed.
200 """
201 filtered_callbacks = [f for f in self._callbacks if f != fn]
202 removed_count = len(self._callbacks) - len(filtered_callbacks)
203 if removed_count:
204 self._callbacks[:] = filtered_callbacks
205 return removed_count
206
207 # So-called internal methods (note: no set_running_or_notify_cancel()).
208
209 def set_result(self, result):
210 """Mark the future done and set its result.
211
212 If the future is already done when this method is called, raises
213 InvalidStateError.
214 """
215 if self._state != _PENDING:
216 raise InvalidStateError('{}: {!r}'.format(self._state, self))
217 self._result = result
218 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400219 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700220
221 def set_exception(self, exception):
222 """Mark the future done and set an exception.
223
224 If the future is already done when this method is called, raises
225 InvalidStateError.
226 """
227 if self._state != _PENDING:
228 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800229 if isinstance(exception, type):
230 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500231 if type(exception) is StopIteration:
232 raise TypeError("StopIteration interacts badly with generators "
233 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700234 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700235 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400236 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900237 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700238
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700239 def __iter__(self):
240 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700241 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700242 yield self # This tells Task to wait for completion.
243 assert self.done(), "yield from wasn't used with future"
244 return self.result() # May raise too.
245
Victor Stinner71080fc2015-07-25 02:23:21 +0200246 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400247 __await__ = __iter__ # make compatible with 'await' expression
248
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700249
Yury Selivanov01c521b2016-10-23 22:34:35 -0400250# Needed for testing purposes.
251_PyFuture = Future
252
253
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500254def _set_result_unless_cancelled(fut, result):
255 """Helper setting the result only if the future was not cancelled."""
256 if fut.cancelled():
257 return
258 fut.set_result(result)
259
260
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700261def _set_concurrent_future_state(concurrent, source):
262 """Copy state from a future to a concurrent.futures.Future."""
263 assert source.done()
264 if source.cancelled():
265 concurrent.cancel()
266 if not concurrent.set_running_or_notify_cancel():
267 return
268 exception = source.exception()
269 if exception is not None:
270 concurrent.set_exception(exception)
271 else:
272 result = source.result()
273 concurrent.set_result(result)
274
275
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500276def _copy_future_state(source, dest):
277 """Internal helper to copy state from another Future.
278
279 The other Future may be a concurrent.futures.Future.
280 """
281 assert source.done()
282 if dest.cancelled():
283 return
284 assert not dest.done()
285 if source.cancelled():
286 dest.cancel()
287 else:
288 exception = source.exception()
289 if exception is not None:
290 dest.set_exception(exception)
291 else:
292 result = source.result()
293 dest.set_result(result)
294
295
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700296def _chain_future(source, destination):
297 """Chain two futures so that when one completes, so does the other.
298
299 The result (or exception) of source will be copied to destination.
300 If destination is cancelled, source gets cancelled too.
301 Compatible with both asyncio.Future and concurrent.futures.Future.
302 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700303 if not isfuture(source) and not isinstance(source,
304 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700305 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700306 if not isfuture(destination) and not isinstance(destination,
307 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700308 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700309 source_loop = source._loop if isfuture(source) else None
310 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700311
312 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700313 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500314 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700315 else:
316 _set_concurrent_future_state(future, other)
317
318 def _call_check_cancel(destination):
319 if destination.cancelled():
320 if source_loop is None or source_loop is dest_loop:
321 source.cancel()
322 else:
323 source_loop.call_soon_threadsafe(source.cancel)
324
325 def _call_set_state(source):
326 if dest_loop is None or dest_loop is source_loop:
327 _set_state(destination, source)
328 else:
329 dest_loop.call_soon_threadsafe(_set_state, destination, source)
330
331 destination.add_done_callback(_call_check_cancel)
332 source.add_done_callback(_call_set_state)
333
334
335def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700336 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700337 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700338 return future
339 assert isinstance(future, concurrent.futures.Future), \
340 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400341 if loop is None:
342 loop = events.get_event_loop()
343 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700344 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700345 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900346
347
348try:
349 import _asyncio
350except ImportError:
351 pass
352else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400353 # _CFuture is needed for tests.
354 Future = _CFuture = _asyncio.Future