88
99from stream import (
1010 DecompressMemMapReader ,
11- FDCompressedSha1Writer
11+ FDCompressedSha1Writer ,
12+ Sha1Writer ,
13+ OStream ,
14+ OInfo
1215 )
1316
1417from utils import (
3437import os
3538
3639
40+ __all__ = ('ObjectDBR' , 'ObjectDBW' , 'FileDBBase' , 'LooseObjectDB' , 'PackedDB' ,
41+ 'CompoundDB' , 'ReferenceDB' , 'GitObjectDB' )
42+
3743class ObjectDBR (object ):
3844 """Defines an interface for object database lookup.
3945 Objects are identified either by hex-sha (40 bytes) or
4046 by sha (20 bytes)"""
41- __slots__ = tuple ()
4247
4348 def __contains__ (self , sha ):
4449 return self .has_obj
@@ -52,35 +57,34 @@ def has_object(self, sha):
5257 raise NotImplementedError ("To be implemented in subclass" )
5358
5459 def info (self , sha ):
55- """ :return: ODB_Info instance
60+ """ :return: OInfo instance
5661 :param sha: 40 bytes hexsha or 20 bytes binary sha
5762 :raise BadObject:"""
5863 raise NotImplementedError ("To be implemented in subclass" )
5964
6065 def info_async (self , input_channel ):
6166 """Retrieve information of a multitude of objects asynchronously
6267 :param input_channel: Channel yielding the sha's of the objects of interest
63- :return: Channel yielding ODB_Info|InvalidODB_Info , in any order"""
68+ :return: Channel yielding OInfo|InvalidOInfo , in any order"""
6469 raise NotImplementedError ("To be implemented in subclass" )
6570
6671 def stream (self , sha ):
67- """:return: ODB_OStream instance
72+ """:return: OStream instance
6873 :param sha: 40 bytes hexsha or 20 bytes binary sha
6974 :raise BadObject:"""
7075 raise NotImplementedError ("To be implemented in subclass" )
7176
7277 def stream_async (self , input_channel ):
73- """Retrieve the ODB_OStream of multiple objects
78+ """Retrieve the OStream of multiple objects
7479 :param input_channel: see ``info``
7580 :param max_threads: see ``ObjectDBW.store``
76- :return: Channel yielding ODB_OStream|InvalidODB_OStream instances in any order"""
81+ :return: Channel yielding OStream|InvalidOStream instances in any order"""
7782 raise NotImplementedError ("To be implemented in subclass" )
7883
7984 #} END query interface
8085
8186class ObjectDBW (object ):
8287 """Defines an interface to create objects in the database"""
83- __slots__ = "_ostream"
8488
8589 def __init__ (self , * args , ** kwargs ):
8690 self ._ostream = None
@@ -99,12 +103,12 @@ def set_ostream(self, stream):
99103 def ostream (self ):
100104 """:return: overridden output stream this instance will write to, or None
101105 if it will write to the default stream"""
102- return self ._ostream
106+ return self ._ostream
103107
104108 def store (self , istream ):
105109 """Create a new object in the database
106110 :return: the input istream object with its sha set to its corresponding value
107- :param istream: ODB_IStream compatible instance. If its sha is already set
111+ :param istream: IStream compatible instance. If its sha is already set
108112 to a value, the object will just be stored in the our database format,
109113 in which case the input stream is expected to be in object format ( header + contents ).
110114 :raise IOError: if data could not be written"""
@@ -115,30 +119,23 @@ def store_async(self, input_channel):
115119 return right away, returning an output channel which receives the results as
116120 they are computed.
117121
118- :return: Channel yielding your ODB_IStream which served as input, in any order.
122+ :return: Channel yielding your IStream which served as input, in any order.
119123 The IStreams sha will be set to the sha it received during the process,
120124 or its error attribute will be set to the exception informing about the error.
121- :param input_channel: Channel yielding ODB_IStream instance.
125+ :param input_channel: Channel yielding IStream instance.
122126 As the same instances will be used in the output channel, you can create a map
123127 between the id(istream) -> istream
124128 :note:As some ODB implementations implement this operation as atomic, they might
125129 abort the whole operation if one item could not be processed. Hence check how
126130 many items have actually been produced."""
127- # a trivial implementation, ignoring the threads for now
128- # TODO: add configuration to the class to determine whether we may
129- # actually use multiple threads, default False of course. If the add
130- shas = list ()
131- for args in iter_info :
132- shas .append (self .store (dry_run = dry_run , sha_as_hex = sha_as_hex , * args ))
133- return shas
131+ raise NotImplementedError ("To be implemented in subclass" )
134132
135133 #} END edit interface
136134
137135
138136class FileDBBase (object ):
139137 """Provides basic facilities to retrieve files of interest, including
140138 caching facilities to help mapping hexsha's to objects"""
141- __slots__ = ('_root_path' , )
142139
143140 def __init__ (self , root_path ):
144141 """Initialize this instance to look for its files at the given root path
@@ -164,15 +161,11 @@ def db_path(self, rela_path):
164161 return join (self ._root_path , rela_path )
165162 #} END interface
166163
167- #{ Utiltities
168-
169-
170- #} END utilities
171164
172165
173166class LooseObjectDB (FileDBBase , ObjectDBR , ObjectDBW ):
174167 """A database which operates on loose object files"""
175- __slots__ = ( '_hexsha_to_file' , '_fd_open_flags' )
168+
176169 # CONFIGURATION
177170 # chunks in which data will be copied between streams
178171 stream_chunk_size = chunk_size
@@ -238,21 +231,26 @@ def _map_loose_object(self, sha):
238231 finally :
239232 os .close (fd )
240233 # END assure file is closed
234+
235+ def set_ostream (self , stream ):
236+ """:raise TypeError: if the stream does not support the Sha1Writer interface"""
237+ if stream is not None and not isinstance (stream , Sha1Writer ):
238+ raise TypeError ("Output stream musst support the %s interface" % Sha1Writer .__name__ )
239+ return super (LooseObjectDB , self ).set_ostream (stream )
241240
242241 def info (self , sha ):
243242 m = self ._map_loose_object (sha )
244243 try :
245- return loose_object_header_info (m )
244+ type , size = loose_object_header_info (m )
245+ return OInfo (sha , type , size )
246246 finally :
247247 m .close ()
248248 # END assure release of system resources
249249
250- def object (self , sha ):
250+ def stream (self , sha ):
251251 m = self ._map_loose_object (sha )
252- reader = DecompressMemMapReader (m , close_on_deletion = True )
253- type , size = reader .initialize ()
254-
255- return type , size , reader
252+ type , size , stream = DecompressMemMapReader .new (m , close_on_deletion = True )
253+ return OStream (sha , type , size , stream )
256254
257255 def has_object (self , sha ):
258256 try :
@@ -263,27 +261,33 @@ def has_object(self, sha):
263261 # END check existance
264262
265263 def store (self , istream ):
266- # open a tmp file to write the data to
267- # todo: implement ostream properly
268- fd , tmp_path = tempfile .mkstemp (prefix = 'obj' , dir = self ._root_path )
269- writer = FDCompressedSha1Writer (fd )
264+ """note: The sha we produce will be hex by nature"""
265+ assert istream .sha is None , "Direct istream writing not yet implemented"
266+ tmp_path = None
267+ writer = self .ostream ()
268+ if writer is None :
269+ # open a tmp file to write the data to
270+ fd , tmp_path = tempfile .mkstemp (prefix = 'obj' , dir = self ._root_path )
271+ writer = FDCompressedSha1Writer (fd )
272+ # END handle custom writer
270273
271274 try :
272- write_object (type , size , stream , writer ,
273- close_target_stream = True , chunk_size = self .stream_chunk_size )
274- except :
275- os .remove (tmp_path )
276- raise
277- # END assure tmpfile removal on error
278-
275+ try :
276+ write_object (istream .type , istream .size , istream .read , writer .write ,
277+ chunk_size = self .stream_chunk_size )
278+ except :
279+ if tmp_path :
280+ os .remove (tmp_path )
281+ raise
282+ # END assure tmpfile removal on error
283+ finally :
284+ if tmp_path :
285+ writer .close ()
286+ # END assure target stream is closed
279287
280- # in dry-run mode, we delete the file afterwards
281288 sha = writer .sha (as_hex = True )
282289
283- if dry_run :
284- os .remove (tmp_path )
285- else :
286- # rename the file into place
290+ if tmp_path :
287291 obj_path = self .db_path (self .object_path (sha ))
288292 obj_dir = dirname (obj_path )
289293 if not isdir (obj_dir ):
@@ -292,11 +296,8 @@ def store(self, istream):
292296 rename (tmp_path , obj_path )
293297 # END handle dry_run
294298
295- if not sha_as_hex :
296- sha = hex_to_bin (sha )
297- # END handle sha format
298-
299- return sha
299+ istream .sha = sha
300+ return istream
300301
301302
302303class PackedDB (FileDBBase , ObjectDBR ):
@@ -320,18 +321,17 @@ class GitObjectDB(LooseObjectDB):
320321 :note: for now, we use the git command to do all the lookup, just until he
321322 have packs and the other implementations
322323 """
323- __slots__ = ('_git' , )
324324 def __init__ (self , root_path , git ):
325325 """Initialize this instance with the root and a git command"""
326326 super (GitObjectDB , self ).__init__ (root_path )
327327 self ._git = git
328328
329329 def info (self , sha ):
330- discard , type , size = self ._git .get_object_header (sha )
331- return type , size
330+ t = self ._git .get_object_header (sha )
331+ return OInfo ( t [ 0 ], t [ 1 ], t [ 2 ])
332332
333- def object (self , sha ):
333+ def stream (self , sha ):
334334 """For now, all lookup is done by git itself"""
335- discard , type , size , stream = self ._git .stream_object_data (sha )
336- return type , size , stream
335+ t = self ._git .stream_object_data (sha )
336+ return OStream ( t [ 0 ], t [ 1 ], t [ 2 ], t [ 3 ])
337337
0 commit comments