Mercurial > hgstorage
changeset 0:1af6b1f1a435
initial commit
| author | Radomir Dopieralski <sheep@stxnext.pl> |
|---|---|
| date | Thu, 17 Mar 2011 23:25:56 +0100 |
| parents | |
| children | 6d152b15f0d2 |
| files | hgstorage.py test_repo.py |
| diffstat | 2 files changed, 203 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgstorage.py Thu Mar 17 23:25:56 2011 +0100 @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import thread +import urllib +import datetime + +os.environ['HGENCODING'] = 'utf8' +os.environ['HGMERGE'] = "internal:merge" + +import mercurial.hg +import mercurial.ui +import mercurial.revlog +import mercurial.context + + +class Storage(object): + def __init__(self, path, prefix=None): + self.path = path + self.prefix = prefix or '' + self.ui = self.get_ui() + self._repos = {} + + def create(self): + repo = mercurial.hg.repository(self.ui, self.path, create=True) + + @classmethod + def get_ui(cls): + """Create and initialize an ui object.""" + + ui = mercurial.ui.ui() + ui.quiet = True + ui._report_untrusted = False + ui.setconfig('ui', 'interactive', False) + return ui + + @property + def repo(self): + """Keep one open repository per thread.""" + + thread_id = thread.get_ident() + try: + return self._repos[thread_id] + except KeyError: + repo = mercurial.hg.repository(self.ui, self.path) + self._repos[thread_id] = repo + return repo + + def _key_to_file(self, key): + """Translate key names to file names.""" + + filename = urllib.quote(key.encode('utf8', 'ignore'), safe='') + if filename.startswith(('.', '_')) or filename == '': + filename = '_%s' % filename + return os.path.join(self.prefix, filename) + + def _file_to_key(self, filename): + """Translate file names to key names.""" + + assert filename.startswith(self.prefix) + filename = filename[len(self.prefix):].strip('/') + if filename.startswith('_'): + filename = filename[1:] + key = unicode(urllib.unquote(filename), 'utf8', 'ignore') + return key + + def get_session(self): + return Session(self) + + +class Session(object): + def __init__(self, storage): + self.storage = storage + self.tip = storage.repo.changectx('tip') + + def __contains__(self, key): + filename = self.storage._key_to_file(key) + return filename in self.tip + + def __getitem__(self, key): + return Item(self, key) + + def save(self, data, key, user=None, message=None, parent_rev=None, + encoding=None, force=False): + """Save a new revision of the item.""" + + if encoding: + data = data.encode(encoding) + is_text = True + else: + is_text = False + + filename = self.storage._key_to_file(key) + if parent_rev is None: + parents = ('tip', None) + else: + try: + filetip = self.tip[filename] + last_rev = filetip.filerev() + except mercurial.revlog.LookupError: + last_rev = -1 + if force: + parents = (last_rev, parent_rev) + elif parent_rev == last_rev: + parents = ('tip', None) + elif is_text: + # XXX merge the data + parents = (last_rev, parent_rev) + else: + raise ValueError("conflict when saving binary data") + + def filectxfn(repo, memctx, path): + return mercurial.context.memfilectx(path, data, False, False, None) + + ctx = mercurial.context.memctx( + repo=self.storage.repo, parents=parents, + text=message or "Commit Message", files=[filename], + filectxfn=filectxfn, user=user or "user") + self.storage.repo.commitctx(ctx) + + def get_filectx(self, key, rev=None): + filename = self.storage._key_to_file(key) + try: + filetip = self.tip[filename] + except mercurial.revlog.LookupError: + raise KeyError("item %r doesn't exist") + if rev is None: + filectx = filetip + else: + try: + filectx = filetip.filectx(rev) + except mercurial.revlog.LookupError: + raise KeyError() + return filectx + + def load(self, key, rev=None, encoding=None): + filectx = self.get_filectx(key, rev) + data = filectx.data() + if encoding: + data = unicode(data, encoding) + return data + + def meta(self, key, rev=None): + filectx = self.get_filectx(key, rev) + if rev is None: + rev = filectx.filerev() + date = datetime.datetime.fromtimestamp(filectx.date()[0]) + user = unicode(filectx.user(), "utf8", 'replace').split('<')[0].strip() + message = unicode(filectx.description(), 'utf8', 'replace') + return rev, date, user, message
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test_repo.py Thu Mar 17 23:25:56 2011 +0100 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import os + +import py +import py.test + +import hgstorage + +def clear_directory(top): + """ + A helper function to remove a directory with all its contents. + """ + + for root, dirs, files in os.walk(top, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + path = os.path.join(root, name) + if os.path.islink(path): + os.remove(path) + else: + os.rmdir(path) + try: + os.removedirs(top) + except OSError: + pass + +def pytest_funcarg__storage(request): + path = str(request.config.ensuretemp('storage')) + request.addfinalizer(lambda: clear_directory(path)) + storage = hgstorage.Storage(path) + storage.create() + return storage + + +class TestStorage(object): + def test_save(self, storage): + """Test saving of items.""" + + s = storage.get_session() + s.save('text', 'title', 'user', 'message') + s = storage.get_session() + exists = 'title' in s + assert exists + data = s.load('title') + assert 'text' == data + rev, date, user, message = s.meta('title') + assert 'user' == user + assert 'message' == message +
