from .error import type_assert as _type_assert_
-from .error import GnulibModuleNotFoundError as _GnulibModuleNotFoundError_
+from .error import UnknownModuleError as _UnknownModuleError_
from .config import Base as _BaseConfig_
from .module import Base as _BaseModule_
from .module import File as _FileModule_
try:
return _FileModule_(path, name=name) if full else _BaseModule_(name)
except FileNotFoundError:
- raise _GnulibModuleNotFoundError_(name)
+ raise _UnknownModuleError_(name)
def modules(self, full=True):
path = _os_.path.join(root, name)
name = path[len(prefix) + 1:]
yield self.module(name, full)
-
-
- def transitive_closure(self, config, modules, tests):
- """
- GnulibGit.transitive_closure(config, modules, tests) ->
- set((
- (module0, demander0, condition0),
- (moduleN, demanderN, conditionN),
- ))
-
- config is a gnulib configuration (e.g. pygnulib.Config.Base instance).
- modules is an iterable, yielding a module (either name or instance).
- tests designates whether to consider the corresponding test module and its dependencies.
-
- The returned value is a set, containing a sequence of tuples (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).
- The correct iteration over the resulting table is shown below:
-
- for (module, demander, condition) in gnulib.transitive_closure(modules):
- if demander is not None:
- if condition is not None:
- fmt = "{0} is added as dependency from {1} under '{2}' condition"
- args = (module.name, demander.name, condition)
- else:
- fmt = "{0} is added as dependency from {1} without specific condition"
- args = (module.name, demander.name)
- else:
- fmt = "{0} is added unconditionally"
- args = (module.name,)
- print(fmt.format(args))
- """
- demanders = set()
- (old, new) = (set(), set())
- for module in modules:
- if isinstance(module, str):
- module = self.module(module)
- new.add((module, None, None))
- while old != new:
- old.update(new)
- for (demander, _, _) in old:
- if demander in demanders:
- continue
- if tests:
- try:
- name = "{0}-tests".format(demander.name)
- new.add((self.module(name), None, None))
- except _GnulibModuleNotFoundError_:
- pass # ignore non-existent tests
- for (dependency, condition) in demander.dependencies:
- module = self.module(dependency)
- exclude = (
- (not config.obsolete and module.obsolete),
- (not config.cxx_tests and module.cxx_test),
- (not config.longrunning_tests and module.longrunning_test),
- (not config.privileged_tests and module.privileged_test),
- (not config.unportable_tests and module.unportable_test),
- )
- if any(exclude):
- continue
- condition = condition if condition.strip() else None
- new.add((module, demander, condition))
- demanders.add(demander)
- return new
from .error import type_assert as _type_assert_
+from .error import UnknownModuleError as _UnknownModuleError_
+from .config import Option as _ConfigOption_
def __lt__(self, value):
+ if not isinstance(value, Base):
+ return True
return self.name < value.name
def __le__(self, value):
return self.__lt__(value) or self.__eq__(value)
def __eq__(self, value):
+ if not isinstance(value, Base):
+ return False
return self.name == value.name
def __ne__(self, value):
def __exit__(self, exctype, excval, exctrace):
self.close()
+
+
+
+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, _ConfigOption_)
+
+ 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))