1 |
Replace Fuse with Future, which is similar more generic. The |
2 |
code ends up being slightly more verbose, but more flexible. |
3 |
The Future class will be useful elsewhere, including the |
4 |
EventLoop class. |
5 |
--- |
6 |
Applies to the *repoman* branch. |
7 |
|
8 |
pym/portage/util/futures.py | 118 ++++++++++++++++++++++++++++ |
9 |
pym/repoman/fuse.py | 68 ---------------- |
10 |
pym/repoman/main.py | 12 ++- |
11 |
pym/repoman/modules/scan/ebuild/ebuild.py | 10 ++- |
12 |
pym/repoman/modules/scan/ebuild/isebuild.py | 25 ++++-- |
13 |
pym/repoman/modules/scan/metadata/unused.py | 10 ++- |
14 |
pym/repoman/scanner.py | 4 +- |
15 |
7 files changed, 164 insertions(+), 83 deletions(-) |
16 |
create mode 100644 pym/portage/util/futures.py |
17 |
delete mode 100644 pym/repoman/fuse.py |
18 |
|
19 |
diff --git a/pym/portage/util/futures.py b/pym/portage/util/futures.py |
20 |
new file mode 100644 |
21 |
index 0000000..c648f10 |
22 |
--- /dev/null |
23 |
+++ b/pym/portage/util/futures.py |
24 |
@@ -0,0 +1,118 @@ |
25 |
+# Copyright 2016 Gentoo Foundation |
26 |
+# Distributed under the terms of the GNU General Public License v2 |
27 |
+# |
28 |
+# For compatibility with python versions which do not have the |
29 |
+# asyncio module (Python 3.3 and earlier), this module provides a |
30 |
+# subset of the asyncio.futures.Futures interface. |
31 |
+ |
32 |
+from __future__ import unicode_literals |
33 |
+ |
34 |
+__all__ = ( |
35 |
+ 'CancelledError', |
36 |
+ 'Future', |
37 |
+ 'InvalidStateError', |
38 |
+) |
39 |
+ |
40 |
+try: |
41 |
+ from asyncio import ( |
42 |
+ CancelledError, |
43 |
+ Future, |
44 |
+ InvalidStateError, |
45 |
+ ) |
46 |
+except ImportError: |
47 |
+ |
48 |
+ from portage.exception import PortageException |
49 |
+ |
50 |
+ _PENDING = 'PENDING' |
51 |
+ _CANCELLED = 'CANCELLED' |
52 |
+ _FINISHED = 'FINISHED' |
53 |
+ |
54 |
+ class Error(PortageException): |
55 |
+ pass |
56 |
+ |
57 |
+ class CancelledError(Error): |
58 |
+ def __init__(self): |
59 |
+ Error.__init__(self, "cancelled") |
60 |
+ |
61 |
+ class InvalidStateError(Error): |
62 |
+ pass |
63 |
+ |
64 |
+ class Future(object): |
65 |
+ |
66 |
+ # Class variables serving as defaults for instance variables. |
67 |
+ _state = _PENDING |
68 |
+ _result = None |
69 |
+ _exception = None |
70 |
+ |
71 |
+ def cancel(self): |
72 |
+ """Cancel the future and schedule callbacks. |
73 |
+ |
74 |
+ If the future is already done or cancelled, return False. Otherwise, |
75 |
+ change the future's state to cancelled, schedule the callbacks and |
76 |
+ return True. |
77 |
+ """ |
78 |
+ if self._state != _PENDING: |
79 |
+ return False |
80 |
+ self._state = _CANCELLED |
81 |
+ return True |
82 |
+ |
83 |
+ def done(self): |
84 |
+ """Return True if the future is done. |
85 |
+ |
86 |
+ Done means either that a result / exception are available, or that the |
87 |
+ future was cancelled. |
88 |
+ """ |
89 |
+ return self._state != _PENDING |
90 |
+ |
91 |
+ def result(self): |
92 |
+ """Return the result this future represents. |
93 |
+ |
94 |
+ If the future has been cancelled, raises CancelledError. If the |
95 |
+ future's result isn't yet available, raises InvalidStateError. If |
96 |
+ the future is done and has an exception set, this exception is raised. |
97 |
+ """ |
98 |
+ if self._state == _CANCELLED: |
99 |
+ raise CancelledError() |
100 |
+ if self._state != _FINISHED: |
101 |
+ raise InvalidStateError('Result is not ready.') |
102 |
+ if self._exception is not None: |
103 |
+ raise self._exception |
104 |
+ return self._result |
105 |
+ |
106 |
+ def exception(self): |
107 |
+ """Return the exception that was set on this future. |
108 |
+ |
109 |
+ The exception (or None if no exception was set) is returned only if |
110 |
+ the future is done. If the future has been cancelled, raises |
111 |
+ CancelledError. If the future isn't done yet, raises |
112 |
+ InvalidStateError. |
113 |
+ """ |
114 |
+ if self._state == _CANCELLED: |
115 |
+ raise CancelledError |
116 |
+ if self._state != _FINISHED: |
117 |
+ raise InvalidStateError('Exception is not set.') |
118 |
+ return self._exception |
119 |
+ |
120 |
+ def set_result(self, result): |
121 |
+ """Mark the future done and set its result. |
122 |
+ |
123 |
+ If the future is already done when this method is called, raises |
124 |
+ InvalidStateError. |
125 |
+ """ |
126 |
+ if self._state != _PENDING: |
127 |
+ raise InvalidStateError('{}: {!r}'.format(self._state, self)) |
128 |
+ self._result = result |
129 |
+ self._state = _FINISHED |
130 |
+ |
131 |
+ def set_exception(self, exception): |
132 |
+ """Mark the future done and set an exception. |
133 |
+ |
134 |
+ If the future is already done when this method is called, raises |
135 |
+ InvalidStateError. |
136 |
+ """ |
137 |
+ if self._state != _PENDING: |
138 |
+ raise InvalidStateError('{}: {!r}'.format(self._state, self)) |
139 |
+ if isinstance(exception, type): |
140 |
+ exception = exception() |
141 |
+ self._exception = exception |
142 |
+ self._state = _FINISHED |
143 |
diff --git a/pym/repoman/fuse.py b/pym/repoman/fuse.py |
144 |
deleted file mode 100644 |
145 |
index ac864fd..0000000 |
146 |
--- a/pym/repoman/fuse.py |
147 |
+++ /dev/null |
148 |
@@ -1,68 +0,0 @@ |
149 |
- |
150 |
-''' |
151 |
-fuse.py |
152 |
- |
153 |
-A tiny one-time-fuse class that uses a boolean to mimic the property of |
154 |
-an electrical fuse. IT's good (True) until it is popped (bad, False). |
155 |
-It is not resetable. |
156 |
-''' |
157 |
- |
158 |
- |
159 |
-class Fuse(object): |
160 |
- '''A One time fuse style boolean instance''' |
161 |
- |
162 |
- __slots__ = ('_state') |
163 |
- |
164 |
- def __init__(self): |
165 |
- self._state = True |
166 |
- |
167 |
- def pop(self): |
168 |
- '''Blow's the fuse state (makes it False)''' |
169 |
- self._state = False |
170 |
- |
171 |
- def __repr__(self): |
172 |
- '''x.__repr__() <==> repr(x)''' |
173 |
- return repr(self._state>0) |
174 |
- |
175 |
- def __str__(self): |
176 |
- '''x.__str__() <==> str(x)''' |
177 |
- return ['False', 'True'][self._state] |
178 |
- |
179 |
- def __bool__(self): |
180 |
- '''self != 0''' |
181 |
- return self._state != 0 |
182 |
- |
183 |
- def __nonzero__(self): |
184 |
- '''self != 0''' |
185 |
- return self._state != 0 |
186 |
- |
187 |
- def __abs__(self): |
188 |
- '''x.__abs__() <==> abs(x)''' |
189 |
- return [0, 1] [self._state] |
190 |
- |
191 |
- def __int__(self): |
192 |
- '''int(self)''' |
193 |
- return [0, 1][self._state] |
194 |
- |
195 |
- def __eq__(self, value): |
196 |
- '''Return self==value.''' |
197 |
- return self._state == value |
198 |
- |
199 |
- def __ne__(self, value): |
200 |
- '''Return self!=value.''' |
201 |
- return self._state != value |
202 |
- |
203 |
- def __ge__(self, value): |
204 |
- '''Return self>=value.''' |
205 |
- return self._state >= value |
206 |
- |
207 |
- def __gt__(self, value): |
208 |
- return self._state > value |
209 |
- |
210 |
- def __le__(self, value): |
211 |
- '''Return self<=value.''' |
212 |
- return self._state <= value |
213 |
- |
214 |
- def __lt__(self, value): |
215 |
- '''Return self<value.''' |
216 |
- return self._state < value |
217 |
diff --git a/pym/repoman/main.py b/pym/repoman/main.py |
218 |
index 2ccda99..62c3c2c 100755 |
219 |
--- a/pym/repoman/main.py |
220 |
+++ b/pym/repoman/main.py |
221 |
@@ -22,10 +22,13 @@ import portage.repository.config |
222 |
from portage.output import create_color_func, nocolor |
223 |
from portage.output import ConsoleStyleFile, StyleWriter |
224 |
from portage.util import formatter |
225 |
+from portage.util.futures import ( |
226 |
+ Future, |
227 |
+ InvalidStateError, |
228 |
+) |
229 |
|
230 |
from repoman.actions import Actions |
231 |
from repoman.argparser import parse_args |
232 |
-from repoman.fuse import Fuse |
233 |
from repoman.qa_data import ( |
234 |
format_qa_output, format_qa_output_column, qahelp, |
235 |
qawarnings, qacats) |
236 |
@@ -76,7 +79,7 @@ def repoman_main(argv): |
237 |
# Set this to False when an extraordinary issue (generally |
238 |
# something other than a QA issue) makes it impossible to |
239 |
# commit (like if Manifest generation fails). |
240 |
- can_force = Fuse() |
241 |
+ can_force = Future() |
242 |
|
243 |
portdir, portdir_overlay, mydir = utilities.FindPortdir(repoman_settings) |
244 |
if portdir is None: |
245 |
@@ -171,6 +174,11 @@ def repoman_main(argv): |
246 |
qa_output = qa_output.getvalue() |
247 |
qa_output = qa_output.splitlines(True) |
248 |
|
249 |
+ try: |
250 |
+ can_force = can_force.result() |
251 |
+ except InvalidStateError: |
252 |
+ can_force = True |
253 |
+ |
254 |
# output the results |
255 |
actions = Actions(repo_settings, options, scanner, vcs_settings) |
256 |
if actions.inform(can_force, result): |
257 |
diff --git a/pym/repoman/modules/scan/ebuild/ebuild.py b/pym/repoman/modules/scan/ebuild/ebuild.py |
258 |
index 540411f..67eee3f 100644 |
259 |
--- a/pym/repoman/modules/scan/ebuild/ebuild.py |
260 |
+++ b/pym/repoman/modules/scan/ebuild/ebuild.py |
261 |
@@ -8,6 +8,7 @@ from repoman.modules.scan.scanbase import ScanBase |
262 |
# import our initialized portage instance |
263 |
from repoman._portage import portage |
264 |
from portage import os |
265 |
+from portage.util.futures import InvalidStateError |
266 |
|
267 |
pv_toolong_re = re.compile(r'[0-9]{19,}') |
268 |
|
269 |
@@ -127,15 +128,18 @@ class Ebuild(ScanBase): |
270 |
def pkg_invalid(self, **kwargs): |
271 |
'''Sets some pkg info and checks for invalid packages |
272 |
|
273 |
- @param validity_fuse: Fuse instance |
274 |
+ @param validity_future: Future instance |
275 |
@returns: dictionary, including {pkg object} |
276 |
''' |
277 |
- fuse = kwargs.get('validity_fuse') |
278 |
+ fuse = kwargs.get('validity_future') |
279 |
if self.pkg.invalid: |
280 |
for k, msgs in self.pkg.invalid.items(): |
281 |
for msg in msgs: |
282 |
self.qatracker.add_error(k, "%s: %s" % (self.relative_path, msg)) |
283 |
- fuse.pop() |
284 |
+ try: |
285 |
+ fuse.set_result(False) |
286 |
+ except InvalidStateError: |
287 |
+ pass |
288 |
return {'continue': True, 'pkg': self.pkg} |
289 |
return {'continue': False, 'pkg': self.pkg} |
290 |
|
291 |
diff --git a/pym/repoman/modules/scan/ebuild/isebuild.py b/pym/repoman/modules/scan/ebuild/isebuild.py |
292 |
index 514d23e..a8870c7 100644 |
293 |
--- a/pym/repoman/modules/scan/ebuild/isebuild.py |
294 |
+++ b/pym/repoman/modules/scan/ebuild/isebuild.py |
295 |
@@ -9,6 +9,7 @@ from _emerge.RootConfig import RootConfig |
296 |
from repoman._portage import portage |
297 |
|
298 |
from portage import os |
299 |
+from portage.util.futures import InvalidStateError |
300 |
|
301 |
from repoman.qa_data import no_exec, allvars |
302 |
from repoman.modules.scan.scanbase import ScanBase |
303 |
@@ -35,13 +36,13 @@ class IsEbuild(ScanBase): |
304 |
@param checkdirlist: list of files in the current package directory |
305 |
@param checkdir: current package directory path |
306 |
@param xpkg: current package directory being checked |
307 |
- @param validity_fuse: Fuse instance |
308 |
+ @param validity_future: Future instance |
309 |
@returns: dictionary, including {pkgs, can_force} |
310 |
''' |
311 |
checkdirlist = kwargs.get('checkdirlist') |
312 |
checkdir = kwargs.get('checkdir') |
313 |
xpkg = kwargs.get('xpkg') |
314 |
- fuse = kwargs.get('validity_fuse') |
315 |
+ fuse = kwargs.get('validity_future') |
316 |
can_force = kwargs.get('can_force') |
317 |
self.continue_ = False |
318 |
ebuildlist = [] |
319 |
@@ -64,15 +65,24 @@ class IsEbuild(ScanBase): |
320 |
try: |
321 |
myaux = dict(zip(allvars, self.portdb.aux_get(cpv, allvars))) |
322 |
except KeyError: |
323 |
- fuse.pop() |
324 |
+ try: |
325 |
+ fuse.set_result(False) |
326 |
+ except InvalidStateError: |
327 |
+ pass |
328 |
self.qatracker.add_error("ebuild.syntax", os.path.join(xpkg, y)) |
329 |
continue |
330 |
except IOError: |
331 |
- fuse.pop() |
332 |
+ try: |
333 |
+ fuse.set_result(False) |
334 |
+ except InvalidStateError: |
335 |
+ pass |
336 |
self.qatracker.add_error("ebuild.output", os.path.join(xpkg, y)) |
337 |
continue |
338 |
if not portage.eapi_is_supported(myaux["EAPI"]): |
339 |
- fuse.pop() |
340 |
+ try: |
341 |
+ fuse.set_result(False) |
342 |
+ except InvalidStateError: |
343 |
+ pass |
344 |
self.qatracker.add_error("EAPI.unsupported", os.path.join(xpkg, y)) |
345 |
continue |
346 |
pkgs[pf] = Package( |
347 |
@@ -86,7 +96,10 @@ class IsEbuild(ScanBase): |
348 |
# metadata leads to false positives for several checks, and false |
349 |
# positives confuse users. |
350 |
self.continue_ = True |
351 |
- can_force.pop() |
352 |
+ try: |
353 |
+ fuse.set_result(False) |
354 |
+ except InvalidStateError: |
355 |
+ pass |
356 |
|
357 |
return {'continue': self.continue_, 'pkgs': pkgs} |
358 |
|
359 |
diff --git a/pym/repoman/modules/scan/metadata/unused.py b/pym/repoman/modules/scan/metadata/unused.py |
360 |
index 6def48f..114c1f1 100644 |
361 |
--- a/pym/repoman/modules/scan/metadata/unused.py |
362 |
+++ b/pym/repoman/modules/scan/metadata/unused.py |
363 |
@@ -1,4 +1,6 @@ |
364 |
|
365 |
+from portage.util.futures import InvalidStateError |
366 |
+ |
367 |
|
368 |
class UnusedCheck(object): |
369 |
'''Checks and reports any un-used metadata.xml use flag descriptions''' |
370 |
@@ -16,14 +18,18 @@ class UnusedCheck(object): |
371 |
@param xpkg: the pacakge being checked |
372 |
@param muselist: use flag list |
373 |
@param used_useflags: use flag list |
374 |
- @param validity_fuse: Fuse instance |
375 |
+ @param validity_future: Future instance |
376 |
''' |
377 |
xpkg = kwargs.get('xpkg') |
378 |
muselist = kwargs.get('muselist') |
379 |
used_useflags = kwargs.get('used_useflags') |
380 |
+ try: |
381 |
+ valid_state = kwargs['validity_future'].result() |
382 |
+ except InvalidStateError: |
383 |
+ valid_state = True |
384 |
# check if there are unused local USE-descriptions in metadata.xml |
385 |
# (unless there are any invalids, to avoid noise) |
386 |
- if kwargs.get('validity_fuse'): |
387 |
+ if valid_state: |
388 |
for myflag in muselist.difference(used_useflags): |
389 |
self.qatracker.add_error( |
390 |
"metadata.warning", |
391 |
diff --git a/pym/repoman/scanner.py b/pym/repoman/scanner.py |
392 |
index a9f56e9..e9a8e20 100644 |
393 |
--- a/pym/repoman/scanner.py |
394 |
+++ b/pym/repoman/scanner.py |
395 |
@@ -9,7 +9,7 @@ import portage |
396 |
from portage import normalize_path |
397 |
from portage import os |
398 |
from portage.output import green |
399 |
-from repoman.fuse import Fuse |
400 |
+from portage.util.futures import Future |
401 |
from repoman.modules.commit import repochecks |
402 |
from repoman.profile import check_profiles, dev_profile_keywords, setup_profile |
403 |
from repoman.repos import repo_metadata |
404 |
@@ -232,7 +232,7 @@ class Scanner(object): |
405 |
'repolevel': self.repolevel, |
406 |
'catdir': catdir, |
407 |
'pkgdir': pkgdir, |
408 |
- 'validity_fuse': Fuse() |
409 |
+ 'validity_future': Future() |
410 |
} |
411 |
# need to set it up for ==> self.modules or some other ordered list |
412 |
for mod in ['Manifests', 'IsEbuild', 'KeywordChecks', 'FileChecks', |
413 |
-- |
414 |
2.7.4 |