]> Savannah Git Hosting - gnulib.git/commitdiff
[pygnulib] misc. bug fixes and improvements; project => VFS
authorDmitry Selyutin <ghostmansd@gmail.com>
Thu, 24 Aug 2017 20:17:12 +0000 (23:17 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Thu, 24 Aug 2017 20:17:12 +0000 (23:17 +0300)
pygnulib/config.py
pygnulib/filesystem.py [new file with mode: 0644]
pygnulib/module.py
pygnulib/project.py [deleted file]

index b8cbf50aed8a9fd0fe34cc47411df034683670b8..6058ca100612e710a83784063fd272243c06f4e5 100644 (file)
@@ -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 (file)
index 0000000..2fe67db
--- /dev/null
@@ -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)
index 69824f1176e2652fd4070e67535759d90f92efb1..8ad34988855cad9438212ca0418d9a91b349ed4d 100644 (file)
@@ -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 (file)
index 7c01986..0000000
+++ /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)