|
1 | 1 | """Contains implementations of database retrieveing objects""" |
2 | | -from exc import ( |
3 | | - InvalidDBRoot, |
4 | | - BadObject, |
5 | | - BadObjectType |
6 | | - ) |
7 | | - |
8 | | -from stream import ( |
9 | | - DecompressMemMapReader, |
10 | | - FDCompressedSha1Writer, |
11 | | - Sha1Writer, |
12 | | - OStream, |
13 | | - OInfo |
14 | | - ) |
15 | | - |
16 | | -from util import ( |
| 2 | +from gitdb.util import ( |
17 | 3 | pool, |
18 | | - ENOENT, |
19 | | - to_hex_sha, |
20 | | - exists, |
21 | | - hex_to_bin, |
22 | | - isdir, |
23 | | - mkdir, |
24 | | - rename, |
25 | | - dirname, |
26 | 4 | join |
27 | 5 | ) |
28 | 6 |
|
29 | | -from fun import ( |
30 | | - chunk_size, |
31 | | - loose_object_header_info, |
32 | | - write_object, |
33 | | - stream_copy |
34 | | - ) |
35 | | - |
36 | | - |
37 | 7 | from async import ( |
38 | 8 | ChannelThreadTask |
39 | 9 | ) |
40 | 10 |
|
41 | | -import tempfile |
42 | | -import mmap |
43 | | -import os |
44 | 11 |
|
45 | | - |
46 | | -__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'LooseObjectDB', 'PackedDB', |
47 | | - 'CompoundDB', 'ReferenceDB', 'GitObjectDB' ) |
| 12 | +__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB') |
48 | 13 |
|
49 | 14 |
|
50 | 15 | class ObjectDBR(object): |
@@ -104,6 +69,7 @@ def stream_async(self, reader): |
104 | 69 |
|
105 | 70 | #} END query interface |
106 | 71 |
|
| 72 | + |
107 | 73 | class ObjectDBW(object): |
108 | 74 | """Defines an interface to create objects in the database""" |
109 | 75 |
|
@@ -183,181 +149,7 @@ def db_path(self, rela_path): |
183 | 149 | return join(self._root_path, rela_path) |
184 | 150 | #} END interface |
185 | 151 |
|
186 | | - |
187 | | - |
188 | | -class LooseObjectDB(FileDBBase, ObjectDBR, ObjectDBW): |
189 | | - """A database which operates on loose object files""" |
190 | | - |
191 | | - # CONFIGURATION |
192 | | - # chunks in which data will be copied between streams |
193 | | - stream_chunk_size = chunk_size |
194 | | - |
195 | | - |
196 | | - def __init__(self, root_path): |
197 | | - super(LooseObjectDB, self).__init__(root_path) |
198 | | - self._hexsha_to_file = dict() |
199 | | - # Additional Flags - might be set to 0 after the first failure |
200 | | - # Depending on the root, this might work for some mounts, for others not, which |
201 | | - # is why it is per instance |
202 | | - self._fd_open_flags = getattr(os, 'O_NOATIME', 0) |
203 | | - |
204 | | - #{ Interface |
205 | | - def object_path(self, hexsha): |
206 | | - """ |
207 | | - :return: path at which the object with the given hexsha would be stored, |
208 | | - relative to the database root""" |
209 | | - return join(hexsha[:2], hexsha[2:]) |
210 | | - |
211 | | - def readable_db_object_path(self, hexsha): |
212 | | - """ |
213 | | - :return: readable object path to the object identified by hexsha |
214 | | - :raise BadObject: If the object file does not exist""" |
215 | | - try: |
216 | | - return self._hexsha_to_file[hexsha] |
217 | | - except KeyError: |
218 | | - pass |
219 | | - # END ignore cache misses |
220 | | - |
221 | | - # try filesystem |
222 | | - path = self.db_path(self.object_path(hexsha)) |
223 | | - if exists(path): |
224 | | - self._hexsha_to_file[hexsha] = path |
225 | | - return path |
226 | | - # END handle cache |
227 | | - raise BadObject(hexsha) |
228 | | - |
229 | | - #} END interface |
230 | | - |
231 | | - def _map_loose_object(self, sha): |
232 | | - """ |
233 | | - :return: memory map of that file to allow random read access |
234 | | - :raise BadObject: if object could not be located""" |
235 | | - db_path = self.db_path(self.object_path(to_hex_sha(sha))) |
236 | | - try: |
237 | | - fd = os.open(db_path, os.O_RDONLY|self._fd_open_flags) |
238 | | - except OSError,e: |
239 | | - if e.errno != ENOENT: |
240 | | - # try again without noatime |
241 | | - try: |
242 | | - fd = os.open(db_path, os.O_RDONLY) |
243 | | - except OSError: |
244 | | - raise BadObject(to_hex_sha(sha)) |
245 | | - # didn't work because of our flag, don't try it again |
246 | | - self._fd_open_flags = 0 |
247 | | - else: |
248 | | - raise BadObject(to_hex_sha(sha)) |
249 | | - # END handle error |
250 | | - # END exception handling |
251 | | - try: |
252 | | - return mmap.mmap(fd, 0, access=mmap.ACCESS_READ) |
253 | | - finally: |
254 | | - os.close(fd) |
255 | | - # END assure file is closed |
256 | | - |
257 | | - def set_ostream(self, stream): |
258 | | - """:raise TypeError: if the stream does not support the Sha1Writer interface""" |
259 | | - if stream is not None and not isinstance(stream, Sha1Writer): |
260 | | - raise TypeError("Output stream musst support the %s interface" % Sha1Writer.__name__) |
261 | | - return super(LooseObjectDB, self).set_ostream(stream) |
262 | | - |
263 | | - def info(self, sha): |
264 | | - m = self._map_loose_object(sha) |
265 | | - try: |
266 | | - type, size = loose_object_header_info(m) |
267 | | - return OInfo(sha, type, size) |
268 | | - finally: |
269 | | - m.close() |
270 | | - # END assure release of system resources |
271 | | - |
272 | | - def stream(self, sha): |
273 | | - m = self._map_loose_object(sha) |
274 | | - type, size, stream = DecompressMemMapReader.new(m, close_on_deletion = True) |
275 | | - return OStream(sha, type, size, stream) |
276 | | - |
277 | | - def has_object(self, sha): |
278 | | - try: |
279 | | - self.readable_db_object_path(to_hex_sha(sha)) |
280 | | - return True |
281 | | - except BadObject: |
282 | | - return False |
283 | | - # END check existance |
284 | | - |
285 | | - def store(self, istream): |
286 | | - """note: The sha we produce will be hex by nature""" |
287 | | - tmp_path = None |
288 | | - writer = self.ostream() |
289 | | - if writer is None: |
290 | | - # open a tmp file to write the data to |
291 | | - fd, tmp_path = tempfile.mkstemp(prefix='obj', dir=self._root_path) |
292 | | - writer = FDCompressedSha1Writer(fd) |
293 | | - # END handle custom writer |
294 | | - |
295 | | - try: |
296 | | - try: |
297 | | - if istream.sha is not None: |
298 | | - stream_copy(istream.read, writer.write, istream.size, self.stream_chunk_size) |
299 | | - else: |
300 | | - # write object with header, we have to make a new one |
301 | | - write_object(istream.type, istream.size, istream.read, writer.write, |
302 | | - chunk_size=self.stream_chunk_size) |
303 | | - # END handle direct stream copies |
304 | | - except: |
305 | | - if tmp_path: |
306 | | - os.remove(tmp_path) |
307 | | - raise |
308 | | - # END assure tmpfile removal on error |
309 | | - finally: |
310 | | - if tmp_path: |
311 | | - writer.close() |
312 | | - # END assure target stream is closed |
313 | | - |
314 | | - sha = istream.sha or writer.sha(as_hex=True) |
315 | | - |
316 | | - if tmp_path: |
317 | | - obj_path = self.db_path(self.object_path(sha)) |
318 | | - obj_dir = dirname(obj_path) |
319 | | - if not isdir(obj_dir): |
320 | | - mkdir(obj_dir) |
321 | | - # END handle destination directory |
322 | | - rename(tmp_path, obj_path) |
323 | | - # END handle dry_run |
324 | | - |
325 | | - istream.sha = sha |
326 | | - return istream |
327 | | - |
328 | | - |
329 | | -class PackedDB(FileDBBase, ObjectDBR): |
330 | | - """A database operating on a set of object packs""" |
331 | | - |
332 | | - |
| 152 | + |
333 | 153 | class CompoundDB(ObjectDBR): |
334 | 154 | """A database which delegates calls to sub-databases""" |
335 | | - |
336 | | - |
337 | | -class ReferenceDB(CompoundDB): |
338 | | - """A database consisting of database referred to in a file""" |
339 | | - |
340 | | - |
341 | | -#class GitObjectDB(CompoundDB, ObjectDBW): |
342 | | -class GitObjectDB(LooseObjectDB): |
343 | | - """A database representing the default git object store, which includes loose |
344 | | - objects, pack files and an alternates file |
345 | | - |
346 | | - It will create objects only in the loose object database. |
347 | | - :note: for now, we use the git command to do all the lookup, just until he |
348 | | - have packs and the other implementations |
349 | | - """ |
350 | | - def __init__(self, root_path, git): |
351 | | - """Initialize this instance with the root and a git command""" |
352 | | - super(GitObjectDB, self).__init__(root_path) |
353 | | - self._git = git |
354 | | - |
355 | | - def info(self, sha): |
356 | | - t = self._git.get_object_header(sha) |
357 | | - return OInfo(*t) |
358 | | - |
359 | | - def stream(self, sha): |
360 | | - """For now, all lookup is done by git itself""" |
361 | | - t = self._git.stream_object_data(sha) |
362 | | - return OStream(*t) |
363 | | - |
| 155 | + # TODO |
0 commit comments