Skip to content

Commit 7c1169f

Browse files
committed
Removed compression flag from IStream and OStream types, as a valid object will always be compressed if generated by the system ( even future memory db's will compress it )
loose db: implemented direct stream copy, indicated by a sha set in the IStream, including test. This will be the case once Packs are exploded for instance
1 parent a243827 commit 7c1169f

File tree

3 files changed

+66
-33
lines changed

3 files changed

+66
-33
lines changed

lib/git/odb/db.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
from fun import (
3030
chunk_size,
3131
loose_object_header_info,
32-
write_object
32+
write_object,
33+
stream_copy
3334
)
3435

3536
import tempfile
@@ -262,7 +263,6 @@ def has_object(self, sha):
262263

263264
def store(self, istream):
264265
"""note: The sha we produce will be hex by nature"""
265-
assert istream.sha is None, "Direct istream writing not yet implemented"
266266
tmp_path = None
267267
writer = self.ostream()
268268
if writer is None:
@@ -273,8 +273,13 @@ def store(self, istream):
273273

274274
try:
275275
try:
276-
write_object(istream.type, istream.size, istream.read, writer.write,
277-
chunk_size=self.stream_chunk_size)
276+
if istream.sha is not None:
277+
stream_copy(istream.read, writer.write, istream.size, self.stream_chunk_size)
278+
else:
279+
# write object with header, we have to make a new one
280+
write_object(istream.type, istream.size, istream.read, writer.write,
281+
chunk_size=self.stream_chunk_size)
282+
# END handle direct stream copies
278283
except:
279284
if tmp_path:
280285
os.remove(tmp_path)
@@ -285,7 +290,7 @@ def store(self, istream):
285290
writer.close()
286291
# END assure target stream is closed
287292

288-
sha = writer.sha(as_hex=True)
293+
sha = istream.sha or writer.sha(as_hex=True)
289294

290295
if tmp_path:
291296
obj_path = self.db_path(self.object_path(sha))

lib/git/odb/stream.py

+7-22
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,6 @@ def __new__(cls, sha, type, size, stream, *args, **kwargs):
6969

7070
def __init__(self, *args, **kwargs):
7171
tuple.__init__(self)
72-
#{ Interface
73-
74-
def is_compressed(self):
75-
""":return: True if reads of this stream yield zlib compressed data. Default False
76-
:note: this does not imply anything about the actual internal storage.
77-
Hence the data could be uncompressed, but read compressed, or vice versa"""
78-
return False
79-
80-
#} END interface
8172

8273
#{ Stream Reader Interface
8374

@@ -97,11 +88,11 @@ class IStream(list):
9788
The only method your content stream must support is 'read'"""
9889
__slots__ = tuple()
9990

100-
def __new__(cls, type, size, stream, sha=None, compressed=False):
101-
return list.__new__(cls, (sha, type, size, stream, compressed, None))
91+
def __new__(cls, type, size, stream, sha=None):
92+
return list.__new__(cls, (sha, type, size, stream, None))
10293

103-
def __init__(self, type, size, stream, sha=None, compressed=None):
104-
list.__init__(self, (sha, type, size, stream, compressed, None))
94+
def __init__(self, type, size, stream, sha=None):
95+
list.__init__(self, (sha, type, size, stream, None))
10596

10697
#{ Interface
10798

@@ -117,11 +108,11 @@ def binsha(self):
117108

118109
def _error(self):
119110
""":return: the error that occurred when processing the stream, or None"""
120-
return self[5]
111+
return self[4]
121112

122113
def _set_error(self, exc):
123114
"""Set this input stream to the given exc, may be None to reset the error"""
124-
self[5] = exc
115+
self[4] = exc
125116

126117
error = property(_error, _set_error)
127118

@@ -172,13 +163,6 @@ def _set_stream(self, stream):
172163
stream = property(_stream, _set_stream)
173164

174165
#} END odb info interface
175-
176-
#{ OStream interface
177-
178-
def is_compressed(self):
179-
return self[4]
180-
181-
#} END OStream interface
182166

183167

184168
class InvalidOInfo(tuple):
@@ -397,6 +381,7 @@ def read(self, size=-1):
397381
class Sha1Writer(object):
398382
"""Simple stream writer which produces a sha whenever you like as it degests
399383
everything it is supposed to write"""
384+
__slots__ = "sha1"
400385

401386
def __init__(self):
402387
self.sha1 = make_sha("")

test/git/test_odb.py

+49-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def close(self):
3131

3232
def _assert(self):
3333
assert self.was_read
34-
34+
35+
3536
class DeriveTest(OStream):
3637
def __init__(self, sha, type, size, stream, *args, **kwargs):
3738
self.myarg = kwargs.pop('myarg')
@@ -41,6 +42,27 @@ def _assert(self):
4142
assert self.args
4243
assert self.myarg
4344

45+
46+
class ZippedStoreShaWriter(Sha1Writer):
47+
"""Remembers everything someone writes to it"""
48+
__slots__ = ('buf', 'zip')
49+
def __init__(self):
50+
Sha1Writer.__init__(self)
51+
self.buf = StringIO()
52+
self.zip = zlib.compressobj(1) # fastest
53+
54+
def __getattr__(self, attr):
55+
return getattr(self.buf, attr)
56+
57+
def write(self, data):
58+
alen = Sha1Writer.write(self, data)
59+
self.buf.write(self.zip.compress(data))
60+
return alen
61+
62+
def close(self):
63+
self.buf.write(self.zip.flush())
64+
65+
4466
#} END stream utilitiess
4567

4668

@@ -68,15 +90,11 @@ def test_streams(self):
6890
ostream.read(20)
6991
assert stream.bytes == 20
7092

71-
# defaults false
72-
assert not ostream.is_compressed()
73-
7493
# derive with own args
7594
DeriveTest(sha, Blob.type, s, stream, 'mine',myarg = 3)._assert()
7695

7796
# test istream
7897
istream = IStream(Blob.type, s, stream)
79-
assert not istream.is_compressed()
8098
assert istream.sha == None
8199
istream.sha = sha
82100
assert istream.sha == sha
@@ -94,6 +112,10 @@ def test_streams(self):
94112
istream.stream = None
95113
assert istream.stream is None
96114

115+
assert istream.error is None
116+
istream.error = Exception()
117+
assert isinstance(istream.error, Exception)
118+
97119
def _assert_stream_reader(self, stream, cdata, rewind_stream=lambda s: None):
98120
"""Make stream tests - the orig_stream is seekable, allowing it to be
99121
rewound and reused
@@ -218,13 +240,14 @@ def _assert_object_writing(self, db):
218240
"""General tests to verify object writing, compatible to ObjectDBW
219241
:note: requires write access to the database"""
220242
# start in 'dry-run' mode, using a simple sha1 writer
221-
ostreams = (Sha1Writer, None)
243+
ostreams = (ZippedStoreShaWriter, None)
222244
for ostreamcls in ostreams:
223245
for data in self.all_data:
224246
dry_run = ostreamcls is not None
225247
ostream = None
226248
if ostreamcls is not None:
227249
ostream = ostreamcls()
250+
assert isinstance(ostream, Sha1Writer)
228251
# END create ostream
229252

230253
prev_ostream = db.set_ostream(ostream)
@@ -252,6 +275,26 @@ def _assert_object_writing(self, db):
252275
else:
253276
self.failUnlessRaises(BadObject, db.info, sha)
254277
self.failUnlessRaises(BadObject, db.stream, sha)
278+
279+
# DIRECT STREAM COPY
280+
# our data hase been written in object format to the StringIO
281+
# we pasesd as output stream. No physical database representation
282+
# was created.
283+
# Test direct stream copy of object streams, the result must be
284+
# identical to what we fed in
285+
ostream.seek(0)
286+
istream.stream = ostream
287+
assert istream.sha is not None
288+
prev_sha = istream.sha
289+
290+
db.set_ostream(ZippedStoreShaWriter())
291+
db.store(istream)
292+
assert istream.sha == prev_sha
293+
new_ostream = db.ostream()
294+
295+
# note: only works as long our store write uses the same compression
296+
# level, which is zip
297+
assert ostream.getvalue() == new_ostream.getvalue()
255298
# END for each data set
256299
# END for each dry_run mode
257300

0 commit comments

Comments
 (0)