commit: edd2994047479bbc3460d90ae13a883cb331630d Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Sat Apr 16 20:24:06 2016 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Sat Apr 16 20:47:52 2016 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=edd29940
repoman: replace Fuse with Future Replace Fuse with Future, which is similar more generic. The code ends up being slightly more verbose, but more flexible. The Future class will be useful elsewhere, including the EventLoop class. pym/portage/util/futures.py | 118 ++++++++++++++++++++++++++++ pym/repoman/fuse.py | 68 ---------------- pym/repoman/main.py | 12 ++- pym/repoman/modules/scan/ebuild/ebuild.py | 10 ++- pym/repoman/modules/scan/ebuild/isebuild.py | 25 ++++-- pym/repoman/modules/scan/metadata/unused.py | 10 ++- pym/repoman/scanner.py | 4 +- 7 files changed, 164 insertions(+), 83 deletions(-) diff --git a/pym/portage/util/futures.py b/pym/portage/util/futures.py new file mode 100644 index 0000000..c648f10 --- /dev/null +++ b/pym/portage/util/futures.py @@ -0,0 +1,118 @@ +# Copyright 2016 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', +) + +try: + from asyncio import ( + CancelledError, + Future, + InvalidStateError, + ) +except ImportError: + + from portage.exception import PortageException + + _PENDING = 'PENDING' + _CANCELLED = 'CANCELLED' + _FINISHED = 'FINISHED' + + class Error(PortageException): + pass + + class CancelledError(Error): + def __init__(self): + Error.__init__(self, "cancelled") + + class InvalidStateError(Error): + pass + + class Future(object): + + # Class variables serving as defaults for instance variables. + _state = _PENDING + _result = None + _exception = None + + 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 + return True + + 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 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 + + 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 diff --git a/pym/repoman/fuse.py b/pym/repoman/fuse.py deleted file mode 100644 index ac864fd..0000000 --- a/pym/repoman/fuse.py +++ /dev/null @@ -1,68 +0,0 @@ - -''' -fuse.py - -A tiny one-time-fuse class that uses a boolean to mimic the property of -an electrical fuse. IT's good (True) until it is popped (bad, False). -It is not resetable. -''' - - -class Fuse(object): - '''A One time fuse style boolean instance''' - - __slots__ = ('_state') - - def __init__(self): - self._state = True - - def pop(self): - '''Blow's the fuse state (makes it False)''' - self._state = False - - def __repr__(self): - '''x.__repr__() <==> repr(x)''' - return repr(self._state>0) - - def __str__(self): - '''x.__str__() <==> str(x)''' - return ['False', 'True'][self._state] - - def __bool__(self): - '''self != 0''' - return self._state != 0 - - def __nonzero__(self): - '''self != 0''' - return self._state != 0 - - def __abs__(self): - '''x.__abs__() <==> abs(x)''' - return [0, 1] [self._state] - - def __int__(self): - '''int(self)''' - return [0, 1][self._state] - - def __eq__(self, value): - '''Return self==value.''' - return self._state == value - - def __ne__(self, value): - '''Return self!=value.''' - return self._state != value - - def __ge__(self, value): - '''Return self>=value.''' - return self._state >= value - - def __gt__(self, value): - return self._state > value - - def __le__(self, value): - '''Return self<=value.''' - return self._state <= value - - def __lt__(self, value): - '''Return self<value.''' - return self._state < value diff --git a/pym/repoman/main.py b/pym/repoman/main.py index 2ccda99..62c3c2c 100755 --- a/pym/repoman/main.py +++ b/pym/repoman/main.py @@ -22,10 +22,13 @@ import portage.repository.config from portage.output import create_color_func, nocolor from portage.output import ConsoleStyleFile, StyleWriter from portage.util import formatter +from portage.util.futures import ( + Future, + InvalidStateError, +) from repoman.actions import Actions from repoman.argparser import parse_args -from repoman.fuse import Fuse from repoman.qa_data import ( format_qa_output, format_qa_output_column, qahelp, qawarnings, qacats) @@ -76,7 +79,7 @@ def repoman_main(argv): # Set this to False when an extraordinary issue (generally # something other than a QA issue) makes it impossible to # commit (like if Manifest generation fails). - can_force = Fuse() + can_force = Future() portdir, portdir_overlay, mydir = utilities.FindPortdir(repoman_settings) if portdir is None: @@ -171,6 +174,11 @@ def repoman_main(argv): qa_output = qa_output.getvalue() qa_output = qa_output.splitlines(True) + try: + can_force = can_force.result() + except InvalidStateError: + can_force = True + # output the results actions = Actions(repo_settings, options, scanner, vcs_settings) if actions.inform(can_force, result): diff --git a/pym/repoman/modules/scan/ebuild/ebuild.py b/pym/repoman/modules/scan/ebuild/ebuild.py index 540411f..67eee3f 100644 --- a/pym/repoman/modules/scan/ebuild/ebuild.py +++ b/pym/repoman/modules/scan/ebuild/ebuild.py @@ -8,6 +8,7 @@ from repoman.modules.scan.scanbase import ScanBase # import our initialized portage instance from repoman._portage import portage from portage import os +from portage.util.futures import InvalidStateError pv_toolong_re = re.compile(r'[0-9]{19,}') @@ -127,15 +128,18 @@ class Ebuild(ScanBase): def pkg_invalid(self, **kwargs): '''Sets some pkg info and checks for invalid packages - @param validity_fuse: Fuse instance + @param validity_future: Future instance @returns: dictionary, including {pkg object} ''' - fuse = kwargs.get('validity_fuse') + fuse = kwargs.get('validity_future') if self.pkg.invalid: for k, msgs in self.pkg.invalid.items(): for msg in msgs: self.qatracker.add_error(k, "%s: %s" % (self.relative_path, msg)) - fuse.pop() + try: + fuse.set_result(False) + except InvalidStateError: + pass return {'continue': True, 'pkg': self.pkg} return {'continue': False, 'pkg': self.pkg} diff --git a/pym/repoman/modules/scan/ebuild/isebuild.py b/pym/repoman/modules/scan/ebuild/isebuild.py index 514d23e..a8870c7 100644 --- a/pym/repoman/modules/scan/ebuild/isebuild.py +++ b/pym/repoman/modules/scan/ebuild/isebuild.py @@ -9,6 +9,7 @@ from _emerge.RootConfig import RootConfig from repoman._portage import portage from portage import os +from portage.util.futures import InvalidStateError from repoman.qa_data import no_exec, allvars from repoman.modules.scan.scanbase import ScanBase @@ -35,13 +36,13 @@ class IsEbuild(ScanBase): @param checkdirlist: list of files in the current package directory @param checkdir: current package directory path @param xpkg: current package directory being checked - @param validity_fuse: Fuse instance + @param validity_future: Future instance @returns: dictionary, including {pkgs, can_force} ''' checkdirlist = kwargs.get('checkdirlist') checkdir = kwargs.get('checkdir') xpkg = kwargs.get('xpkg') - fuse = kwargs.get('validity_fuse') + fuse = kwargs.get('validity_future') can_force = kwargs.get('can_force') self.continue_ = False ebuildlist = [] @@ -64,15 +65,24 @@ class IsEbuild(ScanBase): try: myaux = dict(zip(allvars, self.portdb.aux_get(cpv, allvars))) except KeyError: - fuse.pop() + try: + fuse.set_result(False) + except InvalidStateError: + pass self.qatracker.add_error("ebuild.syntax", os.path.join(xpkg, y)) continue except IOError: - fuse.pop() + try: + fuse.set_result(False) + except InvalidStateError: + pass self.qatracker.add_error("ebuild.output", os.path.join(xpkg, y)) continue if not portage.eapi_is_supported(myaux["EAPI"]): - fuse.pop() + try: + fuse.set_result(False) + except InvalidStateError: + pass self.qatracker.add_error("EAPI.unsupported", os.path.join(xpkg, y)) continue pkgs[pf] = Package( @@ -86,7 +96,10 @@ class IsEbuild(ScanBase): # metadata leads to false positives for several checks, and false # positives confuse users. self.continue_ = True - can_force.pop() + try: + fuse.set_result(False) + except InvalidStateError: + pass return {'continue': self.continue_, 'pkgs': pkgs} diff --git a/pym/repoman/modules/scan/metadata/unused.py b/pym/repoman/modules/scan/metadata/unused.py index 6def48f..114c1f1 100644 --- a/pym/repoman/modules/scan/metadata/unused.py +++ b/pym/repoman/modules/scan/metadata/unused.py @@ -1,4 +1,6 @@ +from portage.util.futures import InvalidStateError + class UnusedCheck(object): '''Checks and reports any un-used metadata.xml use flag descriptions''' @@ -16,14 +18,18 @@ class UnusedCheck(object): @param xpkg: the pacakge being checked @param muselist: use flag list @param used_useflags: use flag list - @param validity_fuse: Fuse instance + @param validity_future: Future instance ''' xpkg = kwargs.get('xpkg') muselist = kwargs.get('muselist') used_useflags = kwargs.get('used_useflags') + try: + valid_state = kwargs['validity_future'].result() + except InvalidStateError: + valid_state = True # check if there are unused local USE-descriptions in metadata.xml # (unless there are any invalids, to avoid noise) - if kwargs.get('validity_fuse'): + if valid_state: for myflag in muselist.difference(used_useflags): self.qatracker.add_error( "metadata.warning", diff --git a/pym/repoman/scanner.py b/pym/repoman/scanner.py index a9f56e9..e9a8e20 100644 --- a/pym/repoman/scanner.py +++ b/pym/repoman/scanner.py @@ -9,7 +9,7 @@ import portage from portage import normalize_path from portage import os from portage.output import green -from repoman.fuse import Fuse +from portage.util.futures import Future from repoman.modules.commit import repochecks from repoman.profile import check_profiles, dev_profile_keywords, setup_profile from repoman.repos import repo_metadata @@ -232,7 +232,7 @@ class Scanner(object): 'repolevel': self.repolevel, 'catdir': catdir, 'pkgdir': pkgdir, - 'validity_fuse': Fuse() + 'validity_future': Future() } # need to set it up for ==> self.modules or some other ordered list for mod in ['Manifests', 'IsEbuild', 'KeywordChecks', 'FileChecks',