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 |