~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store.py

  • Committer: Robert Collins
  • Date: 2005-09-06 15:39:59 UTC
  • mto: (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050906153959-c79d671a510ca2fc
move RemoteStore to store.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import os, tempfile, types, osutils, gzip, errno
25
25
from stat import ST_SIZE
26
26
from StringIO import StringIO
 
27
from bzrlib.errors import BzrError
27
28
from bzrlib.trace import mutter
28
29
import bzrlib.ui
 
30
from bzrlib.remotebranch import get_url
29
31
 
30
32
######################################################################
31
33
# stores
34
36
    pass
35
37
 
36
38
 
37
 
class ImmutableStore(object):
38
 
    """Store that holds files indexed by unique names.
 
39
class Store(object):
 
40
    """An abstract store that holds files indexed by unique names.
39
41
 
40
42
    Files can be added, but not modified once they are in.  Typically
41
43
    the hash is used as the name, or something else known to be unique,
56
58
    >>> st.add(StringIO('goodbye'), '123123')
57
59
    >>> st['123123'].read()
58
60
    'goodbye'
 
61
    """
 
62
 
 
63
    def total_size(self):
 
64
        """Return (count, bytes)
 
65
 
 
66
        This is the (compressed) size stored on disk, not the size of
 
67
        the content."""
 
68
        total = 0
 
69
        count = 0
 
70
        for fid in self:
 
71
            count += 1
 
72
            total += self._item_size(fid)
 
73
        return count, total
 
74
 
 
75
 
 
76
class ImmutableStore(Store):
 
77
    """Store that stores files on disk.
59
78
 
60
79
    TODO: Atomic add by writing to a temporary file and renaming.
 
80
    TODO: Guard against the same thing being stored twice, compressed and
 
81
          uncompressed during copy_multi_immutable - the window is for a
 
82
          matching store with some crack code that lets it offer a 
 
83
          non gz FOO and then a fz FOO.
61
84
 
62
85
    In bzr 0.0.5 and earlier, files within the store were marked
63
86
    readonly on disk.  This is no longer done but existing stores need
65
88
    """
66
89
 
67
90
    def __init__(self, basedir):
 
91
        super(ImmutableStore, self).__init__()
68
92
        self._basedir = basedir
69
93
 
70
94
    def _path(self, id):
91
115
            
92
116
        p = self._path(fileid)
93
117
        if os.access(p, os.F_OK) or os.access(p + '.gz', os.F_OK):
94
 
            from bzrlib.errors import bailout
95
118
            raise BzrError("store %r already contains id %r" % (self._basedir, fileid))
96
119
 
97
120
        fn = p
114
137
    def copy_multi(self, other, ids, permit_failure=False):
115
138
        """Copy texts for ids from other into self.
116
139
 
117
 
        If an id is present in self, it is skipped.  A count of copied
118
 
        ids is returned, which may be less than len(ids).
 
140
        If an id is present in self, it is skipped.
 
141
 
 
142
        Returns (count_copied, failed), where failed is a collection of ids
 
143
        that could not be copied.
119
144
        """
120
145
        pb = bzrlib.ui.ui_factory.progress_bar()
121
146
        
124
149
        if isinstance(other, ImmutableStore):
125
150
            return self.copy_multi_immutable(other, to_copy, pb)
126
151
        count = 0
 
152
        failed = set()
127
153
        for id in to_copy:
128
154
            count += 1
129
155
            pb.update('copy', count, len(to_copy))
133
159
                try:
134
160
                    entry = other[id]
135
161
                except IndexError:
136
 
                    failures.add(id)
 
162
                    failed.add(id)
137
163
                    continue
138
164
                self.add(entry, id)
139
165
                
140
 
        assert count == len(to_copy)
 
166
        if not permit_failure:
 
167
            assert count == len(to_copy)
141
168
        pb.clear()
142
 
        return count
143
 
 
 
169
        return count, failed
144
170
 
145
171
    def copy_multi_immutable(self, other, to_copy, pb, permit_failure=False):
146
172
        from shutil import copyfile
171
197
        assert count == len(to_copy)
172
198
        pb.clear()
173
199
        return count, failed
174
 
    
175
200
 
176
201
    def __contains__(self, fileid):
177
202
        """"""
179
204
        return (os.access(p, os.R_OK)
180
205
                or os.access(p + '.gz', os.R_OK))
181
206
 
182
 
    # TODO: Guard against the same thing being stored twice, compressed and uncompresse
 
207
    def _item_size(self, fid):
 
208
        p = self._path(fid)
 
209
        try:
 
210
            return os.stat(p)[ST_SIZE]
 
211
        except OSError:
 
212
            return os.stat(p + '.gz')[ST_SIZE]
183
213
 
184
214
    def __iter__(self):
185
215
        for f in os.listdir(self._basedir):
192
222
    def __len__(self):
193
223
        return len(os.listdir(self._basedir))
194
224
 
195
 
 
196
225
    def __getitem__(self, fileid):
197
226
        """Returns a file reading from a particular entry."""
198
227
        p = self._path(fileid)
211
240
        raise IndexError(fileid)
212
241
 
213
242
 
214
 
    def total_size(self):
215
 
        """Return (count, bytes)
216
 
 
217
 
        This is the (compressed) size stored on disk, not the size of
218
 
        the content."""
219
 
        total = 0
220
 
        count = 0
221
 
        for fid in self:
222
 
            count += 1
223
 
            p = self._path(fid)
224
 
            try:
225
 
                total += os.stat(p)[ST_SIZE]
226
 
            except OSError:
227
 
                total += os.stat(p + '.gz')[ST_SIZE]
228
 
                
229
 
        return count, total
230
 
 
231
 
 
232
 
 
233
 
 
234
243
class ImmutableScratchStore(ImmutableStore):
235
244
    """Self-destructing test subclass of ImmutableStore.
236
245
 
238
247
 Obviously you should not put anything precious in it.
239
248
    """
240
249
    def __init__(self):
241
 
        ImmutableStore.__init__(self, tempfile.mkdtemp())
 
250
        super(ImmutableScratchStore, self).__init__(tempfile.mkdtemp())
242
251
 
243
252
    def __del__(self):
244
253
        for f in os.listdir(self._basedir):
248
257
            os.remove(fpath)
249
258
        os.rmdir(self._basedir)
250
259
        mutter("%r destroyed" % self)
 
260
 
 
261
 
 
262
class ImmutableMemoryStore(Store):
 
263
    """A memory only store."""
 
264
 
 
265
    def __init__(self):
 
266
        super(ImmutableMemoryStore, self).__init__()
 
267
        self._contents = {}
 
268
 
 
269
    def add(self, stream, fileid, compressed=True):
 
270
        if self._contents.has_key(fileid):
 
271
            raise StoreError("fileid %s already in the store" % fileid)
 
272
        self._contents[fileid] = stream.read()
 
273
 
 
274
    def __getitem__(self, fileid):
 
275
        """Returns a file reading from a particular entry."""
 
276
        if not self._contents.has_key(fileid):
 
277
            raise IndexError
 
278
        return StringIO(self._contents[fileid])
 
279
 
 
280
    def _item_size(self, fileid):
 
281
        return len(self._contents[fileid])
 
282
 
 
283
    def __iter__(self):
 
284
        return iter(self._contents.keys())
 
285
 
 
286
 
 
287
class RemoteStore(object):
 
288
 
 
289
    def __init__(self, baseurl):
 
290
        self._baseurl = baseurl
 
291
 
 
292
    def _path(self, name):
 
293
        if '/' in name:
 
294
            raise ValueError('invalid store id', name)
 
295
        return self._baseurl + '/' + name
 
296
        
 
297
    def __getitem__(self, fileid):
 
298
        p = self._path(fileid)
 
299
        try:
 
300
            return get_url(p, compressed=True)
 
301
        except:
 
302
            raise KeyError(fileid)