/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
711 by Martin Pool
- store docs
1
# Copyright (C) 2005 by Canonical Development Ltd
1 by mbp at sourcefrog
import from baz patch-364
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1374 by Martin Pool
todo
17
# TODO: Could remember a bias towards whether a particular store is typically
18
# compressed or not.
19
711 by Martin Pool
- store docs
20
"""
21
Stores are the main data-storage mechanism for Bazaar-NG.
1 by mbp at sourcefrog
import from baz patch-364
22
23
A store is a simple write-once container indexed by a universally
711 by Martin Pool
- store docs
24
unique ID.
25
"""
1 by mbp at sourcefrog
import from baz patch-364
26
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
27
from cStringIO import StringIO
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
28
1393.2.3 by John Arbash Meinel
Fixing typos, updating stores, getting tests to pass.
29
from bzrlib.errors import BzrError, UnlistableStore, TransportNotPossible
1104 by Martin Pool
- Add a simple UIFactory
30
from bzrlib.trace import mutter
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
31
import bzrlib.transport
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
32
from bzrlib.transport.local import LocalTransport
1 by mbp at sourcefrog
import from baz patch-364
33
34
######################################################################
35
# stores
36
37
class StoreError(Exception):
38
    pass
39
40
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
41
class Store(object):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
42
    """This class represents the abstract storage layout for saving information.
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
43
    
1 by mbp at sourcefrog
import from baz patch-364
44
    Files can be added, but not modified once they are in.  Typically
45
    the hash is used as the name, or something else known to be unique,
46
    such as a UUID.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
47
    """
48
49
    def __len__(self):
50
        raise NotImplementedError('Children should define their length')
51
52
    def __getitem__(self, fileid):
53
        """Returns a file reading from a particular entry."""
54
        raise NotImplementedError
55
56
    def __contains__(self, fileid):
57
        """"""
58
        raise NotImplementedError
59
60
    def __iter__(self):
61
        raise NotImplementedError
62
907.1.43 by John Arbash Meinel
Restoring compatibility for Storage.add(file, fileid), it is a little arbitrary, and compatibility is better
63
    def add(self, f, fileid):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
64
        """Add a file object f to the store accessible from the given fileid"""
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
65
        raise NotImplementedError('Children of Store must define their method of adding entries.')
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
66
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
67
    def add_multi(self, entries):
68
        """Add a series of file-like or string objects to the store with the given
69
        identities.
70
        
907.1.43 by John Arbash Meinel
Restoring compatibility for Storage.add(file, fileid), it is a little arbitrary, and compatibility is better
71
        :param entries: A list of tuples of file,id pairs [(file1, id1), (file2, id2), ...]
72
                        This could also be a generator yielding (file,id) pairs.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
73
        """
907.1.43 by John Arbash Meinel
Restoring compatibility for Storage.add(file, fileid), it is a little arbitrary, and compatibility is better
74
        for f, fileid in entries:
75
            self.add(f, fileid)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
76
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
77
    def has(self, fileids):
78
        """Return True/False for each entry in fileids.
79
80
        :param fileids: A List or generator yielding file ids.
81
        :return: A generator or list returning True/False for each entry.
82
        """
83
        for fileid in fileids:
84
            if fileid in self:
85
                yield True
86
            else:
87
                yield False
88
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
89
    def listable(self):
90
        """Return True if this store is able to be listed."""
91
        return hasattr(self, "__iter__")
92
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
93
    def get(self, fileids, permit_failure=False, pb=None):
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
94
        """Return a set of files, one for each requested entry.
95
        
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
96
        :param permit_failure: If true, return None for entries which do not 
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
97
                               exist.
98
        :return: A list or generator of file-like objects, one for each id.
99
        """
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
100
        for fileid in fileids:
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
101
            try:
102
                yield self[fileid]
103
            except KeyError:
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
104
                if permit_failure:
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
105
                    yield None
106
                else:
107
                    raise
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
108
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
109
    def copy_multi(self, other, ids, pb=None, permit_failure=False):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
110
        """Copy texts for ids from other into self.
111
112
        If an id is present in self, it is skipped.  A count of copied
113
        ids is returned, which may be less than len(ids).
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
114
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
115
        :param other: Another Store object
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
116
        :param ids: A list of entry ids to be copied
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
117
        :param pb: A ProgressBar object, if none is given, the default will be created.
118
        :param permit_failure: Allow missing entries to be ignored
119
        :return: (n_copied, [failed]) The number of entries copied successfully,
120
            followed by a list of entries which could not be copied (because they
121
            were missing)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
122
        """
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
123
        if pb is None:
124
            pb = bzrlib.ui.ui_factory.progress_bar()
125
1393.1.14 by Martin Pool
doc
126
        # XXX: Is there any reason why we couldn't make this accept a generator
127
        # and build a list as it finds things to copy?
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
128
        ids = list(ids) # Make sure we don't have a generator, since we iterate 2 times
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
129
        pb.update('preparing to copy')
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
130
        to_copy = []
131
        for file_id, has in zip(ids, self.has(ids)):
132
            if not has:
133
                to_copy.append(file_id)
134
        return self._do_copy(other, to_copy, pb, permit_failure=permit_failure)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
135
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
136
    def _do_copy(self, other, to_copy, pb, permit_failure=False):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
137
        """This is the standard copying mechanism, just get them one at
138
        a time from remote, and store them locally.
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
139
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
140
        :param other: Another Store object
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
141
        :param to_copy: A list of entry ids to copy
142
        :param pb: A ProgressBar object to display completion status.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
143
        :param permit_failure: Allow missing entries to be ignored
144
        :return: (n_copied, [failed])
145
            The number of entries copied, and a list of failed entries.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
146
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
147
        # This should be updated to use add_multi() rather than
148
        # the current methods of buffering requests.
149
        # One question, is it faster to queue up 1-10 and then copy 1-10
150
        # then queue up 11-20, copy 11-20
151
        # or to queue up 1-10, copy 1, queue 11, copy 2, etc?
152
        # sort of pipeline versus batch.
907.1.30 by John Arbash Meinel
Updated CompressedTextStore to use copy_to when possible.
153
154
        # We can't use self._transport.copy_to because we don't know
155
        # whether the local tree is in the same format as other
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
156
        failed = set()
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
157
        def buffer_requests():
907.1.26 by John Arbash Meinel
Fixing some store stuff so that 'bzr branch' works.
158
            count = 0
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
159
            buffered_requests = []
160
            for fileid in to_copy:
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
161
                try:
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
162
                    f = other[fileid]
974.1.77 by Aaron Bentley
Fixed branch handling of missing revisions
163
                except KeyError:
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
164
                    if permit_failure:
165
                        failed.add(fileid)
166
                        continue
167
                    else:
168
                        raise
169
170
                buffered_requests.append((f, fileid))
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
171
                if len(buffered_requests) > self._max_buffered_requests:
172
                    yield buffered_requests.pop(0)
173
                    count += 1
174
                    pb.update('copy', count, len(to_copy))
175
176
            for req in buffered_requests:
177
                yield req
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
178
                count += 1
179
                pb.update('copy', count, len(to_copy))
180
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
181
            assert count == len(to_copy)
907.1.26 by John Arbash Meinel
Fixing some store stuff so that 'bzr branch' works.
182
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
183
        self.add_multi(buffer_requests())
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
184
185
        pb.clear()
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
186
        return len(to_copy), failed
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
187
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
188
189
class TransportStore(Store):
190
    """A TransportStore is a Store superclass for Stores that use Transports."""
191
192
    _max_buffered_requests = 10
193
194
    def __init__(self, transport):
195
        assert isinstance(transport, bzrlib.transport.Transport)
196
        super(TransportStore, self).__init__()
197
        self._transport = transport
198
199
    def __repr__(self):
200
        if self._transport is None:
201
            return "%s(None)" % (self.__class__.__name__)
202
        else:
203
            return "%s(%r)" % (self.__class__.__name__, self._transport.base)
204
205
    __str__ = __repr__
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
206
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
207
    def listable(self):
208
        """Return True if this store is able to be listed."""
209
        return self._transport.listable()
210
1092.2.1 by Robert Collins
minor refactors to store, create an ImmutableMemoryStore for testing or other such operations
211
212
class ImmutableMemoryStore(Store):
213
    """A memory only store."""
214
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
215
    def __contains__(self, fileid):
216
        return self._contents.has_key(fileid)
217
1092.2.1 by Robert Collins
minor refactors to store, create an ImmutableMemoryStore for testing or other such operations
218
    def __init__(self):
219
        super(ImmutableMemoryStore, self).__init__()
220
        self._contents = {}
221
222
    def add(self, stream, fileid, compressed=True):
223
        if self._contents.has_key(fileid):
224
            raise StoreError("fileid %s already in the store" % fileid)
225
        self._contents[fileid] = stream.read()
226
227
    def __getitem__(self, fileid):
228
        """Returns a file reading from a particular entry."""
229
        if not self._contents.has_key(fileid):
230
            raise IndexError
231
        return StringIO(self._contents[fileid])
232
233
    def _item_size(self, fileid):
234
        return len(self._contents[fileid])
235
236
    def __iter__(self):
237
        return iter(self._contents.keys())
1092.2.2 by Robert Collins
move RemoteStore to store.py
238
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
239
    def total_size(self):
240
        result = 0
241
        count = 0
242
        for fileid in self:
243
            count += 1
244
            result += self._item_size(fileid)
245
        return count, result
246
        
247
248
class CachedStore(Store):
1092.2.3 by Robert Collins
move CachedStore into store.py
249
    """A store that caches data locally, to avoid repeated downloads.
250
    The precacache method should be used to avoid server round-trips for
251
    every piece of data.
252
    """
253
254
    def __init__(self, store, cache_dir):
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
255
        super(CachedStore, self).__init__()
1092.2.3 by Robert Collins
move CachedStore into store.py
256
        self.source_store = store
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
257
        # This clones the source store type with a locally bound
258
        # transport. FIXME: it assumes a constructor is == cloning.
259
        # clonable store - it might be nicer to actually have a clone()
260
        # or something. RBC 20051003
261
        self.cache_store = store.__class__(LocalTransport(cache_dir))
1092.2.3 by Robert Collins
move CachedStore into store.py
262
263
    def __getitem__(self, id):
264
        mutter("Cache add %s" % id)
265
        if id not in self.cache_store:
266
            self.cache_store.add(self.source_store[id], id)
267
        return self.cache_store[id]
268
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
269
    def __contains__(self, fileid):
270
        if fileid in self.cache_store:
271
            return True
272
        if fileid in self.source_store:
273
            # We could copy at this time
274
            return True
275
        return False
276
277
    def get(self, fileids, permit_failure=False, pb=None):
278
        fileids = list(fileids)
279
        hasids = self.cache_store.has(fileids)
280
        needs = set()
281
        for has, fileid in zip(hasids, fileids):
282
            if not has:
283
                needs.add(fileid)
284
        if needs:
285
            self.cache_store.copy_multi(self.source_store, needs,
286
                    permit_failure=permit_failure)
287
        return self.cache_store.get(fileids,
288
                permit_failure=permit_failure, pb=pb)
289
1092.2.3 by Robert Collins
move CachedStore into store.py
290
    def prefetch(self, ids):
291
        """Copy a series of ids into the cache, before they are used.
292
        For remote stores that support pipelining or async downloads, this can
293
        increase speed considerably.
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
294
1092.3.4 by Robert Collins
update symlink branch to integration
295
        Failures while prefetching are ignored.
1092.2.3 by Robert Collins
move CachedStore into store.py
296
        """
297
        mutter("Prefetch of ids %s" % ",".join(ids))
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
298
        self.cache_store.copy_multi(self.source_store, ids, 
1092.3.4 by Robert Collins
update symlink branch to integration
299
                                    permit_failure=True)
1092.2.19 by Robert Collins
update with integration
300
301
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
302
def copy_all(store_from, store_to):
303
    """Copy all ids from one store to another."""
1393.1.14 by Martin Pool
doc
304
    # TODO: Optional progress indicator
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
305
    if not store_from.listable():
306
        raise UnlistableStore(store_from)
307
    ids = [f for f in store_from]
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
308
    store_to.copy_multi(store_from, ids)
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
309