changeset 39039:cf5c7b3ad70b

vfs: integrate file operations into the base VFS
author Dmitry Selyutin <ghostmansd@gmail.com>
date Thu, 19 Oct 2017 10:10:13 +0300
parents fabec54a24f2
children 440c72b5b26e
files pygnulib/vfs.py
diffstat 1 files changed, 64 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/pygnulib/vfs.py	Wed Oct 18 10:52:27 2017 +0300
+++ b/pygnulib/vfs.py	Thu Oct 19 10:10:13 2017 +0300
@@ -18,6 +18,14 @@
 
 
 
+class Type:
+    """VFS file types"""
+    Root = 1
+    Local = 2
+    Dynamic = 3
+
+
+
 class Base:
     """gnulib generic virtual file system"""
     def __init__(self, path, **table):
@@ -69,6 +77,62 @@
         return self.__path
 
 
+    def backup(self, name):
+        """Backup the given file."""
+        backup = "{}~".format(name)
+        try:
+            _os_.unlink(self[backup])
+        except FileNotFoundError:
+            pass # ignore non-existent files
+        _shutil_.copy(self[name], self[backup])
+
+
+    def lookup(self, name, root, local, patch="patch"):
+        """
+        Try to look up a regular file inside virtual file systems or combine it via patch utility.
+
+        - file is present only inside the root VFS: open the file.
+        - file is present inside the local VFS: open the local file.
+        - both file and patch are present: combine in memory.
+        - file is not present: raise an FileNotFoundError exception.
+
+        The function returns a pair of values, representing the file path and type.
+        The first element, path, is just a regular file system path.
+        The second element, type, is an integer indicating the file type.
+        Note that it is the caller's responsibility to remove all temporary files.
+        """
+        _type_assert_("root", root, Base)
+        _type_assert_("local", local, Base)
+        _type_assert_("patch", patch, str)
+        if name in local:
+            return (local[name], Type.Local)
+        diff = "{}.diff".format(name)
+        if diff not in local:
+            return (root[name], Type.Root)
+        tmp = _tempfile_.NamedTemporaryFile(mode="w+b", delete=False)
+        with _codecs_.open(root[name], "rb") as stream:
+            _shutil_.copyfileobj(stream, tmp)
+            tmp.close()
+        stdin = _codecs_.open(local[diff], "rb")
+        cmd = (patch, "-s", tmp.name)
+        pipes = _sp_.Popen(cmd, stdin=stdin, stdout=_sp_.PIPE, stderr=_sp_.PIPE)
+        (stdout, stderr) = pipes.communicate()
+        stdout = stdout.decode("UTF-8")
+        stderr = stderr.decode("UTF-8")
+        returncode = pipes.returncode
+        if returncode != 0:
+            cmd = "patch -s {} < {}".format(tmp.name, local[diff])
+            raise _sp_.CalledProcessError(returncode, cmd, stdout, stderr)
+        return (tmp.name, Type.Dynamic)
+
+
+    def unlink(self, name, backup=True):
+        """Unlink a file, backing it up if necessary."""
+        if backup:
+            self.backup(name)
+        _os_.unlink(self[name])
+
+
 
 class GnulibGit(Base):
     """gnulib git repository"""
@@ -134,36 +198,3 @@
                 path = _os_.path.join(root, name)
                 name = path[len(prefix) + 1:]
                 yield self.module(name, full)
-
-
-
-def lookup(name, root, local, patch="patch"):
-    """
-    Look up a file inside base VFS or local VFS, or combine it using patch.
-    If file is available or can be generated via patching, return a readable stream.
-    """
-    _type_assert_("root", root, Base)
-    _type_assert_("local", local, Base)
-    _type_assert_("patch", patch, str)
-    if name in local:
-        return _codecs_.open(local[name], "rb")
-    diff = "{}.diff".format(name)
-    if diff not in local:
-        return _codecs_.open(root[name], "rb")
-    tmp = _tempfile_.NamedTemporaryFile(mode="w+b", delete=False)
-    with _codecs_.open(root[name], "rb") as stream:
-        _shutil_.copyfileobj(stream, tmp)
-        tmp.close()
-    stdin = _codecs_.open(local[diff], "rb")
-    cmd = (patch, "-s", tmp.name)
-    pipes = _sp_.Popen(cmd, stdin=stdin, stdout=_sp_.PIPE, stderr=_sp_.PIPE)
-    (stdout, stderr) = pipes.communicate()
-    stdout = stdout.decode("UTF-8")
-    stderr = stderr.decode("UTF-8")
-    returncode = pipes.returncode
-    if returncode != 0:
-        cmd = "patch -s {} < {}".format(tmp.name, local[diff])
-        raise _sp_.CalledProcessError(returncode, cmd, stdout, stderr)
-    stream = _codecs_.open(tmp.name, "rb")
-    _os_.unlink(tmp.name)
-    return stream