Permalink
Browse files

experimental multidb support

  • Loading branch information...
1 parent e392475 commit 64b0c468d4ae320d720d03d0cbc8e9a0b0e42f1f @mcdonc mcdonc committed Mar 28, 2012
Showing with 142 additions and 38 deletions.
  1. +2 −0 CHANGES.txt
  2. +1 −1 docs/conf.py
  3. +26 −0 docs/index.rst
  4. +60 −25 pyramid_zodbconn/__init__.py
  5. +36 −11 pyramid_zodbconn/tests/test_init.py
  6. +1 −1 setup.py
  7. +16 −0 tox.ini
View
@@ -3,6 +3,8 @@ Next release
- Includeme docstring fix.
+- Add named database capability.
+
0.2 (2011-08-24)
----------------
View
@@ -62,7 +62,7 @@
# other places throughout the built documents.
#
# The short X.Y version.
-version = '0.2'
+version = '0.3dev'
# The full version, including alpha/beta/rc tags.
release = version
View
@@ -88,6 +88,32 @@ configuration.
When the request is finalized, the connection you've opened via
``get_connection`` will be closed.
+Named Databases
+---------------
+
+If you need to use more than one ZODB database in your Pyramid application,
+you can use *named* databases via configuration. Named databases are
+specified by ``zodbconn.uri.thename`` in settings configuration. For
+example:
+
+.. code-block:: ini
+
+ [app:myapp]
+ ...
+ zodbconn.uri.main = zeo://localhost:9991?cache_size=25MB
+ zodbconn.uri.sessions = zeo://localhost:9992?cache_size=100MB
+ ...
+
+Once this is done, you can use :func:`pyramid_zodbconn.get_connection` to
+obtain a reference to each of the named databases:
+
+ main_conn = get_connection(request, 'main')
+ sessions_conn = get_connection(request, 'sessions')
+
+The ``zodbconn.uri.foo`` parameter is a URL which describes a ZODB database,
+the same as ``zodbconn.uri``. You can combine named and unnamed database
+configuration in the same application.
+
URI Schemes
-----------
@@ -2,45 +2,80 @@
from ZODB import DB
from pyramid.exceptions import ConfigurationError
-def get_connection(request):
+def get_connection(request, dbname=None):
""" Obtain a connection from the database set up as ``zodbconn.uri`` in
- the current configuration. ``request`` must be a Pyramid request object."""
+ the current configuration. ``request`` must be a Pyramid request object.
+ If you're using named databases, ``dbname`` must be the name of a
+ database (e.g. if you've added ``zodbconn.uri.foo`` to the configuration,
+ it should be ``foo``).
+ """
# not a tween. rationale: tweens don't get called until the router accepts
# a request. during paster shell, paster ptweens, etc, the router is
# never invoked
+
registry = request.registry
- zodb_conn = getattr(request, '_zodb_conn', None)
- if zodb_conn is None:
- zodb_db = getattr(registry, 'zodb_database', None)
- if zodb_db is None:
- raise ConfigurationError(
- 'pyramid_zodbconn not included in configuration or no '
- 'zodbconn.uri defined in Pyramid application settings')
- zodb_conn = request._zodb_conn = zodb_db.open()
- def finished(request):
- del request._zodb_conn
- zodb_conn.transaction_manager.abort()
- zodb_conn.close()
- request.add_finished_callback(finished)
- return zodb_conn
+ zodb_conns = getattr(request, '_zodb_conns', None)
+
+ if zodb_conns is None:
+ zodb_conns = request._zodb_conns = {}
+
+ conn = zodb_conns.get(dbname)
+ if conn is not None:
+ return conn
+
+ zodb_dbs = getattr(registry, '_zodb_databases', None)
+ if zodb_dbs is None:
+ raise ConfigurationError(
+ 'pyramid_zodbconn not included in configuration')
+
+ db = zodb_dbs.get(dbname)
+ if db is None:
+ if dbname is None:
+ msg = 'No zodbconn.uri defined in Pyramid settings'
+ else:
+ msg = 'No zodbconn.uri.%s defined in Pyramid settings' % dbname
+ raise ConfigurationError(msg)
+
+ zodbconn = db.open()
+ zodb_conns[dbname] = zodbconn
+
+ def finished(request):
+ del request._zodb_conns[dbname]
+ zodbconn.transaction_manager.abort()
+ zodbconn.close()
+
+ request.add_finished_callback(finished)
+ return zodbconn
def db_from_uri(uri, resolve_uri=resolve_uri):
storage_factory, dbkw = resolve_uri(uri)
storage = storage_factory()
return DB(storage, **dbkw)
+NAMED = 'zodbconn.uri.'
+
+def get_uris(settings):
+ uri = settings.get('zodbconn.uri')
+ if uri is not None:
+ yield None, uri
+ for k, v in settings.items():
+ if k.startswith(NAMED):
+ yield (k[len(NAMED):], v)
+
def includeme(config, db_from_uri=db_from_uri):
"""
This includeme recognizes a ``zodbconn.uri`` setting in your deployment
- settings and creates a ZODB database if it finds one::
+ settings and creates a ZODB database if it finds one. ``zodbconn.uri``
+ is the database URI or URIs (either a whitespace-delimited string, a
+ carriage-return-delimed string or a list of strings).
+
+ It will also recognized *named* database URIs:
- zodbconn.uri: The database URI or URIs (either a whitespace-delimited
- string, a carriage-return-delimed string or a list of strings).
+ zodbconn.uri.sessions = file:///home/project/var/Data.fs
+
"""
# db_from_uri in
- uri = config.registry.settings.get('zodbconn.uri')
- if uri is None:
- config.registry.zodb_database = None
- else:
- db = db_from_uri(uri)
- config.registry.zodb_database = db
+ databases = config.registry._zodb_databases = {}
+ for name, uri in get_uris(config.registry.settings):
+ databases[name] = db_from_uri(uri)
+
@@ -1,33 +1,46 @@
import unittest
from pyramid import testing
-class Test_get_zodb_conn(unittest.TestCase):
- def _callFUT(self, request):
+class Test_get_connection(unittest.TestCase):
+ def _callFUT(self, request, dbname=None):
from pyramid_zodbconn import get_connection
- return get_connection(request)
+ return get_connection(request, dbname=dbname)
def _makeRequest(self):
request = testing.DummyRequest()
- request.registry.zodb_database = DummyDB()
+ request.registry._zodb_databases = {None:DummyDB()}
return request
+ def test_without_include(self):
+ from pyramid.exceptions import ConfigurationError
+ request = self._makeRequest()
+ del request.registry._zodb_databases
+ self.assertRaises(ConfigurationError, self._callFUT, request)
+
def test_without_zodb_database(self):
from pyramid.exceptions import ConfigurationError
request = self._makeRequest()
- del request.registry.zodb_database
+ del request.registry._zodb_databases[None]
self.assertRaises(ConfigurationError, self._callFUT, request)
+ def test_without_zodb_database_named(self):
+ from pyramid.exceptions import ConfigurationError
+ request = self._makeRequest()
+ self.assertRaises(ConfigurationError, self._callFUT, request, 'wont')
+
def test_zodb_conn_already_exists(self):
request = self._makeRequest()
dummy_conn = DummyConnection()
- request._zodb_conn = dummy_conn
+ request._zodb_conns = {}
+ request._zodb_conns[None] = dummy_conn
conn = self._callFUT(request)
self.assertEqual(conn, dummy_conn)
def test_zodb_conn_new(self):
request = self._makeRequest()
conn = self._callFUT(request)
- self.assertEqual(conn, request.registry.zodb_database.connection)
+ self.assertEqual(conn,
+ request.registry._zodb_databases[None].connection)
self.assertEqual(len(request.finished_callbacks), 1)
callback = request.finished_callbacks[0]
self.assertFalse(conn.closed)
@@ -43,23 +56,35 @@ def tearDown(self):
testing.tearDown()
def _callFUT(self, config, db_from_uri=None):
+ config.captured_uris = []
self.db = DummyDB()
if db_from_uri is None:
def db_from_uri(uri):
- config.captured_uri = uri
+ config.captured_uris.append(uri)
return self.db
from pyramid_zodbconn import includeme
return includeme(config, db_from_uri=db_from_uri)
def test_with_uri(self):
self.config.registry.settings['zodbconn.uri'] = 'uri'
self._callFUT(self.config)
- self.assertEqual(self.config.captured_uri, 'uri')
- self.assertEqual(self.config.registry.zodb_database, self.db)
+ self.assertEqual(self.config.captured_uris, ['uri'])
+ self.assertEqual(self.config.registry._zodb_databases[None], self.db)
def test_without_uri(self):
self._callFUT(self.config)
- self.assertEqual(self.config.registry.zodb_database, None)
+ self.assertEqual(self.config.registry._zodb_databases, {})
+
+ def test_with_named_uris(self):
+ self.config.registry.settings['zodbconn.uri'] = 'uri'
+ self.config.registry.settings['zodbconn.uri.foo'] = 'uri.foo'
+ self.config.registry.settings['zodbconn.uri.bar'] = 'uri.bar'
+ self._callFUT(self.config)
+ self.assertEqual(self.config.captured_uris,
+ ['uri', 'uri.foo', 'uri.bar'])
+ self.assertEqual(self.config.registry._zodb_databases[None], self.db)
+ self.assertEqual(self.config.registry._zodb_databases['foo'], self.db)
+ self.assertEqual(self.config.registry._zodb_databases['bar'], self.db)
class Test_db_from_uri(unittest.TestCase):
def test_it(self):
View
@@ -30,7 +30,7 @@
]
setup(name='pyramid_zodbconn',
- version='0.2',
+ version='0.3dev',
description=('A package which provides integration betwen Pyramid and '
'ZODB'),
long_description=README + '\n\n' + CHANGES,
View
16 tox.ini
@@ -5,13 +5,29 @@ envlist =
[testenv]
commands =
python setup.py test -q
+deps =
+ ZODB3
+ pyramid>=1.3
+ WebOb>=1.2dev
+ transaction>=1.2
+
+[testenv:py25]
+deps =
+ ZODB3
+ pyramid<1.2.99
+ WebOb<1.1.99
+ transaction<1.1.99
[testenv:cover]
basepython =
python2.6
commands =
python setup.py nosetests --with-xunit --with-xcoverage
deps =
+ ZODB3
+ pyramid>=1.3
+ WebOb>=1.2dev
+ transaction>=1.2
nose
coverage==3.4
nosexcover

0 comments on commit 64b0c46

Please sign in to comment.