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
+