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