From: Dmitry Selyutin Date: Tue, 26 Sep 2017 19:36:56 +0000 (+0300) Subject: module: transform transitive closure back into function X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=c0800781821fe1e5928d9cff659edf4901b2cdb2;p=gnulib.git module: transform transitive closure back into function --- diff --git a/pygnulib.py b/pygnulib.py index 27235d82e9..677fd1e9b9 100755 --- a/pygnulib.py +++ b/pygnulib.py @@ -13,7 +13,7 @@ from pygnulib.error import CommandLineError from pygnulib.error import UnknownModuleError from pygnulib.config import Base as BaseConfig from pygnulib.config import Cache as CacheConfig -from pygnulib.module import Table as ModuleTable +from pygnulib.module import transitive_closure from pygnulib.parser import CommandLine as CommandLineParser from pygnulib.filesystem import GnulibGit as GnulibGitFS @@ -44,28 +44,28 @@ def extract_hook(program, gnulib, mode, namespace, *args, **kwargs): def import_hook(gnulib, namespace, verbosity, options, *args, **kwargs): (_, _) = (args, kwargs) config = BaseConfig(**namespace) - table = ModuleTable(gnulib.module, config.modules, config.options) + values = transitive_closure(gnulib.module, config.modules, config.options) + (base, full, main, final, tests) = transitive_closure(gnulib.module, config.modules, config.options) + # Print some information about modules. print("Module list with included dependencies (indented):", file=sys.stdout) BOLD_ON = BOLD_OFF = "" mode = os.fstat(sys.stdout.fileno()).st_mode if not (stat.S_ISFIFO(mode) or stat.S_ISREG(mode)): BOLD_ON = "\033[1m" BOLD_OFF = "\033[0m" - for module in sorted(table.final): + for module in sorted(final): manual = module.name in config.modules prefix = " " if manual else " " bold_on = BOLD_ON if manual else "" bold_off = BOLD_OFF if manual else "" print("{0}{1}{2}{3}".format(prefix, bold_on, module.name, bold_off), file=sys.stdout) - if verbosity >= 1: print("Main module list:", file=sys.stdout) - for module in sorted(table.main): + for module in sorted(main): print(" {0}".format(module.name), file=sys.stdout) - print("Tests-related module list:", file=sys.stdout) - for module in sorted(table.tests): + for module in sorted(tests): print(" {0}".format(module.name), file=sys.stdout) return os.EX_OK diff --git a/pygnulib/module.py b/pygnulib/module.py index 780ba58d3b..1159ba30f4 100644 --- a/pygnulib/module.py +++ b/pygnulib/module.py @@ -441,98 +441,69 @@ class File(Base): -class Table: - """module transitive closure result""" - def __init__(self, lookup, modules, options): - """ - Perform a transitive closure, generating a set of module dependencies. - Each iteration over the table yields a tuple of (module, demander, condition). - - If condition is None, but demander is not, there is no special condition for this module. - If demander is None, the module is provided unconditionally (condition is always None). - - lookup must be a callable which obtains a pygnulib module by its name. - modules is an iterable, yielding a module (either name or instance). - options may be any combination of gnulib configuration options. - """ - if not callable(lookup): - raise TypeError("lookup must be a callable") - _type_assert_("options", options, int) - if options & ~_ConfigOption_.All: - raise ValueError("unknown configuration options") - - obsolete = bool(options & _ConfigOption_.Obsolete) - tests = bool(options & _ConfigOption_.Tests) - cxx_tests = bool(options & _ConfigOption_.CXX) - longrunning_tests = bool(options & _ConfigOption_.Longrunning) - privileged_tests = bool(options & _ConfigOption_.Privileged) - unportable_tests = bool(options & _ConfigOption_.Unportable) - modules = set(lookup(module) for module in modules) - - def _transitive_closure_(tests): - queue = set() - previous = set() - current = set() - for module in modules: - current.add((module, None, None)) - while previous != current: - previous.update(current) - for (demander, _, _) in previous: - if demander in queue: - continue - if tests and not demander.name.endswith("-tests"): - try: - name = "{0}-tests".format(demander.name) - current.add((lookup(name), None, None)) - except _UnknownModuleError_: - pass # ignore non-existent tests - for (dependency, condition) in demander.dependencies: - module = lookup(dependency) - exclude = ( - (not obsolete and module.obsolete), - (not cxx_tests and module.cxx_test), - (not longrunning_tests and module.longrunning_test), - (not privileged_tests and module.privileged_test), - (not unportable_tests and module.unportable_test), - ) - if not any(exclude): - condition = condition if condition.strip() else None - current.add((module, demander, condition)) - queue.add(demander) - return current - - self.__with_tests = tests - self.__base = _transitive_closure_(False) - self.__full = _transitive_closure_(True) - - - def __iter__(self): - return iter(self.__full) - - - @property - def main(self): - """an iterator over main module set""" - return iter(set(module for (module, _, _) in self.__base)) - - - @property - def tests(self): - """an iterator over tests-related module set""" - final = self.final - if not self.__with_tests: - final = (module for module in final if module.applicability != "all") - main = (module for module in self.main if module.applicability != "all") - return iter(set(final) ^ set(main)) - - - @property - def final(self): - """an iterator over final module set""" - return self.all if self.__with_tests else self.main - - - @property - def all(self): - """an iterator over all modules""" - return iter(set(module for (module, _, _) in self.__full)) +def transitive_closure(lookup, modules, options): + """ + Perform a transitive closure, generating a set of module dependencies. + Each iteration over the table yields a tuple of (module, demander, condition). + + If condition is None, but demander is not, there is no special condition for this module. + If demander is None, the module is provided unconditionally (condition is always None). + + lookup must be a callable which obtains a pygnulib module by its name. + modules is an iterable, yielding a module (either name or instance). + options may be any combination of gnulib configuration options. + """ + if not callable(lookup): + raise TypeError("lookup must be a callable") + _type_assert_("options", options, int) + if options & ~_ConfigOption_.All: + raise ValueError("unknown configuration options") + + obsolete = bool(options & _ConfigOption_.Obsolete) + tests = bool(options & _ConfigOption_.Tests) + cxx_tests = bool(options & _ConfigOption_.CXX) + longrunning_tests = bool(options & _ConfigOption_.Longrunning) + privileged_tests = bool(options & _ConfigOption_.Privileged) + unportable_tests = bool(options & _ConfigOption_.Unportable) + modules = set(lookup(module) for module in modules) + + def _transitive_closure_(tests): + queue = set() + previous = set() + current = set() + for module in modules: + current.add((module, None, None)) + while previous != current: + previous.update(current) + for (demander, _, _) in previous: + if demander in queue: + continue + if tests and not demander.name.endswith("-tests"): + try: + name = "{0}-tests".format(demander.name) + current.add((lookup(name), None, None)) + except _UnknownModuleError_: + pass # ignore non-existent tests + for (dependency, condition) in demander.dependencies: + module = lookup(dependency) + exclude = ( + (not obsolete and module.obsolete), + (not cxx_tests and module.cxx_test), + (not longrunning_tests and module.longrunning_test), + (not privileged_tests and module.privileged_test), + (not unportable_tests and module.unportable_test), + ) + if not any(exclude): + condition = condition if condition.strip() else None + current.add((module, demander, condition)) + queue.add(demander) + return current + + base = _transitive_closure_(False) + full = _transitive_closure_(True) + main = set(module for (module, _, _) in base) + final = set(module for (module, _, _) in full) if tests else set(main) + tests_final = set(module for module in final if not tests or module.applicability != "all") + tests_main = set(module for module in main if module.applicability != "all") + tests = (tests_final ^ tests_main) + return (base, full, main, final, tests)