blob: 472f2a8c74e860b4918dd99bb2cdc064e200fe05 [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 Stinnere40c0782013-12-21 00:19:33 +010066 _log_traceback = False # Used for Python 3.4 and later
67 _tb_logger = None # Used for Python 3.3 only
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070068
69 def __init__(self, *, loop=None):
70 """Initialize the future.
71
Martin Panterc04fb562016-02-10 05:44:01 +000072 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070073 loop object used by the future. If it's not provided, the future uses
74 the default event loop.
75 """
76 if loop is None:
77 self._loop = events.get_event_loop()
78 else:
79 self._loop = loop
80 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +020081 if self._loop.get_debug():
Antoine Pitrou921e9432017-11-07 17:23:29 +010082 self._source_traceback = events.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070083
Yury Selivanova0c1ba62016-10-28 12:52:37 -040084 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +020085
86 def __repr__(self):
INADA Naoki9e4e38e2016-10-09 14:44:47 +090087 return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070088
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090089 def __del__(self):
90 if not self._log_traceback:
91 # set_exception() was not called, or result() or exception()
92 # has consumed the exception
93 return
94 exc = self._exception
95 context = {
96 'message': ('%s exception was never retrieved'
97 % self.__class__.__name__),
98 'exception': exc,
99 'future': self,
100 }
101 if self._source_traceback:
102 context['source_traceback'] = self._source_traceback
103 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100104
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700105 def cancel(self):
106 """Cancel the future and schedule callbacks.
107
108 If the future is already done or cancelled, return False. Otherwise,
109 change the future's state to cancelled, schedule the callbacks and
110 return True.
111 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000112 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700113 if self._state != _PENDING:
114 return False
115 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400116 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700117 return True
118
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400119 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700120 """Internal: Ask the event loop to call all callbacks.
121
122 The callbacks are scheduled to be called as soon as possible. Also
123 clears the callback list.
124 """
125 callbacks = self._callbacks[:]
126 if not callbacks:
127 return
128
129 self._callbacks[:] = []
130 for callback in callbacks:
131 self._loop.call_soon(callback, self)
132
133 def cancelled(self):
134 """Return True if the future was cancelled."""
135 return self._state == _CANCELLED
136
137 # Don't implement running(); see http://bugs.python.org/issue18699
138
139 def done(self):
140 """Return True if the future is done.
141
142 Done means either that a result / exception are available, or that the
143 future was cancelled.
144 """
145 return self._state != _PENDING
146
147 def result(self):
148 """Return the result this future represents.
149
150 If the future has been cancelled, raises CancelledError. If the
151 future's result isn't yet available, raises InvalidStateError. If
152 the future is done and has an exception set, this exception is raised.
153 """
154 if self._state == _CANCELLED:
155 raise CancelledError
156 if self._state != _FINISHED:
157 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100158 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700159 if self._tb_logger is not None:
160 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100161 self._tb_logger = None
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 if self._tb_logger is not None:
180 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100181 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700182 return self._exception
183
184 def add_done_callback(self, fn):
185 """Add a callback to be run when the future becomes done.
186
187 The callback is called with a single argument - the future object. If
188 the future is already done when this is called, the callback is
189 scheduled with call_soon.
190 """
191 if self._state != _PENDING:
192 self._loop.call_soon(fn, self)
193 else:
194 self._callbacks.append(fn)
195
196 # New method not in PEP 3148.
197
198 def remove_done_callback(self, fn):
199 """Remove all instances of a callback from the "call when done" list.
200
201 Returns the number of callbacks removed.
202 """
203 filtered_callbacks = [f for f in self._callbacks if f != fn]
204 removed_count = len(self._callbacks) - len(filtered_callbacks)
205 if removed_count:
206 self._callbacks[:] = filtered_callbacks
207 return removed_count
208
209 # So-called internal methods (note: no set_running_or_notify_cancel()).
210
211 def set_result(self, result):
212 """Mark the future done and set its result.
213
214 If the future is already done when this method is called, raises
215 InvalidStateError.
216 """
217 if self._state != _PENDING:
218 raise InvalidStateError('{}: {!r}'.format(self._state, self))
219 self._result = result
220 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400221 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700222
223 def set_exception(self, exception):
224 """Mark the future done and set an exception.
225
226 If the future is already done when this method is called, raises
227 InvalidStateError.
228 """
229 if self._state != _PENDING:
230 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800231 if isinstance(exception, type):
232 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500233 if type(exception) is StopIteration:
234 raise TypeError("StopIteration interacts badly with generators "
235 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700236 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700237 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400238 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900239 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700240
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241 def __iter__(self):
242 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700243 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700244 yield self # This tells Task to wait for completion.
245 assert self.done(), "yield from wasn't used with future"
246 return self.result() # May raise too.
247
Victor Stinner71080fc2015-07-25 02:23:21 +0200248 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400249 __await__ = __iter__ # make compatible with 'await' expression
250
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 Selivanov5d7e3b62015-11-17 12:19:41 -0500256def _set_result_unless_cancelled(fut, result):
257 """Helper setting the result only if the future was not cancelled."""
258 if fut.cancelled():
259 return
260 fut.set_result(result)
261
262
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700263def _set_concurrent_future_state(concurrent, source):
264 """Copy state from a future to a concurrent.futures.Future."""
265 assert source.done()
266 if source.cancelled():
267 concurrent.cancel()
268 if not concurrent.set_running_or_notify_cancel():
269 return
270 exception = source.exception()
271 if exception is not None:
272 concurrent.set_exception(exception)
273 else:
274 result = source.result()
275 concurrent.set_result(result)
276
277
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500278def _copy_future_state(source, dest):
279 """Internal helper to copy state from another Future.
280
281 The other Future may be a concurrent.futures.Future.
282 """
283 assert source.done()
284 if dest.cancelled():
285 return
286 assert not dest.done()
287 if source.cancelled():
288 dest.cancel()
289 else:
290 exception = source.exception()
291 if exception is not None:
292 dest.set_exception(exception)
293 else:
294 result = source.result()
295 dest.set_result(result)
296
297
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700298def _chain_future(source, destination):
299 """Chain two futures so that when one completes, so does the other.
300
301 The result (or exception) of source will be copied to destination.
302 If destination is cancelled, source gets cancelled too.
303 Compatible with both asyncio.Future and concurrent.futures.Future.
304 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700305 if not isfuture(source) and not isinstance(source,
306 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700307 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700308 if not isfuture(destination) and not isinstance(destination,
309 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700310 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700311 source_loop = source._loop if isfuture(source) else None
312 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700313
314 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700315 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500316 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700317 else:
318 _set_concurrent_future_state(future, other)
319
320 def _call_check_cancel(destination):
321 if destination.cancelled():
322 if source_loop is None or source_loop is dest_loop:
323 source.cancel()
324 else:
325 source_loop.call_soon_threadsafe(source.cancel)
326
327 def _call_set_state(source):
328 if dest_loop is None or dest_loop is source_loop:
329 _set_state(destination, source)
330 else:
331 dest_loop.call_soon_threadsafe(_set_state, destination, source)
332
333 destination.add_done_callback(_call_check_cancel)
334 source.add_done_callback(_call_set_state)
335
336
337def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700338 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700339 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700340 return future
341 assert isinstance(future, concurrent.futures.Future), \
342 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400343 if loop is None:
344 loop = events.get_event_loop()
345 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700346 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700347 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900348
349
350try:
351 import _asyncio
352except ImportError:
353 pass
354else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400355 # _CFuture is needed for tests.
356 Future = _CFuture = _asyncio.Future