blob: 9c9900d4c46add62ffcfe6de3918453be62bbd43 [file] [log] [blame]
# 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