# Copyright 2016-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # # For compatibility with python versions which do not have the # asyncio module (Python 3.3 and earlier), this module provides a # subset of the asyncio.futures.Futures interface. from __future__ import unicode_literals __all__ = ( 'CancelledError', 'Future', 'InvalidStateError', 'TimeoutError', ) try: from asyncio import ( CancelledError, Future, InvalidStateError, TimeoutError, ) except ImportError: from portage.exception import PortageException class Error(PortageException): pass class CancelledError(Error): def __init__(self): Error.__init__(self, "cancelled") class TimeoutError(Error): def __init__(self): Error.__init__(self, "timed out") class InvalidStateError(Error): pass Future = None import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.util._eventloop.global_event_loop:global_event_loop@_global_event_loop', ) _PENDING = 'PENDING' _CANCELLED = 'CANCELLED' _FINISHED = 'FINISHED' class _EventLoopFuture(object): """ This class provides (a subset of) the asyncio.Future interface, for use with the EventLoop class, because EventLoop is currently missing some of the asyncio.AbstractEventLoop methods that asyncio.Future requires. """ # Class variables serving as defaults for instance variables. _state = _PENDING _result = None _exception = None _loop = None def __init__(self, loop=None): """Initialize the future. The optional loop argument allows explicitly setting the event loop object used by the future. If it's not provided, the future uses the default event loop. """ if loop is None: self._loop = _global_event_loop() else: self._loop = loop self._callbacks = [] def cancel(self): """Cancel the future and schedule callbacks. If the future is already done or cancelled, return False. Otherwise, change the future's state to cancelled, schedule the callbacks and return True. """ if self._state != _PENDING: return False self._state = _CANCELLED self._schedule_callbacks() return True def _schedule_callbacks(self): """Internal: Ask the event loop to call all callbacks. The callbacks are scheduled to be called as soon as possible. Also clears the callback list. """ callbacks = self._callbacks[:] if not callbacks: return self._callbacks[:] = [] for callback in callbacks: self._loop.call_soon(callback, self) def cancelled(self): """Return True if the future was cancelled.""" return self._state == _CANCELLED def done(self): """Return True if the future is done. Done means either that a result / exception are available, or that the future was cancelled. """ return self._state != _PENDING def result(self): """Return the result this future represents. If the future has been cancelled, raises CancelledError. If the future's result isn't yet available, raises InvalidStateError. If the future is done and has an exception set, this exception is raised. """ if self._state == _CANCELLED: raise CancelledError() if self._state != _FINISHED: raise InvalidStateError('Result is not ready.') if self._exception is not None: raise self._exception return self._result def exception(self): """Return the exception that was set on this future. The exception (or None if no exception was set) is returned only if the future is done. If the future has been cancelled, raises CancelledError. If the future isn't done yet, raises InvalidStateError. """ if self._state == _CANCELLED: raise CancelledError if self._state != _FINISHED: raise InvalidStateError('Exception is not set.') return self._exception def add_done_callback(self, fn): """Add a callback to be run when the future becomes done. The callback is called with a single argument - the future object. If the future is already done when this is called, the callback is scheduled with call_soon. """ if self._state != _PENDING: self._loop.call_soon(fn, self) else: self._callbacks.append(fn) def remove_done_callback(self, fn): """Remove all instances of a callback from the "call when done" list. Returns the number of callbacks removed. """ filtered_callbacks = [f for f in self._callbacks if f != fn] removed_count = len(self._callbacks) - len(filtered_callbacks) if removed_count: self._callbacks[:] = filtered_callbacks return removed_count def set_result(self, result): """Mark the future done and set its result. If the future is already done when this method is called, raises InvalidStateError. """ if self._state != _PENDING: raise InvalidStateError('{}: {!r}'.format(self._state, self)) self._result = result self._state = _FINISHED self._schedule_callbacks() def set_exception(self, exception): """Mark the future done and set an exception. If the future is already done when this method is called, raises InvalidStateError. """ if self._state != _PENDING: raise InvalidStateError('{}: {!r}'.format(self._state, self)) if isinstance(exception, type): exception = exception() self._exception = exception self._state = _FINISHED self._schedule_callbacks() if Future is None: Future = _EventLoopFuture