14
15
# along with this program; if not, write to the Free Software
15
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
Stores are the main data-storage mechanism for Bazaar-NG.
18
"""Stores are the main data-storage mechanism for Bazaar-NG.
20
20
A store is a simple write-once container indexed by a universally
21
unique ID, which is typically the SHA-1 of the content."""
23
__copyright__ = "Copyright (C) 2005 Canonical Ltd."
24
__author__ = "Martin Pool <mbp@canonical.com>"
24
26
import os, tempfile, types, osutils, gzip, errno
25
27
from stat import ST_SIZE
26
28
from StringIO import StringIO
27
from bzrlib.trace import mutter
29
from trace import mutter
30
31
######################################################################
57
58
>>> st['123123'].read()
60
TODO: Atomic add by writing to a temporary file and renaming.
62
In bzr 0.0.5 and earlier, files within the store were marked
63
readonly on disk. This is no longer done but existing stores need
61
:todo: Atomic add by writing to a temporary file and renaming.
63
:todo: Perhaps automatically transform to/from XML in a method?
64
Would just need to tell the constructor what class to
67
:todo: Even within a simple disk store like this, we could
68
gzip the files. But since many are less than one disk
69
block, that might not help a lot.
67
73
def __init__(self, basedir):
74
"""ImmutableStore constructor."""
68
75
self._basedir = basedir
70
77
def _path(self, id):
71
if '\\' in id or '/' in id:
72
raise ValueError("invalid store id %r" % id)
73
78
return os.path.join(self._basedir, id)
75
80
def __repr__(self):
78
83
def add(self, f, fileid, compressed=True):
79
84
"""Add contents of a file into the store.
81
f -- An open file, or file-like object."""
82
# FIXME: Only works on files that will fit in memory
84
from bzrlib.atomicfile import AtomicFile
86
:param f: An open file, or file-like object."""
87
# FIXME: Only works on smallish files
88
# TODO: Can be optimized by copying at the same time as
86
90
mutter("add store entry %r" % (fileid))
87
91
if isinstance(f, types.StringTypes):
92
96
p = self._path(fileid)
93
97
if os.access(p, os.F_OK) or os.access(p + '.gz', os.F_OK):
94
from bzrlib.errors import bailout
95
raise BzrError("store %r already contains id %r" % (self._basedir, fileid))
98
bailout("store %r already contains id %r" % (self._basedir, fileid))
101
af = AtomicFile(fn, 'wb')
104
gf = gzip.GzipFile(mode='wb', fileobj=af)
114
def copy_multi(self, other, ids, permit_failure=False):
115
"""Copy texts for ids from other into self.
117
If an id is present in self, it is skipped.
119
Returns (count_copied, failed), where failed is a collection of ids
120
that could not be copied.
122
pb = bzrlib.ui.ui_factory.progress_bar()
124
pb.update('preparing to copy')
125
to_copy = [id for id in ids if id not in self]
126
if isinstance(other, ImmutableStore):
127
return self.copy_multi_immutable(other, to_copy, pb)
131
pb.update('copy', count, len(to_copy))
132
if not permit_failure:
133
self.add(other[id], id)
142
assert count == len(to_copy)
147
def copy_multi_immutable(self, other, to_copy, pb, permit_failure=False):
148
from shutil import copyfile
153
other_p = other._path(id)
157
if e.errno == errno.ENOENT:
158
if not permit_failure:
159
copyfile(other_p+".gz", p+".gz")
162
copyfile(other_p+".gz", p+".gz")
164
if e.errno == errno.ENOENT:
172
pb.update('copy', count, len(to_copy))
173
assert count == len(to_copy)
101
f = gzip.GzipFile(p + '.gz', 'wb')
102
os.chmod(p + '.gz', 0444)
178
111
def __contains__(self, fileid):
194
127
def __len__(self):
195
128
return len(os.listdir(self._basedir))
198
130
def __getitem__(self, fileid):
199
131
"""Returns a file reading from a particular entry."""
200
132
p = self._path(fileid)
202
134
return gzip.GzipFile(p + '.gz', 'rb')
203
135
except IOError, e:
204
if e.errno != errno.ENOENT:
210
if e.errno != errno.ENOENT:
213
raise IndexError(fileid)
136
if e.errno == errno.ENOENT:
216
141
def total_size(self):
217
142
"""Return (count, bytes)
237
162
"""Self-destructing test subclass of ImmutableStore.
239
164
The Store only exists for the lifetime of the Python object.
240
Obviously you should not put anything precious in it.
165
Obviously you should not put anything precious in it.
242
167
def __init__(self):
243
168
ImmutableStore.__init__(self, tempfile.mkdtemp())
245
170
def __del__(self):
246
171
for f in os.listdir(self._basedir):
247
fpath = os.path.join(self._basedir, f)
248
# needed on windows, and maybe some other filesystems
249
os.chmod(fpath, 0600)
172
os.remove(os.path.join(self._basedir, f))
251
173
os.rmdir(self._basedir)
252
174
mutter("%r destroyed" % self)