aboutsummaryrefslogtreecommitdiff
blob: 9c9900d4c46add62ffcfe6de3918453be62bbd43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# 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