blob: d46a295197b8766890011ae4748eb7c3801befe2 [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
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
Andrew Svetlov88743422017-12-11 17:35:49 +020062 # the difference between
63 # `await Future()` or`yield from Future()` (correct) vs.
Guido van Rossum1140a032016-09-09 12:54:54 -070064 # `yield Future()` (incorrect).
65 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070066
Victor Stinnerc16bace2017-11-28 00:35:33 +010067 _log_traceback = False
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):
Yury Selivanov6370f342017-12-10 18:36:12 -050087 return '<{} {}>'.format(self.__class__.__name__,
88 ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070089
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090090 def __del__(self):
91 if not self._log_traceback:
92 # set_exception() was not called, or result() or exception()
93 # has consumed the exception
94 return
95 exc = self._exception
96 context = {
Yury Selivanov6370f342017-12-10 18:36:12 -050097 'message':
98 f'{self.__class__.__name__} exception was never retrieved',
INADA Naoki3e2ad8e2017-04-25 10:57:18 +090099 'exception': exc,
100 'future': self,
101 }
102 if self._source_traceback:
103 context['source_traceback'] = self._source_traceback
104 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100105
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700106 def cancel(self):
107 """Cancel the future and schedule callbacks.
108
109 If the future is already done or cancelled, return False. Otherwise,
110 change the future's state to cancelled, schedule the callbacks and
111 return True.
112 """
Yury Selivanov7ce1c6f2017-06-11 13:49:18 +0000113 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700114 if self._state != _PENDING:
115 return False
116 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400117 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700118 return True
119
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400120 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700121 """Internal: Ask the event loop to call all callbacks.
122
123 The callbacks are scheduled to be called as soon as possible. Also
124 clears the callback list.
125 """
126 callbacks = self._callbacks[:]
127 if not callbacks:
128 return
129
130 self._callbacks[:] = []
131 for callback in callbacks:
132 self._loop.call_soon(callback, self)
133
134 def cancelled(self):
135 """Return True if the future was cancelled."""
136 return self._state == _CANCELLED
137
138 # Don't implement running(); see http://bugs.python.org/issue18699
139
140 def done(self):
141 """Return True if the future is done.
142
143 Done means either that a result / exception are available, or that the
144 future was cancelled.
145 """
146 return self._state != _PENDING
147
148 def result(self):
149 """Return the result this future represents.
150
151 If the future has been cancelled, raises CancelledError. If the
152 future's result isn't yet available, raises InvalidStateError. If
153 the future is done and has an exception set, this exception is raised.
154 """
155 if self._state == _CANCELLED:
156 raise CancelledError
157 if self._state != _FINISHED:
158 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100159 self._log_traceback = False
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 return self._exception
178
179 def add_done_callback(self, fn):
180 """Add a callback to be run when the future becomes done.
181
182 The callback is called with a single argument - the future object. If
183 the future is already done when this is called, the callback is
184 scheduled with call_soon.
185 """
186 if self._state != _PENDING:
187 self._loop.call_soon(fn, self)
188 else:
189 self._callbacks.append(fn)
190
191 # New method not in PEP 3148.
192
193 def remove_done_callback(self, fn):
194 """Remove all instances of a callback from the "call when done" list.
195
196 Returns the number of callbacks removed.
197 """
198 filtered_callbacks = [f for f in self._callbacks if f != fn]
199 removed_count = len(self._callbacks) - len(filtered_callbacks)
200 if removed_count:
201 self._callbacks[:] = filtered_callbacks
202 return removed_count
203
204 # So-called internal methods (note: no set_running_or_notify_cancel()).
205
206 def set_result(self, result):
207 """Mark the future done and set its result.
208
209 If the future is already done when this method is called, raises
210 InvalidStateError.
211 """
212 if self._state != _PENDING:
213 raise InvalidStateError('{}: {!r}'.format(self._state, self))
214 self._result = result
215 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400216 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700217
218 def set_exception(self, exception):
219 """Mark the future done and set an exception.
220
221 If the future is already done when this method is called, raises
222 InvalidStateError.
223 """
224 if self._state != _PENDING:
225 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800226 if isinstance(exception, type):
227 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500228 if type(exception) is StopIteration:
229 raise TypeError("StopIteration interacts badly with generators "
230 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700231 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700232 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400233 self._schedule_callbacks()
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900234 self._log_traceback = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700235
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700236 def __iter__(self):
237 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700238 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700239 yield self # This tells Task to wait for completion.
Andrew Svetlov88743422017-12-11 17:35:49 +0200240 assert self.done(), "await wasn't used with future"
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241 return self.result() # May raise too.
242
Yury Selivanov6370f342017-12-10 18:36:12 -0500243 __await__ = __iter__ # make compatible with 'await' expression
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400244
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700245
Yury Selivanov01c521b2016-10-23 22:34:35 -0400246# Needed for testing purposes.
247_PyFuture = Future
248
249
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500250def _set_result_unless_cancelled(fut, result):
251 """Helper setting the result only if the future was not cancelled."""
252 if fut.cancelled():
253 return
254 fut.set_result(result)
255
256
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700257def _set_concurrent_future_state(concurrent, source):
258 """Copy state from a future to a concurrent.futures.Future."""
259 assert source.done()
260 if source.cancelled():
261 concurrent.cancel()
262 if not concurrent.set_running_or_notify_cancel():
263 return
264 exception = source.exception()
265 if exception is not None:
266 concurrent.set_exception(exception)
267 else:
268 result = source.result()
269 concurrent.set_result(result)
270
271
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500272def _copy_future_state(source, dest):
273 """Internal helper to copy state from another Future.
274
275 The other Future may be a concurrent.futures.Future.
276 """
277 assert source.done()
278 if dest.cancelled():
279 return
280 assert not dest.done()
281 if source.cancelled():
282 dest.cancel()
283 else:
284 exception = source.exception()
285 if exception is not None:
286 dest.set_exception(exception)
287 else:
288 result = source.result()
289 dest.set_result(result)
290
291
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700292def _chain_future(source, destination):
293 """Chain two futures so that when one completes, so does the other.
294
295 The result (or exception) of source will be copied to destination.
296 If destination is cancelled, source gets cancelled too.
297 Compatible with both asyncio.Future and concurrent.futures.Future.
298 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700299 if not isfuture(source) and not isinstance(source,
300 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700301 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700302 if not isfuture(destination) and not isinstance(destination,
303 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700304 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700305 source_loop = source._loop if isfuture(source) else None
306 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700307
308 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700309 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500310 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700311 else:
312 _set_concurrent_future_state(future, other)
313
314 def _call_check_cancel(destination):
315 if destination.cancelled():
316 if source_loop is None or source_loop is dest_loop:
317 source.cancel()
318 else:
319 source_loop.call_soon_threadsafe(source.cancel)
320
321 def _call_set_state(source):
322 if dest_loop is None or dest_loop is source_loop:
323 _set_state(destination, source)
324 else:
325 dest_loop.call_soon_threadsafe(_set_state, destination, source)
326
327 destination.add_done_callback(_call_check_cancel)
328 source.add_done_callback(_call_set_state)
329
330
331def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700332 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700333 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700334 return future
335 assert isinstance(future, concurrent.futures.Future), \
Yury Selivanov6370f342017-12-10 18:36:12 -0500336 f'concurrent.futures.Future is expected, got {future!r}'
Yury Selivanov7661db62016-05-16 15:38:39 -0400337 if loop is None:
338 loop = events.get_event_loop()
339 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700340 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700341 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900342
343
344try:
345 import _asyncio
346except ImportError:
347 pass
348else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400349 # _CFuture is needed for tests.
350 Future = _CFuture = _asyncio.Future