Skip to content

Commit ad3eb79

Browse files
committed
feat(streams): use named-tuples
+ ~10% faster BREAKING API 1: `OStream` not an instance of `OInfo`, not inheritable (ie no `DeriveTest` class).
1 parent 79a754a commit ad3eb79

File tree

4 files changed

+41
-125
lines changed

4 files changed

+41
-125
lines changed

Diff for: gitdb/base.py

+37-106
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
55
"""Module with basic data structures - they are designed to be lightweight and fast"""
66
from gitdb.util import bin_to_hex, suppress
7+
from collections import namedtuple
8+
79

810
from gitdb.fun import (
911
type_id_to_type_map,
@@ -17,7 +19,7 @@
1719
#{ ODB Bases
1820

1921

20-
class OInfo(tuple):
22+
class OInfo(namedtuple('OInfo', 'binsha, type, size')):
2123

2224
"""Carries information about an object in an ODB, providing information
2325
about the binary sha of the object, the type_string as well as the uncompressed size
@@ -30,109 +32,57 @@ class OInfo(tuple):
3032
assert dbi[2] == dbi.size
3133
3234
The type is designed to be as lightweight as possible."""
33-
__slots__ = tuple()
34-
35-
def __new__(cls, sha, type, size):
36-
return tuple.__new__(cls, (sha, type, size))
37-
38-
def __init__(self, *args):
39-
tuple.__init__(self)
40-
41-
#{ Interface
42-
@property
43-
def binsha(self):
44-
""":return: our sha as binary, 20 bytes"""
45-
return self[0]
35+
__slots__ = ()
4636

4737
@property
4838
def hexsha(self):
4939
""":return: our sha, hex encoded, 40 bytes"""
50-
return bin_to_hex(self[0])
51-
52-
@property
53-
def type(self):
54-
return self[1]
40+
return bin_to_hex(self.binsha)
5541

5642
@property
5743
def type_id(self):
58-
return type_to_type_id_map[self[1]]
44+
return type_to_type_id_map[self.type]
5945

60-
@property
61-
def size(self):
62-
return self[2]
63-
#} END interface
6446

65-
66-
class OPackInfo(tuple):
47+
class OPackInfo(namedtuple('OPackInfo', 'pack_offset, type_id, size')):
6748

6849
"""As OInfo, but provides a type_id property to retrieve the numerical type id, and
6950
does not include a sha.
7051
7152
Additionally, the pack_offset is the absolute offset into the packfile at which
7253
all object information is located. The data_offset property points to the absolute
7354
location in the pack at which that actual data stream can be found."""
74-
__slots__ = tuple()
75-
76-
def __new__(cls, packoffset, type, size):
77-
return tuple.__new__(cls, (packoffset, type, size))
78-
79-
def __init__(self, *args):
80-
tuple.__init__(self)
81-
82-
#{ Interface
83-
84-
@property
85-
def pack_offset(self):
86-
return self[0]
55+
__slots__ = ()
8756

8857
@property
8958
def type(self):
90-
return type_id_to_type_map[self[1]]
91-
92-
@property
93-
def type_id(self):
94-
return self[1]
95-
96-
@property
97-
def size(self):
98-
return self[2]
99-
100-
#} END interface
59+
return type_id_to_type_map[self.type_id]
10160

10261

103-
class ODeltaPackInfo(OPackInfo):
62+
class ODeltaPackInfo(namedtuple('ODeltaPackInfo', 'pack_offset, type_id, size, delta_info')):
10463

10564
"""Adds delta specific information,
10665
Either the 20 byte sha which points to some object in the database,
10766
or the negative offset from the pack_offset, so that pack_offset - delta_info yields
10867
the pack offset of the base object"""
109-
__slots__ = tuple()
110-
111-
def __new__(cls, packoffset, type, size, delta_info):
112-
return tuple.__new__(cls, (packoffset, type, size, delta_info))
68+
__slots__ = ()
11369

114-
#{ Interface
11570
@property
116-
def delta_info(self):
117-
return self[3]
118-
#} END interface
119-
71+
def type(self):
72+
return type_id_to_type_map[self.type_id]
12073

121-
class OStream(OInfo):
12274

75+
class OStream(namedtuple('OStream', 'binsha type size stream')):
12376
"""Base for object streams retrieved from the database, providing additional
12477
information about the stream.
125-
Generally, ODB streams are read-only as objects are immutable"""
126-
__slots__ = tuple()
78+
Generally, ODB streams are read-only as objects are immutable
12779
128-
def __new__(cls, sha, type, size, stream, *args, **kwargs):
129-
"""Helps with the initialization of subclasses"""
130-
return tuple.__new__(cls, (sha, type, size, stream))
80+
.. Note:
81+
Is NOTE a :class:`OInfo` instance; for the effort required, see:
82+
see http://stackoverflow.com/questions/20794182/how-to-make-a-file-like-class-work-with-isinstancecls-io-iobase
13183
132-
def __init__(self, *args, **kwargs):
133-
tuple.__init__(self)
134-
135-
#{ Stream Reader Interface
84+
"""
85+
__slots__ = ()
13686

13787
def __enter__(self):
13888
return self
@@ -148,38 +98,26 @@ def read(self, size=-1):
14898
return self.stream.read(size)
14999

150100
@property
151-
def stream(self):
152-
return self[3]
101+
def hexsha(self):
102+
""":return: our sha, hex encoded, 40 bytes"""
103+
return bin_to_hex(self.binsha)
153104

154-
#} END stream reader interface
105+
@property
106+
def type_id(self):
107+
return type_to_type_id_map[self.type]
155108

156109

157110
class ODeltaStream(OStream):
158-
159-
"""Uses size info of its stream, delaying reads"""
160-
161-
def __new__(cls, sha, type, size, stream, *args, **kwargs):
162-
"""Helps with the initialization of subclasses"""
163-
return tuple.__new__(cls, (sha, type, size, stream))
164-
165-
#{ Stream Reader Interface
166-
167111
@property
168112
def size(self):
169113
return self[3].size
170114

171-
#} END stream reader interface
172-
173115

174-
class OPackStream(OPackInfo):
116+
class OPackStream(namedtuple('OPackStream', 'pack_offset, type_id, size, stream')):
175117

176118
"""Next to pack object information, a stream outputting an undeltified base object
177119
is provided"""
178-
__slots__ = tuple()
179-
180-
def __new__(cls, packoffset, type, size, stream, *args):
181-
"""Helps with the initialization of subclasses"""
182-
return tuple.__new__(cls, (packoffset, type, size, stream))
120+
__slots__ = ()
183121

184122
def __enter__(self):
185123
return self
@@ -191,23 +129,18 @@ def __exit__(self, exc_type, exc_value, traceback):
191129
def close(self):
192130
self.stream.close()
193131

194-
#{ Stream Reader Interface
195132
def read(self, size=-1):
196133
return self.stream.read(size)
197134

198135
@property
199-
def stream(self):
200-
return self[3]
201-
#} END stream reader interface
136+
def type(self):
137+
return type_id_to_type_map[self.type_id]
202138

203139

204-
class ODeltaPackStream(ODeltaPackInfo):
140+
class ODeltaPackStream(namedtuple('ODeltaPackStream', 'pack_offset, type_id, size, delta_info stream')):
205141

206142
"""Provides a stream outputting the uncompressed offset delta information"""
207-
__slots__ = tuple()
208-
209-
def __new__(cls, packoffset, type, size, delta_info, stream):
210-
return tuple.__new__(cls, (packoffset, type, size, delta_info, stream))
143+
__slots__ = ()
211144

212145
def __enter__(self):
213146
return self
@@ -219,14 +152,12 @@ def __exit__(self, exc_type, exc_value, traceback):
219152
def close(self):
220153
self.stream.close()
221154

222-
#{ Stream Reader Interface
223155
def read(self, size=-1):
224156
return self.stream.read(size)
225157

226158
@property
227-
def stream(self):
228-
return self[4]
229-
#} END stream reader interface
159+
def type(self):
160+
return type_id_to_type_map[self.type_id]
230161

231162

232163
class IStream(list):
@@ -238,7 +169,7 @@ class IStream(list):
238169
to blend in without prior conversion.
239170
240171
The only method your content stream must support is 'read'"""
241-
__slots__ = tuple()
172+
__slots__ = ()
242173

243174
def __new__(cls, type, size, stream, sha=None):
244175
return list.__new__(cls, (sha, type, size, stream, None))
@@ -325,7 +256,7 @@ class InvalidOInfo(tuple):
325256
"""Carries information about a sha identifying an object which is invalid in
326257
the queried database. The exception attribute provides more information about
327258
the cause of the issue"""
328-
__slots__ = tuple()
259+
__slots__ = ()
329260

330261
def __new__(cls, sha, exc):
331262
return tuple.__new__(cls, (sha, exc))
@@ -350,7 +281,7 @@ def error(self):
350281
class InvalidOStream(InvalidOInfo):
351282

352283
"""Carries information about an invalid ODB stream"""
353-
__slots__ = tuple()
284+
__slots__ = ()
354285

355286
def __enter__(self):
356287
return self

Diff for: gitdb/test/db/lib.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _assert_object_writing_simple(self, db):
5353
assert db.has_object(istream.binsha)
5454

5555
info = db.info(istream.binsha)
56-
assert isinstance(info, OInfo)
56+
assert isinstance(info, (OInfo, OStream))
5757
assert info.type == istream.type and info.size == istream.size
5858

5959
with db.stream(istream.binsha) as stream:

Diff for: gitdb/test/lib.py

-11
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,4 @@ def close(self):
202202
def _assert(self):
203203
assert self.was_read
204204

205-
206-
class DeriveTest(OStream):
207-
208-
def __init__(self, sha, type, size, stream, *args, **kwargs):
209-
self.myarg = kwargs.pop('myarg')
210-
self.args = args
211-
212-
def _assert(self):
213-
assert self.args
214-
assert self.myarg
215-
216205
#} END stream utilitiess

Diff for: gitdb/test/test_base.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from gitdb.test.lib import (
77
TestBase,
88
DummyStream,
9-
DeriveTest,
109
)
1110

1211
from gitdb import (
@@ -73,17 +72,14 @@ def test_streams(self):
7372

7473
# test deltapackstream
7574
dpostream = ODeltaPackStream(*(dpinfo + (stream, )))
76-
dpostream.stream is stream
75+
assert dpostream.stream is stream
7776
dpostream.read(5)
7877
stream._assert()
7978
assert stream.bytes == 5
8079

81-
# derive with own args
82-
DeriveTest(sha, str_blob_type, s, stream, 'mine', myarg=3)._assert()
83-
8480
# test istream
8581
istream = IStream(str_blob_type, s, stream)
86-
assert istream.binsha == None
82+
assert istream.binsha is None
8783
istream.binsha = sha
8884
assert istream.binsha == sha
8985

@@ -92,7 +88,7 @@ def test_streams(self):
9288

9389
assert istream.size == s
9490
istream.size = s * 2
95-
istream.size == s * 2
91+
assert istream.size == s * 2
9692
assert istream.type == str_blob_type
9793
istream.type = "something"
9894
assert istream.type == "something"

0 commit comments

Comments
 (0)