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