Add minimal vfs interface
authorGuido Günther <agx@sigxcpu.org>
Mon, 8 Apr 2013 08:20:23 +0000 (10:20 +0200)
committerGuido Günther <agx@sigxcpu.org>
Sat, 13 Apr 2013 12:26:24 +0000 (14:26 +0200)
so we can access blobs in git as file like objects

gbp/git/vfs.py [new file with mode: 0644]
tests/test_GitVfs.py [new file with mode: 0644]

diff --git a/gbp/git/vfs.py b/gbp/git/vfs.py
new file mode 100644 (file)
index 0000000..81649eb
--- /dev/null
@@ -0,0 +1,65 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2013 Guido Günther <agx@sigxcpu.org>
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""Make blobs in a git repository accessible as file like objects"""
+
+import StringIO
+from  gbp.git.repository import GitRepositoryError
+
+class GitVfs(object):
+
+    class _File(object):
+        """
+        A file like object representing a file in git
+
+        @todo: We don't support any byte ranges yet.
+        """
+        def __init__(self, content):
+            self._iter = iter
+            self._data = StringIO.StringIO(content)
+
+        def readline(self):
+            return self._data.readline()
+
+        def readlines(self):
+            return self._data.readlines()
+
+        def read(self, size=None):
+            return self._data.read(size)
+
+        def close(self):
+            return self.close()
+
+    def __init__(self, repo, committish=None):
+        """
+        Access files in a unpaced Debian source package.
+
+        @param repo: the git repository to act on
+        @param committish: the committish to act on
+        """
+        self._repo = repo
+        self._committish = committish or 'HEAD'
+
+    def open(self, path, flags=None):
+        flags = flags or 'r'
+
+        if flags != 'r':
+            raise NotImplementedError("Only reading supported so far")
+        try:
+            return GitVfs._File(self._repo.show(
+                "%s:%s" % (self._committish, path)))
+        except GitRepositoryError as e:
+            raise IOError(e)
diff --git a/tests/test_GitVfs.py b/tests/test_GitVfs.py
new file mode 100644 (file)
index 0000000..c4e2694
--- /dev/null
@@ -0,0 +1,55 @@
+# vim: set fileencoding=utf-8 :
+
+"""
+Test L{gbp.git.GitVfs}
+"""
+
+import os
+import gbp.log
+
+from . import context
+
+gbp.log.setup(color=False, verbose=True)
+
+def test_read():
+    repo_dir = context.new_tmpdir(__name__)
+    """
+    Create a repository
+
+    Methods tested:
+         - L{gbp.git.GitVfs.open}
+         - L{gbp.git._File.readline}
+         - L{gbp.git._File.readlines}
+         - L{gbp.git._File.read}
+         - L{gbp.git._File.close}
+
+    >>> import os, gbp.git.vfs
+    >>> repo = gbp.git.GitRepository.create(str(repo_dir))
+    >>> f = file(os.path.join(repo.path, 'foo.txt'), 'w')
+    >>> content = 'al pha\\na\\nb\\nc'
+    >>> f.write('al pha\\na\\nb\\nc')
+    >>> f.close()
+    >>> repo.add_files(repo.path, force=True)
+    >>> repo.commit_all(msg="foo")
+    >>> vfs = gbp.git.vfs.GitVfs(repo, 'HEAD')
+    >>> gf = vfs.open('foo.txt')
+    >>> gf.readline()
+    'al pha\\n'
+    >>> gf.readline()
+    'a\\n'
+    >>> gf.readlines()
+    ['b\\n', 'c']
+    >>> gf.readlines()
+    []
+    >>> gf.readline()
+    ''
+    >>> gf.readline()
+    ''
+    >>> gbp.git.vfs.GitVfs(repo, 'HEAD').open('foo.txt').read() == content
+    True
+    >>> gf = vfs.open('doesnotexist')
+    Traceback (most recent call last):
+    ...
+    IOError: can't get HEAD:doesnotexist: fatal: Path 'doesnotexist' does not exist in 'HEAD'
+    >>> context.teardown()
+    """