Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / dulwich / archive.py @ 959

History | View | Annotate | Download (3.98 KB)

1
# archive.py -- Creating an archive from a tarball
2
# Copyright (C) 2015 Jonas Haag <jonas@lophus.org>
3
# Copyright (C) 2015 Jelmer Vernooij <jelmer@jelmer.uk>
4
#
5
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
6
# General Public License as public by the Free Software Foundation; version 2.0
7
# or (at your option) any later version. You can redistribute it and/or
8
# modify it under the terms of either of these two licenses.
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
#
16
# You should have received a copy of the licenses; if not, see
17
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
18
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
19
# License, Version 2.0.
20
#
21

    
22
"""Generates tarballs for Git trees.
23

24
"""
25

    
26
import posixpath
27
import stat
28
import tarfile
29
from io import BytesIO
30
from contextlib import closing
31

    
32

    
33
class ChunkedBytesIO(object):
34
    """Turn a list of bytestrings into a file-like object.
35

36
    This is similar to creating a `BytesIO` from a concatenation of the
37
    bytestring list, but saves memory by NOT creating one giant bytestring first::
38

39
        BytesIO(b''.join(list_of_bytestrings)) =~= ChunkedBytesIO(list_of_bytestrings)
40
    """
41
    def __init__(self, contents):
42
        self.contents = contents
43
        self.pos = (0, 0)
44

    
45
    def read(self, maxbytes=None):
46
        if maxbytes < 0:
47
            maxbytes = float('inf')
48

    
49
        buf = []
50
        chunk, cursor = self.pos
51

    
52
        while chunk < len(self.contents):
53
            if maxbytes < len(self.contents[chunk]) - cursor:
54
                buf.append(self.contents[chunk][cursor:cursor+maxbytes])
55
                cursor += maxbytes
56
                self.pos = (chunk, cursor)
57
                break
58
            else:
59
                buf.append(self.contents[chunk][cursor:])
60
                maxbytes -= len(self.contents[chunk]) - cursor
61
                chunk += 1
62
                cursor = 0
63
                self.pos = (chunk, cursor)
64
        return b''.join(buf)
65

    
66

    
67
def tar_stream(store, tree, mtime, format=''):
68
    """Generate a tar stream for the contents of a Git tree.
69

70
    Returns a generator that lazily assembles a .tar.gz archive, yielding it in
71
    pieces (bytestrings). To obtain the complete .tar.gz binary file, simply
72
    concatenate these chunks.
73

74
    :param store: Object store to retrieve objects from
75
    :param tree: Tree object for the tree root
76
    :param mtime: UNIX timestamp that is assigned as the modification time for
77
        all files
78
    :param format: Optional compression format for tarball
79
    :return: Bytestrings
80
    """
81
    buf = BytesIO()
82
    with closing(tarfile.open(None, "w:%s" % format, buf)) as tar:
83
        for entry_abspath, entry in _walk_tree(store, tree):
84
            try:
85
                blob = store[entry.sha]
86
            except KeyError:
87
                # Entry probably refers to a submodule, which we don't yet support.
88
                continue
89
            data = ChunkedBytesIO(blob.chunked)
90

    
91
            info = tarfile.TarInfo()
92
            info.name = entry_abspath.decode('ascii') # tarfile only works with ascii.
93
            info.size = blob.raw_length()
94
            info.mode = entry.mode
95
            info.mtime = mtime
96

    
97
            tar.addfile(info, data)
98
            yield buf.getvalue()
99
            buf.truncate(0)
100
            buf.seek(0)
101
    yield buf.getvalue()
102

    
103

    
104
def _walk_tree(store, tree, root=b''):
105
    """Recursively walk a dulwich Tree, yielding tuples of
106
    (absolute path, TreeEntry) along the way.
107
    """
108
    for entry in tree.iteritems():
109
        entry_abspath = posixpath.join(root, entry.path)
110
        if stat.S_ISDIR(entry.mode):
111
            for _ in _walk_tree(store, store[entry.sha], entry_abspath):
112
                yield _
113
        else:
114
            yield (entry_abspath, entry)