blob: b2e57efdb66cbfaa89bb49d69451ed4017aee3a1 [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
Antoine Pitrou22b11282017-11-07 17:03:28 +010035 - This class is not thread-safe.
36
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070037 - result() and exception() do not take a timeout argument and
38 raise an exception when the future isn't done yet.
39
40 - Callbacks registered with add_done_callback() are always called
Antoine Pitrou22b11282017-11-07 17:03:28 +010041 via the event loop's call_soon().
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070042
43 - This class is not compatible with the wait() and as_completed()
44 methods in the concurrent.futures package.
45
46 (In Python 3.4 or later we may be able to unify the implementations.)
47 """
48
49 # Class variables serving as defaults for instance variables.
50 _state = _PENDING
51 _result = None
52 _exception = None
53 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +010054 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070055
Guido van Rossum1140a032016-09-09 12:54:54 -070056 # This field is used for a dual purpose:
57 # - Its presence is a marker to declare that a class implements
58 # the Future protocol (i.e. is intended to be duck-type compatible).
59 # The value must also be not-None, to enable a subclass to declare
60 # that it is not compatible by setting this to None.
61 # - It is set by __iter__() below so that Task._step() can tell
62 # the difference between `yield from Future()` (correct) vs.
63 # `yield Future()` (incorrect).
64 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070065
Victor Stinnerc16bace2017-11-28 00:35:33 +010066 _log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070067
68 def __init__(self, *, loop=None):
69 """Initialize the future.
70
Martin Panterc04fb562016-02-10 05:44:01 +000071 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070072 loop object used by the future. If it's not provided, the future uses
73 the default event loop.
74 """
75 if loop is None:
76 self._loop = events.get_event_loop()
77 else:
78 self._loop = loop
79 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020080 if self._loop.get_debug():
Antoine Pitrou921e9432017-11-07 17:23:29 +010081 self._source_traceback = events.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070082
Yury Selivanova0c1ba62016-10-28 12:52:37 -040083 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020084
85 def __repr__(self):
INADA Naoki9e4e38e2016-10-09 14:44:47 +090086 return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070087
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090088 def __del__(self):
89 if not self._log_traceback:
90 # set_exception() was not called, or result() or exception()
91 # has consumed the exception
92 return
93 exc = self._exception
94 context = {
95 'message': ('%s exception was never retrieved'
96 % self.__class__.__name__),
97 'exception': exc,
98 'future': self,
99 }
100 if self._source_traceback:
101 context['source_traceback'] = self._source_traceback
102 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100103
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700104 def cancel(self):
105 """Cancel the future and schedule callbacks.
106
107 If the future is already done or cancelled, return False. Otherwise,
108 change the future's state to cancelled, schedule the callbacks and
109 return True.
110 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000111 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700112 if self._state != _PENDING:
113 return False
114 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400115 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700116 return True
117
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400118 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700119 """Internal: Ask the event loop to call all callbacks.
120
121 The callbacks are scheduled to be called as soon as possible. Also
122 clears the callback list.
123 """
124 callbacks = self._callbacks[:]
125 if not callbacks:
126 return
127
128 self._callbacks[:] = []
129 for callback in callbacks:
130 self._loop.call_soon(callback, self)
131
132 def cancelled(self):
133 """Return True if the future was cancelled."""
134 return self._state == _CANCELLED
135
136 # Don't implement running(); see http://bugs.python.org/issue18699
137
138 def done(self):
139 """Return True if the future is done.
140
141 Done means either that a result / exception are available, or that the
142 future was cancelled.
143 """
144 return self._state != _PENDING
145
146 def result(self):
147 """Return the result this future represents.
148
149 If the future has been cancelled, raises CancelledError. If the
150 future's result isn't yet available, raises InvalidStateError. If
151 the future is done and has an exception set, this exception is raised.
152 """
153 if self._state == _CANCELLED:
154 raise CancelledError
155 if self._state != _FINISHED:
156 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100157 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700158 if self._exception is not None:
159 raise self._exception
160 return self._result
161
162 def exception(self):
163 """Return the exception that was set on this future.
164
165 The exception (or None if no exception was set) is returned only if
166 the future is done. If the future has been cancelled, raises
167 CancelledError. If the future isn't done yet, raises
168 InvalidStateError.
169 """
170 if self._state == _CANCELLED:
171 raise CancelledError
172 if self._state != _FINISHED:
173 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100174 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700175 return self._exception
176
177 def add_done_callback(self, fn):
178 """Add a callback to be run when the future becomes done.
179
180 The callback is called with a single argument - the future object. If
181 the future is already done when this is called, the callback is
182 scheduled with call_soon.
183 """
184 if self._state != _PENDING:
185 self._loop.call_soon(fn, self)
186 else:
187 self._callbacks.append(fn)
188
189 # New method not in PEP 3148.
190
191 def remove_done_callback(self, fn):
192 """Remove all instances of a callback from the "call when done" list.
193
194 Returns the number of callbacks removed.
195 """
196 filtered_callbacks = [f for f in self._callbacks if f != fn]
197 removed_count = len(self._callbacks) - len(filtered_callbacks)
198 if removed_count:
199 self._callbacks[:] = filtered_callbacks
200 return removed_count
201
202 # So-called internal methods (note: no set_running_or_notify_cancel()).
203
204 def set_result(self, result):
205 """Mark the future done and set its result.
206
207 If the future is already done when this method is called, raises
208 InvalidStateError.
209 """
210 if self._state != _PENDING:
211 raise InvalidStateError('{}: {!r}'.format(self._state, self))
212 self._result = result
213 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400214 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700215
216 def set_exception(self, exception):
217 """Mark the future done and set an exception.
218
219 If the future is already done when this method is called, raises
220 InvalidStateError.
221 """
222 if self._state != _PENDING:
223 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800224 if isinstance(exception, type):
225 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500226 if type(exception) is StopIteration:
227 raise TypeError("StopIteration interacts badly with generators "
228 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700229 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700230 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400231 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900232 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700233
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700234 def __iter__(self):
235 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700236 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700237 yield self # This tells Task to wait for completion.
238 assert self.done(), "yield from wasn't used with future"
239 return self.result() # May raise too.
240
Victor Stinner71080fc2015-07-25 02:23:21 +0200241 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400242 __await__ = __iter__ # make compatible with 'await' expression
243
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700244
Yury Selivanov01c521b2016-10-23 22:34:35 -0400245# Needed for testing purposes.
246_PyFuture = Future
247
248
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500249def _set_result_unless_cancelled(fut, result):
250 """Helper setting the result only if the future was not cancelled."""
251 if fut.cancelled():
252 return
253 fut.set_result(result)
254
255
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700256def _set_concurrent_future_state(concurrent, source):
257 """Copy state from a future to a concurrent.futures.Future."""
258 assert source.done()
259 if source.cancelled():
260 concurrent.cancel()
261 if not concurrent.set_running_or_notify_cancel():
262 return
263 exception = source.exception()
264 if exception is not None:
265 concurrent.set_exception(exception)
266 else:
267 result = source.result()
268 concurrent.set_result(result)
269
270
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500271def _copy_future_state(source, dest):
272 """Internal helper to copy state from another Future.
273
274 The other Future may be a concurrent.futures.Future.
275 """
276 assert source.done()
277 if dest.cancelled():
278 return
279 assert not dest.done()
280 if source.cancelled():
281 dest.cancel()
282 else:
283 exception = source.exception()
284 if exception is not None:
285 dest.set_exception(exception)
286 else:
287 result = source.result()
288 dest.set_result(result)
289
290
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700291def _chain_future(source, destination):
292 """Chain two futures so that when one completes, so does the other.
293
294 The result (or exception) of source will be copied to destination.
295 If destination is cancelled, source gets cancelled too.
296 Compatible with both asyncio.Future and concurrent.futures.Future.
297 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700298 if not isfuture(source) and not isinstance(source,
299 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700300 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700301 if not isfuture(destination) and not isinstance(destination,
302 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700303 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700304 source_loop = source._loop if isfuture(source) else None
305 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700306
307 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700308 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500309 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700310 else:
311 _set_concurrent_future_state(future, other)
312
313 def _call_check_cancel(destination):
314 if destination.cancelled():
315 if source_loop is None or source_loop is dest_loop:
316 source.cancel()
317 else:
318 source_loop.call_soon_threadsafe(source.cancel)
319
320 def _call_set_state(source):
321 if dest_loop is None or dest_loop is source_loop:
322 _set_state(destination, source)
323 else:
324 dest_loop.call_soon_threadsafe(_set_state, destination, source)
325
326 destination.add_done_callback(_call_check_cancel)
327 source.add_done_callback(_call_set_state)
328
329
330def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700331 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700332 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700333 return future
334 assert isinstance(future, concurrent.futures.Future), \
335 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400336 if loop is None:
337 loop = events.get_event_loop()
338 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700339 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700340 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900341
342
343try:
344 import _asyncio
345except ImportError:
346 pass
347else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400348 # _CFuture is needed for tests.
349 Future = _CFuture = _asyncio.Future