blob: bcd4d16b9d158f579decbe700c1467818a728ca8 [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""A Future class similar to the one in PEP 3148."""
2
3__all__ = ['CancelledError', 'TimeoutError',
4 'InvalidStateError',
5 'Future', 'wrap_future',
6 ]
7
8import concurrent.futures._base
9import logging
Victor Stinner313a9802014-07-29 12:58:23 +020010import reprlib
Victor Stinner4c3c6992013-12-19 22:42:40 +010011import sys
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070012import traceback
13
Victor Stinner71080fc2015-07-25 02:23:21 +020014from . import compat
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070015from . import events
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070016
17# States for Future.
18_PENDING = 'PENDING'
19_CANCELLED = 'CANCELLED'
20_FINISHED = 'FINISHED'
21
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070022Error = concurrent.futures._base.Error
23CancelledError = concurrent.futures.CancelledError
24TimeoutError = concurrent.futures.TimeoutError
25
26STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
27
28
29class InvalidStateError(Error):
30 """The operation is not allowed in this state."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070031
32
33class _TracebackLogger:
34 """Helper to log a traceback upon destruction if not cleared.
35
36 This solves a nasty problem with Futures and Tasks that have an
37 exception set: if nobody asks for the exception, the exception is
38 never logged. This violates the Zen of Python: 'Errors should
39 never pass silently. Unless explicitly silenced.'
40
41 However, we don't want to log the exception as soon as
42 set_exception() is called: if the calling code is written
43 properly, it will get the exception and handle it properly. But
44 we *do* want to log it if result() or exception() was never called
45 -- otherwise developers waste a lot of time wondering why their
46 buggy code fails silently.
47
48 An earlier attempt added a __del__() method to the Future class
49 itself, but this backfired because the presence of __del__()
50 prevents garbage collection from breaking cycles. A way out of
51 this catch-22 is to avoid having a __del__() method on the Future
52 class itself, but instead to have a reference to a helper object
53 with a __del__() method that logs the traceback, where we ensure
54 that the helper object doesn't participate in cycles, and only the
55 Future has a reference to it.
56
57 The helper object is added when set_exception() is called. When
58 the Future is collected, and the helper is present, the helper
59 object is also collected, and its __del__() method will log the
60 traceback. When the Future's result() or exception() method is
Serhiy Storchaka56a6d852014-12-01 18:28:43 +020061 called (and a helper object is present), it removes the helper
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070062 object, after calling its clear() method to prevent it from
63 logging.
64
65 One downside is that we do a fair amount of work to extract the
66 traceback from the exception, even when it is never logged. It
67 would seem cheaper to just store the exception object, but that
68 references the traceback, which references stack frames, which may
69 reference the Future, which references the _TracebackLogger, and
70 then the _TracebackLogger would be included in a cycle, which is
71 what we're trying to avoid! As an optimization, we don't
72 immediately format the exception; we only do the work when
73 activate() is called, which call is delayed until after all the
74 Future's callbacks have run. Since usually a Future has at least
75 one callback (typically set by 'yield from') and usually that
76 callback extracts the callback, thereby removing the need to
77 format the exception.
78
79 PS. I don't claim credit for this solution. I first heard of it
80 in a discussion about closing files when they are collected.
81 """
82
Victor Stinner80f53aa2014-06-27 13:52:20 +020083 __slots__ = ('loop', 'source_traceback', 'exc', 'tb')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070084
Victor Stinner80f53aa2014-06-27 13:52:20 +020085 def __init__(self, future, exc):
86 self.loop = future._loop
87 self.source_traceback = future._source_traceback
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070088 self.exc = exc
89 self.tb = None
90
91 def activate(self):
92 exc = self.exc
93 if exc is not None:
94 self.exc = None
95 self.tb = traceback.format_exception(exc.__class__, exc,
96 exc.__traceback__)
97
98 def clear(self):
99 self.exc = None
100 self.tb = None
101
102 def __del__(self):
103 if self.tb:
Victor Stinner662fd5f2014-11-20 14:16:31 +0100104 msg = 'Future/Task exception was never retrieved\n'
Victor Stinner80f53aa2014-06-27 13:52:20 +0200105 if self.source_traceback:
Victor Stinner662fd5f2014-11-20 14:16:31 +0100106 src = ''.join(traceback.format_list(self.source_traceback))
107 msg += 'Future/Task created at (most recent call last):\n'
108 msg += '%s\n' % src.rstrip()
Victor Stinner80f53aa2014-06-27 13:52:20 +0200109 msg += ''.join(self.tb).rstrip()
110 self.loop.call_exception_handler({'message': msg})
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700111
112
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700113def isfuture(obj):
114 """Check for a Future.
115
116 This returns True when obj is a Future instance or is advertising
117 itself as duck-type compatible by setting _asyncio_future_blocking.
118 See comment in Future for more details.
119 """
120 return getattr(obj, '_asyncio_future_blocking', None) is not None
121
122
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700123class Future:
124 """This class is *almost* compatible with concurrent.futures.Future.
125
126 Differences:
127
128 - result() and exception() do not take a timeout argument and
129 raise an exception when the future isn't done yet.
130
131 - Callbacks registered with add_done_callback() are always called
132 via the event loop's call_soon_threadsafe().
133
134 - This class is not compatible with the wait() and as_completed()
135 methods in the concurrent.futures package.
136
137 (In Python 3.4 or later we may be able to unify the implementations.)
138 """
139
140 # Class variables serving as defaults for instance variables.
141 _state = _PENDING
142 _result = None
143 _exception = None
144 _loop = None
Victor Stinnerfe22e092014-12-04 23:00:13 +0100145 _source_traceback = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700146
Guido van Rossum1140a032016-09-09 12:54:54 -0700147 # This field is used for a dual purpose:
148 # - Its presence is a marker to declare that a class implements
149 # the Future protocol (i.e. is intended to be duck-type compatible).
150 # The value must also be not-None, to enable a subclass to declare
151 # that it is not compatible by setting this to None.
152 # - It is set by __iter__() below so that Task._step() can tell
153 # the difference between `yield from Future()` (correct) vs.
154 # `yield Future()` (incorrect).
155 _asyncio_future_blocking = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700156
Victor Stinnere40c0782013-12-21 00:19:33 +0100157 _log_traceback = False # Used for Python 3.4 and later
158 _tb_logger = None # Used for Python 3.3 only
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700159
160 def __init__(self, *, loop=None):
161 """Initialize the future.
162
Martin Panterc04fb562016-02-10 05:44:01 +0000163 The optional event_loop argument allows explicitly setting the event
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700164 loop object used by the future. If it's not provided, the future uses
165 the default event loop.
166 """
167 if loop is None:
168 self._loop = events.get_event_loop()
169 else:
170 self._loop = loop
171 self._callbacks = []
Victor Stinner80f53aa2014-06-27 13:52:20 +0200172 if self._loop.get_debug():
173 self._source_traceback = traceback.extract_stack(sys._getframe(1))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700174
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500175 def __format_callbacks(self):
Victor Stinner975735f2014-06-25 21:41:58 +0200176 cb = self._callbacks
177 size = len(cb)
178 if not size:
179 cb = ''
180
181 def format_cb(callback):
Guido van Rossum0a9933e2015-05-02 18:38:24 -0700182 return events._format_callback_source(callback, ())
Victor Stinner975735f2014-06-25 21:41:58 +0200183
184 if size == 1:
185 cb = format_cb(cb[0])
186 elif size == 2:
187 cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
188 elif size > 2:
189 cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
190 size-2,
191 format_cb(cb[-1]))
192 return 'cb=[%s]' % cb
193
Victor Stinner313a9802014-07-29 12:58:23 +0200194 def _repr_info(self):
Victor Stinner975735f2014-06-25 21:41:58 +0200195 info = [self._state.lower()]
196 if self._state == _FINISHED:
Victor Stinner313a9802014-07-29 12:58:23 +0200197 if self._exception is not None:
198 info.append('exception={!r}'.format(self._exception))
199 else:
200 # use reprlib to limit the length of the output, especially
201 # for very long strings
202 result = reprlib.repr(self._result)
203 info.append('result={}'.format(result))
Victor Stinner975735f2014-06-25 21:41:58 +0200204 if self._callbacks:
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500205 info.append(self.__format_callbacks())
Victor Stinner313a9802014-07-29 12:58:23 +0200206 if self._source_traceback:
207 frame = self._source_traceback[-1]
208 info.append('created at %s:%s' % (frame[0], frame[1]))
209 return info
210
211 def __repr__(self):
212 info = self._repr_info()
Victor Stinner975735f2014-06-25 21:41:58 +0200213 return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700214
Victor Stinner978a9af2015-01-29 17:50:58 +0100215 # On Python 3.3 and older, objects with a destructor part of a reference
216 # cycle are never destroyed. It's not more the case on Python 3.4 thanks
217 # to the PEP 442.
Victor Stinner71080fc2015-07-25 02:23:21 +0200218 if compat.PY34:
Victor Stinner4c3c6992013-12-19 22:42:40 +0100219 def __del__(self):
Victor Stinnere40c0782013-12-21 00:19:33 +0100220 if not self._log_traceback:
221 # set_exception() was not called, or result() or exception()
222 # has consumed the exception
223 return
224 exc = self._exception
Yury Selivanov569efa22014-02-18 18:02:19 -0500225 context = {
Victor Stinner80f53aa2014-06-27 13:52:20 +0200226 'message': ('%s exception was never retrieved'
227 % self.__class__.__name__),
Yury Selivanov569efa22014-02-18 18:02:19 -0500228 'exception': exc,
229 'future': self,
230 }
Victor Stinner80f53aa2014-06-27 13:52:20 +0200231 if self._source_traceback:
232 context['source_traceback'] = self._source_traceback
Yury Selivanov569efa22014-02-18 18:02:19 -0500233 self._loop.call_exception_handler(context)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100234
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700235 def cancel(self):
236 """Cancel the future and schedule callbacks.
237
238 If the future is already done or cancelled, return False. Otherwise,
239 change the future's state to cancelled, schedule the callbacks and
240 return True.
241 """
242 if self._state != _PENDING:
243 return False
244 self._state = _CANCELLED
245 self._schedule_callbacks()
246 return True
247
248 def _schedule_callbacks(self):
249 """Internal: Ask the event loop to call all callbacks.
250
251 The callbacks are scheduled to be called as soon as possible. Also
252 clears the callback list.
253 """
254 callbacks = self._callbacks[:]
255 if not callbacks:
256 return
257
258 self._callbacks[:] = []
259 for callback in callbacks:
260 self._loop.call_soon(callback, self)
261
262 def cancelled(self):
263 """Return True if the future was cancelled."""
264 return self._state == _CANCELLED
265
266 # Don't implement running(); see http://bugs.python.org/issue18699
267
268 def done(self):
269 """Return True if the future is done.
270
271 Done means either that a result / exception are available, or that the
272 future was cancelled.
273 """
274 return self._state != _PENDING
275
276 def result(self):
277 """Return the result this future represents.
278
279 If the future has been cancelled, raises CancelledError. If the
280 future's result isn't yet available, raises InvalidStateError. If
281 the future is done and has an exception set, this exception is raised.
282 """
283 if self._state == _CANCELLED:
284 raise CancelledError
285 if self._state != _FINISHED:
286 raise InvalidStateError('Result is not ready.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100287 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700288 if self._tb_logger is not None:
289 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100290 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700291 if self._exception is not None:
292 raise self._exception
293 return self._result
294
295 def exception(self):
296 """Return the exception that was set on this future.
297
298 The exception (or None if no exception was set) is returned only if
299 the future is done. If the future has been cancelled, raises
300 CancelledError. If the future isn't done yet, raises
301 InvalidStateError.
302 """
303 if self._state == _CANCELLED:
304 raise CancelledError
305 if self._state != _FINISHED:
306 raise InvalidStateError('Exception is not set.')
Victor Stinnere40c0782013-12-21 00:19:33 +0100307 self._log_traceback = False
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700308 if self._tb_logger is not None:
309 self._tb_logger.clear()
Victor Stinnere40c0782013-12-21 00:19:33 +0100310 self._tb_logger = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700311 return self._exception
312
313 def add_done_callback(self, fn):
314 """Add a callback to be run when the future becomes done.
315
316 The callback is called with a single argument - the future object. If
317 the future is already done when this is called, the callback is
318 scheduled with call_soon.
319 """
320 if self._state != _PENDING:
321 self._loop.call_soon(fn, self)
322 else:
323 self._callbacks.append(fn)
324
325 # New method not in PEP 3148.
326
327 def remove_done_callback(self, fn):
328 """Remove all instances of a callback from the "call when done" list.
329
330 Returns the number of callbacks removed.
331 """
332 filtered_callbacks = [f for f in self._callbacks if f != fn]
333 removed_count = len(self._callbacks) - len(filtered_callbacks)
334 if removed_count:
335 self._callbacks[:] = filtered_callbacks
336 return removed_count
337
338 # So-called internal methods (note: no set_running_or_notify_cancel()).
339
340 def set_result(self, result):
341 """Mark the future done and set its result.
342
343 If the future is already done when this method is called, raises
344 InvalidStateError.
345 """
346 if self._state != _PENDING:
347 raise InvalidStateError('{}: {!r}'.format(self._state, self))
348 self._result = result
349 self._state = _FINISHED
350 self._schedule_callbacks()
351
352 def set_exception(self, exception):
353 """Mark the future done and set an exception.
354
355 If the future is already done when this method is called, raises
356 InvalidStateError.
357 """
358 if self._state != _PENDING:
359 raise InvalidStateError('{}: {!r}'.format(self._state, self))
Victor Stinner95728982014-01-30 16:01:54 -0800360 if isinstance(exception, type):
361 exception = exception()
Yury Selivanov1bd03072016-03-02 11:03:28 -0500362 if type(exception) is StopIteration:
363 raise TypeError("StopIteration interacts badly with generators "
364 "and cannot be raised into a Future")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700365 self._exception = exception
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700366 self._state = _FINISHED
367 self._schedule_callbacks()
Victor Stinner71080fc2015-07-25 02:23:21 +0200368 if compat.PY34:
Victor Stinnere40c0782013-12-21 00:19:33 +0100369 self._log_traceback = True
Victor Stinner4c3c6992013-12-19 22:42:40 +0100370 else:
Victor Stinner80f53aa2014-06-27 13:52:20 +0200371 self._tb_logger = _TracebackLogger(self, exception)
Victor Stinner4c3c6992013-12-19 22:42:40 +0100372 # Arrange for the logger to be activated after all callbacks
373 # have had a chance to call result() or exception().
374 self._loop.call_soon(self._tb_logger.activate)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700375
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700376 def __iter__(self):
377 if not self.done():
Guido van Rossum1140a032016-09-09 12:54:54 -0700378 self._asyncio_future_blocking = True
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700379 yield self # This tells Task to wait for completion.
380 assert self.done(), "yield from wasn't used with future"
381 return self.result() # May raise too.
382
Victor Stinner71080fc2015-07-25 02:23:21 +0200383 if compat.PY35:
Yury Selivanov1af2bf72015-05-11 22:27:25 -0400384 __await__ = __iter__ # make compatible with 'await' expression
385
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700386
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500387def _set_result_unless_cancelled(fut, result):
388 """Helper setting the result only if the future was not cancelled."""
389 if fut.cancelled():
390 return
391 fut.set_result(result)
392
393
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700394def _set_concurrent_future_state(concurrent, source):
395 """Copy state from a future to a concurrent.futures.Future."""
396 assert source.done()
397 if source.cancelled():
398 concurrent.cancel()
399 if not concurrent.set_running_or_notify_cancel():
400 return
401 exception = source.exception()
402 if exception is not None:
403 concurrent.set_exception(exception)
404 else:
405 result = source.result()
406 concurrent.set_result(result)
407
408
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500409def _copy_future_state(source, dest):
410 """Internal helper to copy state from another Future.
411
412 The other Future may be a concurrent.futures.Future.
413 """
414 assert source.done()
415 if dest.cancelled():
416 return
417 assert not dest.done()
418 if source.cancelled():
419 dest.cancel()
420 else:
421 exception = source.exception()
422 if exception is not None:
423 dest.set_exception(exception)
424 else:
425 result = source.result()
426 dest.set_result(result)
427
428
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700429def _chain_future(source, destination):
430 """Chain two futures so that when one completes, so does the other.
431
432 The result (or exception) of source will be copied to destination.
433 If destination is cancelled, source gets cancelled too.
434 Compatible with both asyncio.Future and concurrent.futures.Future.
435 """
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700436 if not isfuture(source) and not isinstance(source,
437 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700438 raise TypeError('A future is required for source argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700439 if not isfuture(destination) and not isinstance(destination,
440 concurrent.futures.Future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700441 raise TypeError('A future is required for destination argument')
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700442 source_loop = source._loop if isfuture(source) else None
443 dest_loop = destination._loop if isfuture(destination) else None
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700444
445 def _set_state(future, other):
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700446 if isfuture(future):
Yury Selivanov5d7e3b62015-11-17 12:19:41 -0500447 _copy_future_state(other, future)
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700448 else:
449 _set_concurrent_future_state(future, other)
450
451 def _call_check_cancel(destination):
452 if destination.cancelled():
453 if source_loop is None or source_loop is dest_loop:
454 source.cancel()
455 else:
456 source_loop.call_soon_threadsafe(source.cancel)
457
458 def _call_set_state(source):
459 if dest_loop is None or dest_loop is source_loop:
460 _set_state(destination, source)
461 else:
462 dest_loop.call_soon_threadsafe(_set_state, destination, source)
463
464 destination.add_done_callback(_call_check_cancel)
465 source.add_done_callback(_call_set_state)
466
467
468def wrap_future(future, *, loop=None):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700469 """Wrap concurrent.futures.Future object."""
Guido van Rossum7b3b3dc2016-09-09 14:26:31 -0700470 if isfuture(future):
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700471 return future
472 assert isinstance(future, concurrent.futures.Future), \
473 'concurrent.futures.Future is expected, got {!r}'.format(future)
Yury Selivanov7661db62016-05-16 15:38:39 -0400474 if loop is None:
475 loop = events.get_event_loop()
476 new_future = loop.create_future()
Guido van Rossum841d9ee2015-10-03 08:31:42 -0700477 _chain_future(future, new_future)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700478 return new_future