blob: cff9590e4ead5f3f5dd755891b8a979faf0034a6 [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 """
Yury Selivanov176f2eb2017-06-11 14:00:14 +0000194 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700195 if self._state != _PENDING:
196 return False
197 self._state = _CANCELLED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400198 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700199 return True
200
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400201 def _schedule_callbacks(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700202 """Internal: Ask the event loop to call all callbacks.
203
204 The callbacks are scheduled to be called as soon as possible. Also
205 clears the callback list.
206 """
207 callbacks = self._callbacks[:]
208 if not callbacks:
209 return
210
211 self._callbacks[:] = []
212 for callback in callbacks:
213 self._loop.call_soon(callback, self)
214
215 def cancelled(self):
216 """Return True if the future was cancelled."""
217 return self._state == _CANCELLED
218
219 # Don't implement running(); see http://bugs.python.org/issue18699
220
221 def done(self):
222 """Return True if the future is done.
223
224 Done means either that a result / exception are available, or that the
225 future was cancelled.
226 """
227 return self._state != _PENDING
228
229 def result(self):
230 """Return the result this future represents.
231
232 If the future has been cancelled, raises CancelledError. If the
233 future's result isn't yet available, raises InvalidStateError. If
234 the future is done and has an exception set, this exception is raised.
235 """
236 if self._state == _CANCELLED:
237 raise CancelledError
238 if self._state != _FINISHED:
239 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100240 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700241 if self._tb_logger is not None:
242 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100243 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700244 if self._exception is not None:
245 raise self._exception
246 return self._result
247
248 def exception(self):
249 """Return the exception that was set on this future.
250
251 The exception (or None if no exception was set) is returned only if
252 the future is done. If the future has been cancelled, raises
253 CancelledError. If the future isn't done yet, raises
254 InvalidStateError.
255 """
256 if self._state == _CANCELLED:
257 raise CancelledError
258 if self._state != _FINISHED:
259 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100260 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700261 if self._tb_logger is not None:
262 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100263 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700264 return self._exception
265
266 def add_done_callback(self, fn):
267 """Add a callback to be run when the future becomes done.
268
269 The callback is called with a single argument - the future object. If
270 the future is already done when this is called, the callback is
271 scheduled with call_soon.
272 """
273 if self._state != _PENDING:
274 self._loop.call_soon(fn, self)
275 else:
276 self._callbacks.append(fn)
277
278 # New method not in PEP 3148.
279
280 def remove_done_callback(self, fn):
281 """Remove all instances of a callback from the "call when done" list.
282
283 Returns the number of callbacks removed.
284 """
285 filtered_callbacks = [f for f in self._callbacks if f != fn]
286 removed_count = len(self._callbacks) - len(filtered_callbacks)
287 if removed_count:
288 self._callbacks[:] = filtered_callbacks
289 return removed_count
290
291 # So-called internal methods (note: no set_running_or_notify_cancel()).
292
293 def set_result(self, result):
294 """Mark the future done and set its result.
295
296 If the future is already done when this method is called, raises
297 InvalidStateError.
298 """
299 if self._state != _PENDING:
300 raise InvalidStateError('{}: {!r}'.format(self._state, self))
301 self._result = result
302 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400303 self._schedule_callbacks()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700304
305 def set_exception(self, exception):
306 """Mark the future done and set an exception.
307
308 If the future is already done when this method is called, raises
309 InvalidStateError.
310 """
311 if self._state != _PENDING:
312 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800313 if isinstance(exception, type):
314 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500315 if type(exception) is StopIteration:
316 raise TypeError("StopIteration interacts badly with generators "
317 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700318 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700319 self._state = _FINISHED
Yury Selivanova0c1ba62016-10-28 12:52:37 -0400320 self._schedule_callbacks()
Victor Stinner71080fc2015-07-25 02:23:21 +0200321 if compat.PY34:
Victor Stinnere40c0782013-12-21 00:19:33 +0100322 self._log_traceback = True
Victor Stinner4c3c6992013-12-19 22:42:40 +0100323 else:
Victor Stinner80f53aa2014-06-27 13:52:20 +0200324 self._tb_logger = _TracebackLogger(self, exception)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100325 # Arrange for the logger to be activated after all callbacks
326 # have had a chance to call result() or exception().
327 self._loop.call_soon(self._tb_logger.activate)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700328
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700329 def __iter__(self):
330 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700331 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700332 yield self # This tells Task to wait for completion.
333 assert self.done(), "yield from wasn't used with future"
334 return self.result() # May raise too.
335
Victor Stinner71080fc2015-07-25 02:23:21 +0200336 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400337 __await__ = __iter__ # make compatible with 'await' expression
338
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700339
Yury Selivanov01c521b2016-10-23 22:34:35 -0400340# Needed for testing purposes.
341_PyFuture = Future
342
343
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500344def _set_result_unless_cancelled(fut, result):
345 """Helper setting the result only if the future was not cancelled."""
346 if fut.cancelled():
347 return
348 fut.set_result(result)
349
350
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700351def _set_concurrent_future_state(concurrent, source):
352 """Copy state from a future to a concurrent.futures.Future."""
353 assert source.done()
354 if source.cancelled():
355 concurrent.cancel()
356 if not concurrent.set_running_or_notify_cancel():
357 return
358 exception = source.exception()
359 if exception is not None:
360 concurrent.set_exception(exception)
361 else:
362 result = source.result()
363 concurrent.set_result(result)
364
365
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500366def _copy_future_state(source, dest):
367 """Internal helper to copy state from another Future.
368
369 The other Future may be a concurrent.futures.Future.
370 """
371 assert source.done()
372 if dest.cancelled():
373 return
374 assert not dest.done()
375 if source.cancelled():
376 dest.cancel()
377 else:
378 exception = source.exception()
379 if exception is not None:
380 dest.set_exception(exception)
381 else:
382 result = source.result()
383 dest.set_result(result)
384
385
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700386def _chain_future(source, destination):
387 """Chain two futures so that when one completes, so does the other.
388
389 The result (or exception) of source will be copied to destination.
390 If destination is cancelled, source gets cancelled too.
391 Compatible with both asyncio.Future and concurrent.futures.Future.
392 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700393 if not isfuture(source) and not isinstance(source,
394 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700395 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700396 if not isfuture(destination) and not isinstance(destination,
397 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700398 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700399 source_loop = source._loop if isfuture(source) else None
400 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700401
402 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700403 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500404 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700405 else:
406 _set_concurrent_future_state(future, other)
407
408 def _call_check_cancel(destination):
409 if destination.cancelled():
410 if source_loop is None or source_loop is dest_loop:
411 source.cancel()
412 else:
413 source_loop.call_soon_threadsafe(source.cancel)
414
415 def _call_set_state(source):
416 if dest_loop is None or dest_loop is source_loop:
417 _set_state(destination, source)
418 else:
419 dest_loop.call_soon_threadsafe(_set_state, destination, source)
420
421 destination.add_done_callback(_call_check_cancel)
422 source.add_done_callback(_call_set_state)
423
424
425def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700426 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700427 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700428 return future
429 assert isinstance(future, concurrent.futures.Future), \
430 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400431 if loop is None:
432 loop = events.get_event_loop()
433 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700434 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700435 return new_future
INADA Naokic411a7d2016-10-18 11:48:14 +0900436
437
438try:
439 import _asyncio
440except ImportError:
441 pass
442else:
Yury Selivanov01c521b2016-10-23 22:34:35 -0400443 # _CFuture is needed for tests.
444 Future = _CFuture = _asyncio.Future