From: Dmitry Selyutin Date: Thu, 24 Aug 2017 20:17:12 +0000 (+0300) Subject: [pygnulib] misc. bug fixes and improvements; project => VFS X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=533630648ab510c5fc001cb80d0347c964c7b3f3;p=gnulib.git [pygnulib] misc. bug fixes and improvements; project => VFS --- diff --git a/pygnulib/config.py b/pygnulib/config.py index b8cbf50aed..6058ca1006 100644 --- a/pygnulib/config.py +++ b/pygnulib/config.py @@ -8,7 +8,7 @@ import re class Config: - """The most basic gnulib configuration holder""" + """gnulib generic configuration""" _TABLE_ = { "gnulib" : "", "root" : "", @@ -61,9 +61,9 @@ class Config: def __getitem__(self, key): if key not in Config._TABLE_: - key = key.replace("_", "-") + key = key.replace("-", "_") if key not in Config._TABLE_: - raise KeyError("unsupported option: '%s'" % key) + raise KeyError("unsupported option: %r" % key) return self.__dict__["_table_"][key] @@ -71,7 +71,8 @@ class Config: if key not in Config._TABLE_: key = key.replace("_", "-") if key not in Config._TABLE_: - raise KeyError("unsupported option: '%s'" % key) + raise KeyError("unsupported option: %r" % key) + key = key.replace("-", "_") typeid = type(Config._TABLE_[key]) if key == "lgpl": @@ -81,7 +82,7 @@ class Config: if value < 2.59: raise NotImplementedError("pygnulib ") elif not isinstance(value, typeid): - raise TypeError("'%s' option must be of '%s' type" % (key, typeid)) + raise TypeError("%r option must be of %r type" % (key, typeid)) tests = ["tests", "cxx-tests", "longrunning-tests", "privileged-tests", "unportable-tests"] if key == "all-tests": @@ -93,7 +94,7 @@ class Config: class CachedConfig(Config): - """Cached configuration holder""" + """gnulib cached configuration""" _AUTOCONF_ = { "autoconf" : re.compile(".*AC_PREREQ\\(\\[(.*?)\\]\\)", re.S | re.M), "aux-dir" : re.compile("^AC_CONFIG_AUX_DIR\\(\\[(.*?)\\]\\)$", re.S | re.M), diff --git a/pygnulib/filesystem.py b/pygnulib/filesystem.py new file mode 100644 index 0000000000..2fe67db65d --- /dev/null +++ b/pygnulib/filesystem.py @@ -0,0 +1,111 @@ +#!/usr/bin/python +# encoding: UTF-8 + + +import os + + +from .config import Config +from .module import FileModule + + +class FileSystem: + """gnulib generic virtual file system""" + _SUBST_ = { + "build-aux" : "aux-dir", + "doc" : "doc-base", + "lib" : "source-base", + "m4" : "m4-base", + "tests" : "tests-base", + "tests=lib" : "tests-base", + "po" : "po-base", + } + + def __init__(self, root, config): + if not isinstance(root, str): + raise TypeError("root must be of 'str' type") + if not isinstance(config, Config): + raise TypeError("config must be of 'Config' type") + if not os.path.exists(root): + raise FileNotFoundError(root) + if not os.path.isdir(root): + raise NotADirectoryError(root) + self._config_ = config + self._root_ = os.path.realpath(root) + + + def __getitem__(self, name): + """retrieve the canonical path of the specified file name""" + parts = [] + replaced = False + if not isinstance(name, str): + raise TypeError("name must be of 'str' type") + path = os.path.normpath(name) + if os.path.isabs(path): + raise ValueError("name must be a relative path") + for part in path.split(os.path.sep): + if part == "..": + parts += [part] + continue + if not replaced: + for old, new in FileSystem._SUBST_.items(): + if part == old: + part = self._config_[new] + replaced = True + parts += [part] + path = os.path.sep.join([self._root_] + parts) + if not os.path.exists(path): + raise FileNotFoundError(name) + return path + + + +class GitFileSystem(FileSystem): + """gnulib Git-based virtual file system""" + _EXCLUDE_ = { + "." : str.startswith, + "~" : str.endswith, + "-tests" : str.endswith, + "CVS" : str.startswith, + "ChangeLog" : str.__eq__, + "COPYING" : str.__eq__, + "README" : str.__eq__, + "TEMPLATE" : str.__eq__, + "TEMPLATE-TESTS" : str.__eq__, + "TEMPLATE-EXTENDED" : str.__eq__, + } + + + def __init__(self, root, config): + if not os.path.isdir(root): + raise FileNotFoundError(root) + if not os.path.isdir(os.path.join(root, ".git")): + raise TypeError("%r is not a gnulib repository") + super(GitFileSystem, self).__init__(root, config) + + + def module(self, name): + """instantiate gnulib module by its name""" + if name in GitFileSystem._EXCLUDE_: + raise KeyError("module does not exist") + path = os.path.join(self["modules"], name) + return FileModule(path, name=name) + + + def modules(self): + """iterate over all available modules""" + prefix = self["modules"] + for root, _, files in os.walk(prefix): + names = [] + for name in files: + exclude = False + for key, method in GitFileSystem._EXCLUDE_.items(): + if method(name, key): + exclude = True + break + if not exclude: + names += [name] + for name in names: + path = os.path.join(root, name) + name = path[len(prefix) + 1:] + yield FileModule(path, name=name) diff --git a/pygnulib/module.py b/pygnulib/module.py index 69824f1176..8ad3498885 100644 --- a/pygnulib/module.py +++ b/pygnulib/module.py @@ -2,13 +2,14 @@ # encoding: UTF-8 +import codecs import os import re -import codecs class Module: + """gnulib generic module""" _PATTERN_ = re.compile("") _TABLE_ = { "description" : (0x00, str, None, "Description"), @@ -50,6 +51,13 @@ class Module: return self.__dict__["_name_"] + @property + def dependencies(self): + pattern = re.compile("^([A-Za-z0-9_\\-\\+/]+)(?:\\s+(.+))*$", re.S) + for dep in self.__dict__["_table_"]["dependencies"]: + yield pattern.findall(dep)[0] + + def __hash__(self): return hash(str(self)) @@ -84,16 +92,17 @@ class Module: def __getitem__(self, key): if key not in Module._TABLE_: - raise ValueError("unsupported key: '%s'" % key) + raise ValueError("unsupported key: %r" % key) return self.__dict__["_table_"][key] def __setitem__(self, key, value): if key not in Module._TABLE_: - raise ValueError("unsupported key: '%s'" % key) + raise ValueError("unsupported key: %r" % key) typeid = Module._TABLE_[key][1] if not isinstance(value, typeid): - raise TypeError("'%s' key expects '%s' type" % (key, typeid)) + typename = typeid.__name__ + raise TypeError("%r key must be of %r type" % (key, typename)) self.__dict__["_table_"][key] = value @@ -108,7 +117,7 @@ class Module: def __eq__(self, value): if isinstance(value, Module): return (self.__dict__["_name_"] == value.__dict__["_name_"]) \ - and (self.__dict__["_table_"] == value.__dict__["_name_"]) + and (self.__dict__["_table_"] == value.__dict__["_table_"]) return TypeError("cannot compare pygnulib.Module with %r type" % type(value)) def __ne__(self, value): @@ -127,7 +136,7 @@ class Module: class FileModule(Module): - """Read or modify existing gnulib module package""" + """gnulib module text file""" def __init__(self, path, mode="r", name=None, **kwargs): if name is None: @@ -139,10 +148,12 @@ class FileModule(Module): with codecs.open(path, "rb", "UTF-8") as stream: data = stream.read() for key, (_, typeid, pattern, _) in Module._TABLE_.items(): + pattern = re.compile(pattern) match = pattern.findall(data) self[key] = [_ for _ in "".join(match).split("\n") if _.strip()] \ if typeid is list else \ ("\n".join([_.strip() for _ in match]) if match else "") + self["dependencies"] = [_ for _ in self["dependencies"] if not _.startswith("#")] self.__dict__["_stream_"] = None elif mode == "w": super(FileModule, self).__init__(name) diff --git a/pygnulib/project.py b/pygnulib/project.py deleted file mode 100644 index 7c01986505..0000000000 --- a/pygnulib/project.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python -# encoding: UTF-8 - - -import os - - -from .config import Config -from .module import FileModule - - -class Project: - """gnulib project directory""" - _SUBST_ = { - "build-aux" : "aux-dir", - "doc" : "doc-base", - "lib" : "source-base", - "m4" : "m4-base", - "tests" : "tests-base", - "tests=lib" : "tests-base", - "po" : "po-base", - } - - def __init__(self, root, **kwargs): - if not isinstance(root, str): - raise TypeError("root must be of 'str' type") - if not os.path.exists(root): - raise FileNotFoundError(root) - if not os.path.isdir(root): - raise NotADirectoryError(root) - self._root_ = root - self._config_ = Config(**kwargs) - - - @property - def root(self): - return self._root_ - - - def path(self, name): - parts = [] - replaced = False - if not isinstance(name, str): - raise TypeError("src must be of 'str' type") - name = os.path.normpath(name) - if os.path.isabs(name): - raise ValueError("name must be a relative path") - for part in name.split(os.path.sep): - if part == "..": - parts += [part] - continue - if not replaced: - for old, new in Project._SUBST_.items(): - if part == old: - part = self._config_[new] - replaced = True - parts += [part] - return os.path.sep.join(parts) - - - def exists(self, name): - path = self.path(name) - return os.path.exists(path) - - - def stat(self, name): - return os.stat(self.path(name)) - - - -class GitRepository(Project): - """gnulib Git repository""" - _EXCLUDE_ = { - "CVS", - "ChangeLog", - "COPYING", - "README", - "TEMPLATE", - "TEMPLATE-EXTENDED", - } - - - def __init__(self, root, **kwargs): - if not os.path.isdir(root): - raise FileNotFoundError(root) - if not os.path.isdir(os.path.join(root, ".git")): - raise TypeError("%r is not a gnulib repository") - super(GitRepository, self).__init__(root, **kwargs) - - - def module(self, name): - if name in GitRepository._EXCLUDE_: - raise KeyError("module does not exist") - path = os.path.join(self.root, "modules", name) - return FileModule(path, name=name) - - - def modules(self): - for root, _, files in os.walk(os.path.join(self.root, "modules")): - for name in [_ for _ in files if _ not in GitRepository._EXCLUDE_]: - path = os.path.join(root, name) - yield FileModule(path, name=name)