Thomas Waldmann avatar Thomas Waldmann committed 102dfda

added sqlalchemy store

Comments (0)

Files changed (3)

storage/stores/_tests/conftest.py

 
 STORES_PACKAGE = 'storage.stores'
 
-# memcached is not in the loop
-STORES = 'fs kc kt memory sqlite sqlite:compressed'.split()
+STORES = 'fs kc kt memory sqlite sqlite:compressed sqla'.split()
 
 
 constructors = {
                                           'test_table', compression_level=1),
     'kc': lambda store, tmpdir: store(str(tmpdir.join('store.kch'))),
     'kt': lambda store, _: store(),
+    'sqla': lambda store, tmpdir: store('sqlite:///%s' % str(tmpdir.join('store.sqlite')),
+                                        'test_table'),
 }
 
 

storage/stores/_tests/test_sqla.py

+# Copyright: 2011 MoinMoin:RonnyPfannschmidt
+# Copyright: 2011 MoinMoin:ThomasWaldmann
+# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
+
+"""
+MoinMoin - sqla store tests
+"""
+
+
+import pytest
+
+from ..sqla import BytesStore, FileStore
+
+@pytest.mark.multi(Store=[BytesStore, FileStore])
+def test_create(tmpdir, Store):
+    dbfile = tmpdir.join('store.sqlite')
+    assert not dbfile.check()
+    store = Store('sqlite:///%s' % str(dbfile))
+    assert not dbfile.check()
+    store.create()
+    assert dbfile.check()
+    return store
+
+@pytest.mark.multi(Store=[BytesStore, FileStore])
+def test_destroy(tmpdir, Store):
+    dbfile = tmpdir.join('store.sqlite')
+    store = test_create(tmpdir, Store)
+    store.destroy()
+    # XXX: check for dropped table
+

storage/stores/sqla.py

+# Copyright: 2011 MoinMoin:ThomasWaldmann
+# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
+
+"""
+MoinMoin - sqlalchemy store
+
+Stores k/v pairs into any database supported by sqlalchemy.
+"""
+
+
+from __future__ import absolute_import, division
+
+from StringIO import StringIO
+
+from sqlalchemy import create_engine, select, MetaData, Table, Column, String, Binary
+from sqlalchemy.pool import StaticPool
+
+from . import MutableStoreBase, BytesMutableStoreBase, FileMutableStoreBase
+
+KEY_LEN = 128
+VALUE_LEN = 1024 * 1024 # 1MB binary data
+
+
+class _Store(MutableStoreBase):
+    """
+    A simple dict-based in-memory store. No persistence!
+    """
+    @classmethod
+    def from_uri(cls, uri):
+        return cls(uri)
+
+    def __init__(self, db_uri=None, table_name='store', verbose=False):
+        """
+        :param db_uri: The database uri that we pass on to SQLAlchemy.
+                       May contain user/password/host/port/etc.
+        :param verbose: Verbosity setting. If set to True this will print all SQL queries
+                        to the console.
+        """
+        self.db_uri = db_uri
+        self.verbose = verbose
+        self.engine = None
+        self.table = None
+        self.table_name = table_name
+
+    def open(self):
+        db_uri = self.db_uri
+        if db_uri is None:
+            # These are settings that apply only for development / testing only. The additional args are necessary
+            # due to some limitations of the in-memory sqlite database.
+            db_uri = 'sqlite:///:memory:'
+            self.engine = create_engine(db_uri, poolclass=StaticPool, connect_args={'check_same_thread': False})
+        else:
+            self.engine = create_engine(db_uri, echo=self.verbose, echo_pool=self.verbose)
+
+        metadata = MetaData()
+        metadata.bind = self.engine
+        self.table = Table(self.table_name, metadata,
+                           Column('key', String(KEY_LEN), primary_key=True),
+                           Column('value', Binary(VALUE_LEN)),
+                          )
+
+    def close(self):
+        self.engine.dispose()
+        self.table = None
+
+    def create(self):
+        self.open()
+        self.table.create()
+        self.close()
+
+    def destroy(self):
+        self.open()
+        self.table.drop()
+        self.close()
+
+    def __iter__(self):
+        rows = select([self.table.c.key]).execute().fetchall()
+        for row in rows:
+            yield row[0]
+
+    def __delitem__(self, key):
+        self.table.delete().where(self.table.c.key == key).execute()
+
+
+class BytesStore(_Store, BytesMutableStoreBase):
+    def __getitem__(self, key):
+        value = select([self.table.c.value], self.table.c.key == key).execute().fetchone()
+        if value is not None:
+            return value[0]
+        else:
+            raise KeyError(key)
+
+    def __setitem__(self, key, value):
+        self.table.insert().execute(key=key, value=value)
+
+
+class FileStore(_Store, FileMutableStoreBase):
+    def __getitem__(self, key):
+        value = select([self.table.c.value], self.table.c.key == key).execute().fetchone()
+        if value is not None:
+            return StringIO(value[0])
+        else:
+            raise KeyError(key)
+
+    def __setitem__(self, key, stream):
+        self.table.insert().execute(key=key, value=stream.read())
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.