Bläddra i källkod

Allow opening pack objects from memory.

Jelmer Vernooij 15 år sedan
förälder
incheckning
d01ccc7451
1 ändrade filer med 47 tillägg och 15 borttagningar
  1. 47 15
      dulwich/pack.py

+ 47 - 15
dulwich/pack.py

@@ -128,22 +128,31 @@ def iter_sha1(iter):
     return sha1.hexdigest()
     return sha1.hexdigest()
 
 
 
 
-def load_pack_index(filename):
+def load_pack_index(path):
     """Load an index file by path.
     """Load an index file by path.
 
 
     :param filename: Path to the index file
     :param filename: Path to the index file
     """
     """
-    f = GitFile(filename, 'rb')
+    f = GitFile(path, 'rb')
+    return load_pack_index_file(path, f)
+
+
+def load_pack_index_file(path, f):
+    """Load an index file from a file-like object.
+
+    :param path: Path for the index file
+    :param f: File-like object
+    """
     if f.read(4) == '\377tOc':
     if f.read(4) == '\377tOc':
         version = struct.unpack(">L", f.read(4))[0]
         version = struct.unpack(">L", f.read(4))[0]
         if version == 2:
         if version == 2:
             f.seek(0)
             f.seek(0)
-            return PackIndex2(filename, file=f)
+            return PackIndex2(path, file=f)
         else:
         else:
             raise KeyError("Unknown pack index format %d" % version)
             raise KeyError("Unknown pack index format %d" % version)
     else:
     else:
         f.seek(0)
         f.seek(0)
-        return PackIndex1(filename, file=f)
+        return PackIndex1(path, file=f)
 
 
 
 
 def bisect_find_sha(start, end, sha, unpack_name):
 def bisect_find_sha(start, end, sha, unpack_name):
@@ -183,7 +192,7 @@ class PackIndex(object):
     the start and end offset and then bisect in to find if the value is present.
     the start and end offset and then bisect in to find if the value is present.
     """
     """
   
   
-    def __init__(self, filename, file=None):
+    def __init__(self, filename, file=None, size=None):
         """Create a pack index object.
         """Create a pack index object.
     
     
         Provide it with the name of the index file to consider, and it will map
         Provide it with the name of the index file to consider, and it will map
@@ -192,13 +201,22 @@ class PackIndex(object):
         self._filename = filename
         self._filename = filename
         # Take the size now, so it can be checked each time we map the file to
         # Take the size now, so it can be checked each time we map the file to
         # ensure that it hasn't changed.
         # ensure that it hasn't changed.
-        self._size = os.path.getsize(filename)
         if file is None:
         if file is None:
             self._file = GitFile(filename, 'rb')
             self._file = GitFile(filename, 'rb')
         else:
         else:
             self._file = file
             self._file = file
-        self._contents = mmap.mmap(self._file.fileno(), self._size,
-            access=mmap.ACCESS_READ)
+        fileno = getattr(self._file, 'fileno', None)
+        if fileno is not None:
+            if size is None:
+                self._size = os.path.getsize(filename)
+            else:
+                self._size = size
+            self._contents = mmap.mmap(self._file.fileno(), self._size,
+                access=mmap.ACCESS_READ)
+        else:
+            self._file.seek(0)
+            self._contents = self._file.read()
+            self._size = len(self._contents)
   
   
     def __eq__(self, other):
     def __eq__(self, other):
         if not isinstance(other, PackIndex):
         if not isinstance(other, PackIndex):
@@ -329,8 +347,8 @@ class PackIndex(object):
 class PackIndex1(PackIndex):
 class PackIndex1(PackIndex):
     """Version 1 Pack Index."""
     """Version 1 Pack Index."""
 
 
-    def __init__(self, filename, file=None):
-        PackIndex.__init__(self, filename, file)
+    def __init__(self, filename, file=None, size=None):
+        PackIndex.__init__(self, filename, file, size)
         self.version = 1
         self.version = 1
         self._fan_out_table = self._read_fan_out_table(0)
         self._fan_out_table = self._read_fan_out_table(0)
 
 
@@ -355,8 +373,8 @@ class PackIndex1(PackIndex):
 class PackIndex2(PackIndex):
 class PackIndex2(PackIndex):
     """Version 2 Pack Index."""
     """Version 2 Pack Index."""
 
 
-    def __init__(self, filename, file=None):
-        PackIndex.__init__(self, filename, file)
+    def __init__(self, filename, file=None, size=None):
+        PackIndex.__init__(self, filename, file, size)
         assert self._contents[:4] == '\377tOc', "Not a v2 pack index file"
         assert self._contents[:4] == '\377tOc', "Not a v2 pack index file"
         (self.version, ) = unpack_from(">L", self._contents, 4)
         (self.version, ) = unpack_from(">L", self._contents, 4)
         assert self.version == 2, "Version was %d" % self.version
         assert self.version == 2, "Version was %d" % self.version
@@ -467,7 +485,7 @@ class PackData(object):
     It will all just throw a zlib or KeyError.
     It will all just throw a zlib or KeyError.
     """
     """
   
   
-    def __init__(self, filename):
+    def __init__(self, filename, file=None, size=None):
         """Create a PackData object that represents the pack in the given filename.
         """Create a PackData object that represents the pack in the given filename.
     
     
         The file must exist and stay readable until the object is disposed of. It
         The file must exist and stay readable until the object is disposed of. It
@@ -477,14 +495,28 @@ class PackData(object):
         mmap implementation is flawed.
         mmap implementation is flawed.
         """
         """
         self._filename = filename
         self._filename = filename
-        self._size = os.path.getsize(filename)
+        if size is None:
+            self._size = os.path.getsize(filename)
+        else:
+            self._size = size
         self._header_size = 12
         self._header_size = 12
         assert self._size >= self._header_size, "%s is too small for a packfile (%d < %d)" % (filename, self._size, self._header_size)
         assert self._size >= self._header_size, "%s is too small for a packfile (%d < %d)" % (filename, self._size, self._header_size)
-        self._file = GitFile(self._filename, 'rb')
+        if file is None:
+            self._file = GitFile(self._filename, 'rb')
+        else:
+            self._file = file
         self._read_header()
         self._read_header()
         self._offset_cache = LRUSizeCache(1024*1024*20, 
         self._offset_cache = LRUSizeCache(1024*1024*20, 
             compute_size=_compute_object_size)
             compute_size=_compute_object_size)
 
 
+    @classmethod
+    def from_file(cls, file, size):
+        return cls(str(file), file=file, size=size)
+
+    @classmethod
+    def from_path(cls, path):
+        return cls(filename=path)
+
     def close(self):
     def close(self):
         self._file.close()
         self._file.close()