blob: d11d289307eaf341a9ce411f6ae6c5e06b79d454 [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 _TracebackLogger:
31 """Helper to log a traceback upon destruction if not cleared.
32
33 This solves a nasty problem with Futures and Tasks that have an
34 exception set: if nobody asks for the exception, the exception is
35 never logged. This violates the Zen of Python: 'Errors should
36 never pass silently. Unless explicitly silenced.'
37
38 However, we don't want to log the exception as soon as
39 set_exception() is called: if the calling code is written
40 properly, it will get the exception and handle it properly. But
41 we *do* want to log it if result() or exception() was never called
42 -- otherwise developers waste a lot of time wondering why their
43 buggy code fails silently.
44
45 An earlier attempt added a __del__() method to the Future class
46 itself, but this backfired because the presence of __del__()
47 prevents garbage collection from breaking cycles. A way out of
48 this catch-22 is to avoid having a __del__() method on the Future
49 class itself, but instead to have a reference to a helper object
50 with a __del__() method that logs the traceback, where we ensure
51 that the helper object doesn't participate in cycles, and only the
52 Future has a reference to it.
53
54 The helper object is added when set_exception() is called. When
55 the Future is collected, and the helper is present, the helper
56 object is also collected, and its __del__() method will log the
57 traceback. When the Future's result() or exception() method is
Serhiy Storchaka56a6d852014-12-01 18:28:43 +020058 called (and a helper object is present), it removes the helper
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070059 object, after calling its clear() method to prevent it from
60 logging.
61
62 One downside is that we do a fair amount of work to extract the
63 traceback from the exception, even when it is never logged. It
64 would seem cheaper to just store the exception object, but that
65 references the traceback, which references stack frames, which may
66 reference the Future, which references the _TracebackLogger, and
67 then the _TracebackLogger would be included in a cycle, which is
68 what we're trying to avoid! As an optimization, we don't
69 immediately format the exception; we only do the work when
70 activate() is called, which call is delayed until after all the
71 Future's callbacks have run. Since usually a Future has at least
72 one callback (typically set by 'yield from') and usually that
73 callback extracts the callback, thereby removing the need to
74 format the exception.
75
76 PS. I don't claim credit for this solution. I first heard of it
77 in a discussion about closing files when they are collected.
78 """
79
Victor Stinner80f53aa2014-06-27 13:52:20 +020080 __slots__ = ('loop', 'source_traceback', 'exc', 'tb')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070081
Victor Stinner80f53aa2014-06-27 13:52:20 +020082 def __init__(self, future, exc):
83 self.loop = future._loop
84 self.source_traceback = future._source_traceback
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070085 self.exc = exc
86 self.tb = None
87
88 def activate(self):
89 exc = self.exc
90 if exc is not None:
91 self.exc = None
92 self.tb = traceback.format_exception(exc.__class__, exc,
93 exc.__traceback__)
94
95 def clear(self):
96 self.exc = None
97 self.tb = None
98
99 def __del__(self):
100 if self.tb:
Victor Stinner662fd5f2014-11-20 14:16:31 +0100101 msg = 'Future/Task exception was never retrieved\n'
Victor Stinner80f53aa2014-06-27 13:52:20 +0200102 if self.source_traceback:
Victor Stinner662fd5f2014-11-20 14:16:31 +0100103 src = ''.join(traceback.format_list(self.source_traceback))
104 msg += 'Future/Task created at (most recent call last):\n'
105 msg += '%s\n' % src.rstrip()
Victor Stinner80f53aa2014-06-27 13:52:20 +0200106 msg += ''.join(self.tb).rstrip()
107 self.loop.call_exception_handler({'message': msg})
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700108
109
110class Future:
111 """This class is *almost* compatible with concurrent.futures.Future.
112
113 Differences:
114
115 - result() and exception() do not take a timeout argument and
116 raise an exception when the future isn't done yet.
117
118 - Callbacks registered with add_done_callback() are always called
119 via the event loop's call_soon_threadsafe().
120
121 - This class is not compatible with the wait() and as_completed()
122 methods in the concurrent.futures package.
123
124 (In Python 3.4 or later we may be able to unify the implementations.)
125 """
126
127 # Class variables serving as defaults for instance variables.
128 _state = _PENDING
129 _result = None
130 _exception = None
131 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +0100132 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700133
Guido van Rossum1140a032016-09-09 12:54:54 -0700134 # This field is used for a dual purpose:
135 # - Its presence is a marker to declare that a class implements
136 # the Future protocol (i.e. is intended to be duck-type compatible).
137 # The value must also be not-None, to enable a subclass to declare
138 # that it is not compatible by setting this to None.
139 # - It is set by __iter__() below so that Task._step() can tell
140 # the difference between `yield from Future()` (correct) vs.
141 # `yield Future()` (incorrect).
142 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700143
Victor Stinnere40c0782013-12-21 00:19:33 +0100144 _log_traceback = False # Used for Python 3.4 and later
145 _tb_logger = None # Used for Python 3.3 only
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700146
147 def __init__(self, *, loop=None):
148 """Initialize the future.
149
Martin Panterc04fb562016-02-10 05:44:01 +0000150 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700151 loop object used by the future. If it's not provided, the future uses
152 the default event loop.
153 """
154 if loop is None:
155 self._loop = events.get_event_loop()
156 else:
157 self._loop = loop
158 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +0200159 if self._loop.get_debug():
160 self._source_traceback = traceback.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700161
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400162 _repr_info = base_futures._future_repr_info
Victor Stinner313a9802014-07-29 12:58:23 +0200163
164 def __repr__(self):
INADA Naoki9e4e38e2016-10-09 14:44:47 +0900165 return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info()))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700166
Victor Stinner978a9af2015-01-29 17:50:58 +0100167 # On Python 3.3 and older, objects with a destructor part of a reference
168 # cycle are never destroyed. It's not more the case on Python 3.4 thanks
169 # to the PEP 442.
Victor Stinner71080fc2015-07-25 02:23:21 +0200170 if compat.PY34:
Victor Stinner4c3c6992013-12-19 22:42:40 +0100171 def __del__(self):
Victor Stinnere40c0782013-12-21 00:19:33 +0100172 if not self._log_traceback:
173 # set_exception() was not called, or result() or exception()
174 # has consumed the exception
175 return
176 exc = self._exception
Yury Selivanov569efa22014-02-18 18:02:19 -0500177 context = {
Victor Stinner80f53aa2014-06-27 13:52:20 +0200178 'message': ('%s exception was never retrieved'
179 % self.__class__.__name__),
Yury Selivanov569efa22014-02-18 18:02:19 -0500180 'exception': exc,
181 'future': self,
182 }
Victor Stinner80f53aa2014-06-27 13:52:20 +0200183 if self._source_traceback:
184 context['source_traceback'] = self._source_traceback
Yury Selivanov569efa22014-02-18 18:02:19 -0500185 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100186
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700187 def cancel(self):
188 """Cancel the future and schedule callbacks.
189
190 If the future is already done or cancelled, return False. Otherwise,
191 change the future's state to cancelled, schedule the callbacks and
192 return True.
193 """
194 if self._state != _PENDING:
195 return False
196 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400197 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700198 return True
199
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400200 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700201 """Internal: Ask the event loop to call all callbacks.
202
203 The callbacks are scheduled to be called as soon as possible. Also
204 clears the callback list.
205 """
206 callbacks = self._callbacks[:]
207 if not callbacks:
208 return
209
210 self._callbacks[:] = []
211 for callback in callbacks:
212 self._loop.call_soon(callback, self)
213
214 def cancelled(self):
215 """Return True if the future was cancelled."""
216 return self._state == _CANCELLED
217
218 # Don't implement running(); see http://bugs.python.org/issue18699
219
220 def done(self):
221 """Return True if the future is done.
222
223 Done means either that a result / exception are available, or that the
224 future was cancelled.
225 """
226 return self._state != _PENDING
227
228 def result(self):
229 """Return the result this future represents.
230
231 If the future has been cancelled, raises CancelledError. If the
232 future's result isn't yet available, raises InvalidStateError. If
233 the future is done and has an exception set, this exception is raised.
234 """
235 if self._state == _CANCELLED:
236 raise CancelledError
237 if self._state != _FINISHED:
238 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100239 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700240 if self._tb_logger is not None:
241 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100242 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700243 if self._exception is not None:
244 raise self._exception
245 return self._result
246
247 def exception(self):
248 """Return the exception that was set on this future.
249
250 The exception (or None if no exception was set) is returned only if
251 the future is done. If the future has been cancelled, raises
252 CancelledError. If the future isn't done yet, raises
253 InvalidStateError.
254 """
255 if self._state == _CANCELLED:
256 raise CancelledError
257 if self._state != _FINISHED:
258 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100259 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700260 if self._tb_logger is not None:
261 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100262 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700263 return self._exception
264
265 def add_done_callback(self, fn):
266 """Add a callback to be run when the future becomes done.
267
268 The callback is called with a single argument - the future object. If
269 the future is already done when this is called, the callback is
270 scheduled with call_soon.
271 """
272 if self._state != _PENDING:
273 self._loop.call_soon(fn, self)
274 else:
275 self._callbacks.append(fn)
276
277 # New method not in PEP 3148.
278
279 def remove_done_callback(self, fn):
280 """Remove all instances of a callback from the "call when done" list.
281
282 Returns the number of callbacks removed.
283 """
284 filtered_callbacks = [f for f in self._callbacks if f != fn]
285 removed_count = len(self._callbacks) - len(filtered_callbacks)
286 if removed_count:
287 self._callbacks[:] = filtered_callbacks
288 return removed_count
289
290 # So-called internal methods (note: no set_running_or_notify_cancel()).
291
292 def set_result(self, result):
293 """Mark the future done and set its result.
294
295 If the future is already done when this method is called, raises
296 InvalidStateError.
297 """
298 if self._state != _PENDING:
299 raise InvalidStateError('{}: {!r}'.format(self._state, self))
300 self._result = result
301 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400302 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700303
304 def set_exception(self, exception):
305 """Mark the future done and set an exception.
306
307 If the future is already done when this method is called, raises
308 InvalidStateError.
309 """
310 if self._state != _PENDING:
311 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800312 if isinstance(exception, type):
313 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500314 if type(exception) is StopIteration:
315 raise TypeError("StopIteration interacts badly with generators "
316 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700317 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700318 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400319 self._schedule_callbacks()
Victor Stinner71080fc2015-07-25 02:23:21 +0200320 if compat.PY34:
Victor Stinnere40c0782013-12-21 00:19:33 +0100321 self._log_traceback = True
Victor Stinner4c3c6992013-12-19 22:42:40 +0100322 else:
Victor Stinner80f53aa2014-06-27 13:52:20 +0200323 self._tb_logger = _TracebackLogger(self, exception)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100324 # Arrange for the logger to be activated after all callbacks
325 # have had a chance to call result() or exception().
326 self._loop.call_soon(self._tb_logger.activate)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700327
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700328 def __iter__(self):
329 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700330 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700331 yield self # This tells Task to wait for completion.
332 assert self.done(), "yield from wasn't used with future"
333 return self.result() # May raise too.
334
Victor Stinner71080fc2015-07-25 02:23:21 +0200335 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400336 __await__ = __iter__ # make compatible with 'await' expression
337
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700338
Yury Selivanov01c521b2016-10-23 22:34:35 -0400339# Needed for testing purposes.
340_PyFuture = Future
341
342
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500343def _set_result_unless_cancelled(fut, result):
344 """Helper setting the result only if the future was not cancelled."""
345 if fut.cancelled():
346 return
347 fut.set_result(result)
348
349
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700350def _set_concurrent_future_state(concurrent, source):
351 """Copy state from a future to a concurrent.futures.Future."""
352 assert source.done()
353 if source.cancelled():
354 concurrent.cancel()
355 if not concurrent.set_running_or_notify_cancel():
356 return
357 exception = source.exception()
358 if exception is not None:
359 concurrent.set_exception(exception)
360 else:
361 result = source.result()
362 concurrent.set_result(result)
363
364
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500365def _copy_future_state(source, dest):
366 """Internal helper to copy state from another Future.
367
368 The other Future may be a concurrent.futures.Future.
369 """
370 assert source.done()
371 if dest.cancelled():
372 return
373 assert not dest.done()
374 if source.cancelled():
375 dest.cancel()
376 else:
377 exception = source.exception()
378 if exception is not None:
379 dest.set_exception(exception)
380 else:
381 result = source.result()
382 dest.set_result(result)
383
384
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700385def _chain_future(source, destination):
386 """Chain two futures so that when one completes, so does the other.
387
388 The result (or exception) of source will be copied to destination.
389 If destination is cancelled, source gets cancelled too.
390 Compatible with both asyncio.Future and concurrent.futures.Future.
391 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700392 if not isfuture(source) and not isinstance(source,
393 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700394 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700395 if not isfuture(destination) and not isinstance(destination,
396 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700397 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700398 source_loop = source._loop if isfuture(source) else None
399 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700400
401 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700402 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500403 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700404 else:
405 _set_concurrent_future_state(future, other)
406
407 def _call_check_cancel(destination):
408 if destination.cancelled():
409 if source_loop is None or source_loop is dest_loop:
410 source.cancel()
411 else:
412 source_loop.call_soon_threadsafe(source.cancel)
413
414 def _call_set_state(source):
415 if dest_loop is None or dest_loop is source_loop:
416 _set_state(destination, source)
417 else:
418 dest_loop.call_soon_threadsafe(_set_state, destination, source)
419
420 destination.add_done_callback(_call_check_cancel)
421 source.add_done_callback(_call_set_state)
422
423
424def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700425 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700426 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700427 return future
428 assert isinstance(future, concurrent.futures.Future), \
429 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400430 if loop is None:
431 loop = events.get_event_loop()
432 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700433 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700434 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900435
436
437try:
438 import _asyncio
439except ImportError:
440 pass
441else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400442 # _CFuture is needed for tests.
443 Future = _CFuture = _asyncio.Future