Skip to content

Commit 97a17dc

Browse files
committed
Made the db module a package to have enough room for expansion
1 parent 0ef8655 commit 97a17dc

7 files changed

Lines changed: 250 additions & 214 deletions

File tree

db/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
from base import *
3+
from loose import *
4+
from pack import *
5+
from git import *
6+
from ref import *
7+
Lines changed: 5 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,15 @@
11
"""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 (
173
pool,
18-
ENOENT,
19-
to_hex_sha,
20-
exists,
21-
hex_to_bin,
22-
isdir,
23-
mkdir,
24-
rename,
25-
dirname,
264
join
275
)
286

29-
from fun import (
30-
chunk_size,
31-
loose_object_header_info,
32-
write_object,
33-
stream_copy
34-
)
35-
36-
377
from async import (
388
ChannelThreadTask
399
)
4010

41-
import tempfile
42-
import mmap
43-
import os
4411

45-
46-
__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'LooseObjectDB', 'PackedDB',
47-
'CompoundDB', 'ReferenceDB', 'GitObjectDB' )
12+
__all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB')
4813

4914

5015
class ObjectDBR(object):
@@ -104,6 +69,7 @@ def stream_async(self, reader):
10469

10570
#} END query interface
10671

72+
10773
class ObjectDBW(object):
10874
"""Defines an interface to create objects in the database"""
10975

@@ -183,181 +149,7 @@ def db_path(self, rela_path):
183149
return join(self._root_path, rela_path)
184150
#} END interface
185151

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+
333153
class CompoundDB(ObjectDBR):
334154
"""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

db/git.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
from gitdb.stream import (
3+
OInfo,
4+
OStream
5+
)
6+
7+
from loose import LooseObjectDB
8+
9+
__all__ = ('GitObjectDB', )
10+
11+
#class GitObjectDB(CompoundDB, ObjectDBW):
12+
class GitObjectDB(LooseObjectDB):
13+
"""A database representing the default git object store, which includes loose
14+
objects, pack files and an alternates file
15+
16+
It will create objects only in the loose object database.
17+
:note: for now, we use the git command to do all the lookup, just until he
18+
have packs and the other implementations
19+
"""
20+
def __init__(self, root_path, git):
21+
"""Initialize this instance with the root and a git command"""
22+
super(GitObjectDB, self).__init__(root_path)
23+
self._git = git
24+
25+
def info(self, sha):
26+
t = self._git.get_object_header(sha)
27+
return OInfo(*t)
28+
29+
def stream(self, sha):
30+
"""For now, all lookup is done by git itself"""
31+
t = self._git.stream_object_data(sha)
32+
return OStream(*t)
33+

0 commit comments

Comments
 (0)