Author: ianb
Date: 2006-01-13 00:20:19 -0700 (Fri, 13 Jan 2006)
New Revision: 1501
Added:
SQLObject/trunk/sqlobject/tests/test_paste.py
SQLObject/trunk/sqlobject/wsgi_middleware.py
Modified:
SQLObject/docs/News.txt
SQLObject/trunk/setup.py
SQLObject/trunk/sqlobject/tests/dbtest.py
Log:
Added WSGI middleware for configuring connections and running request in transaction
Modified: SQLObject/docs/News.txt
===================================================================
--- SQLObject/docs/News.txt 2006-01-12 16:50:35 UTC (rev 1500)
+++ SQLObject/docs/News.txt 2006-01-13 07:20:19 UTC (rev 1501)
@@ -50,6 +50,11 @@
results are found (more than one result is always an error). If no
default is given, it is an error if no such object exists.
+* Added a WSGI middleware (in ``sqlobject.wsgi_middleware``) for
+ configuring the database for the request. Also handles
+ transactions. Available as ``egg:SQLObject`` in Paste Deploy
+ configuration files.
+
SQLObject 0.7.1
===============
Modified: SQLObject/trunk/setup.py
===================================================================
--- SQLObject/trunk/setup.py 2006-01-12 16:50:35 UTC (rev 1500)
+++ SQLObject/trunk/setup.py 2006-01-13 07:20:19 UTC (rev 1501)
@@ -4,8 +4,10 @@
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup
+ is_setuptools = True
except ImportError:
from distutils.core import setup
+ is_setuptools = False
subpackages = ['firebird', 'include', 'include.pydispatch', 'inheritance',
'manager', 'maxdb', 'mysql', 'mssql', 'postgres', 'sqlite',
@@ -18,6 +20,13 @@
DistributionMetadata.classifiers = None
DistributionMetadata.download_url = None
+kw = {}
+if is_setuptools:
+ kw['entry_points'] = """
+ [paste.filter_app_factory]
+ main = sqlobject.wsgi_middleware:make_middleware
+ """
+
setup(name="SQLObject",
version="0.8",
description="Object-Relational Manager, aka database wrapper",
@@ -58,6 +67,7 @@
'sqlite': ['pysqlite'],
# Others?
},
+ **kw
)
# Send announce to:
@@ -107,4 +117,4 @@
--
Ian Bicking / ia...@co... / http://blog.ianbicking.org
"""
-
\ No newline at end of file
+
Modified: SQLObject/trunk/sqlobject/tests/dbtest.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/dbtest.py 2006-01-12 16:50:35 UTC (rev 1500)
+++ SQLObject/trunk/sqlobject/tests/dbtest.py 2006-01-13 07:20:19 UTC (rev 1501)
@@ -84,9 +84,7 @@
'sqlite:///' + installedDBFilename)
def getConnection(**kw):
- name = conftest.option.Database
- if conftest.connectionShortcuts.has_key(name):
- name = conftest.connectionShortcuts[name]
+ name = getConnectionURI()
conn = sqlobject.connectionForURI(name, **kw)
if conftest.option.show_sql:
conn.debug = True
@@ -94,6 +92,12 @@
conn.debugOutput = True
return conn
+def getConnectionURI():
+ name = conftest.option.Database
+ if conftest.connectionShortcuts.has_key(name):
+ name = conftest.connectionShortcuts[name]
+ return name
+
try:
connection = getConnection()
except Exception, e:
@@ -291,6 +295,6 @@
sqlobject.main.warnings_level = None
sqlobject.main.exception_level = 0
-__all__ = ['getConnection', 'setupClass', 'Dummy', 'raises',
+__all__ = ['getConnection', 'getConnectionURI', 'setupClass', 'Dummy', 'raises',
'd', 'inserts', 'supports', 'deprecated_module',
'setup_module', 'teardown_module']
Added: SQLObject/trunk/sqlobject/tests/test_paste.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_paste.py 2006-01-12 16:50:35 UTC (rev 1500)
+++ SQLObject/trunk/sqlobject/tests/test_paste.py 2006-01-13 07:20:19 UTC (rev 1501)
@@ -0,0 +1,74 @@
+from dbtest import *
+from sqlobject import sqlhub, SQLObject, StringCol
+try:
+ from sqlobject.wsgi_middleware import make_middleware
+except ImportError:
+ disable = True
+
+class NameOnly(SQLObject):
+ name = StringCol()
+
+def makeapp(abort=False, begin=False, fail=False):
+ def app(environ, start_response):
+ NameOnly(name='app1')
+ if fail == 'early':
+ assert 0
+ start_response('200 OK', [('content-type', 'text/plain')])
+ if begin:
+ environ['sqlobject.begin']()
+ NameOnly(name='app2')
+ if abort:
+ environ['sqlobject.abort']()
+ if fail:
+ assert 0
+ return ['ok']
+ return app
+
+def makestack(abort=False, begin=False, fail=False, **kw):
+ app = makeapp(abort=abort, begin=begin, fail=fail)
+ app = make_middleware(app, {}, database=getConnectionURI(), **kw)
+ return app
+
+def runapp(**kw):
+ print '-'*8
+ app = makestack(**kw)
+ env = {}
+ def start_response(*args):
+ pass
+ try:
+ list(app(env, start_response))
+ return True
+ except AssertionError:
+ return False
+
+def setup():
+ setupClass(NameOnly)
+ NameOnly._connection = sqlhub
+
+def names():
+ names = [n.name for n in NameOnly.select(connection=getConnection())]
+ names.sort()
+ return names
+
+def test_fail():
+ setup()
+ assert not runapp(fail=True, use_transaction=True)
+ assert names() == []
+ setup()
+ assert not runapp(fail=True, use_transaction=False)
+ assert names() == ['app1', 'app2']
+ setup()
+ assert not runapp(fail=True, begin=True, use_transaction=True)
+ assert names() == ['app1']
+
+def test_other():
+ setup()
+ assert runapp(fail=False, begin=True, use_transaction=True)
+ assert names() == ['app1', 'app2']
+ setup()
+ assert runapp(fail=False, begin=True, abort=True, use_transaction=True)
+ assert names() == ['app1']
+ setup()
+ assert runapp(use_transaction=True)
+ assert names() == ['app1', 'app2']
+
Property changes on: SQLObject/trunk/sqlobject/tests/test_paste.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: SQLObject/trunk/sqlobject/wsgi_middleware.py
===================================================================
--- SQLObject/trunk/sqlobject/wsgi_middleware.py 2006-01-12 16:50:35 UTC (rev 1500)
+++ SQLObject/trunk/sqlobject/wsgi_middleware.py 2006-01-13 07:20:19 UTC (rev 1501)
@@ -0,0 +1,97 @@
+from paste.deploy.converters import asbool
+from paste.wsgilib import catch_errors
+from paste.util import import_string
+import sqlobject
+import threading
+
+def make_middleware(app, global_conf, database=None, use_transaction=False,
+ hub=None):
+ """
+ WSGI middleware that sets the connection for the request (using
+ the database URI or connection object) and the given hub (or
+ ``sqlobject.sqlhub`` if not given).
+
+ If ``use_transaction`` is true, then the request will be run in a
+ transaction.
+
+ Applications can use the keys (which are all no-argument functions):
+
+ ``sqlobject.get_connection()``:
+ Returns the connection object
+
+ ``sqlobject.abort()``:
+ Aborts the transaction. Does not raise an error, but at the *end*
+ of the request there will be a rollback.
+
+ ``sqlobject.begin()``:
+ Starts a transaction. First commits (or rolls back if aborted) if
+ this is run in a transaction.
+
+ ``sqlobject.in_transaction()``:
+ Returns true or false, depending if we are currently in a
+ transaction.
+ """
+ use_transaction = asbool(use_transaction)
+ if database is None:
+ database = global_conf.get('database')
+ if not database:
+ raise ValueError(
+ "You must provide a 'database' configuration value")
+ if isinstance(hub, basestring):
+ hub = import_string.eval_import(hub)
+ if not hub:
+ hub = sqlobject.sqlhub
+ if isinstance(database, basestring):
+ database = sqlobject.connectionForURI(database)
+ return SQLObjectMiddleware(app, database, use_transaction, hub)
+
+class SQLObjectMiddleware(object):
+
+ def __init__(self, app, conn, use_transaction, hub):
+ self.app = app
+ self.conn = conn
+ self.use_transaction = use_transaction
+ self.hub = hub
+
+ def __call__(self, environ, start_response):
+ conn = [self.conn]
+ if self.use_transaction:
+ conn[0] = conn[0].transaction()
+ any_errors = []
+ use_transaction = [self.use_transaction]
+ self.hub.threadConnection = conn[0]
+ def abort():
+ assert use_transaction[0], (
+ "You cannot abort, because a transaction is not being used")
+ any_errors.append(None)
+ def begin():
+ if use_transaction[0]:
+ if any_errors:
+ conn[0].rollback()
+ else:
+ conn[0].commit()
+ any_errors[:] = []
+ use_transaction[0] = True
+ conn[0] = self.conn.transaction()
+ self.hub.threadConnection = conn[0]
+ def error(exc_info=None):
+ any_errors.append(None)
+ ok()
+ def ok():
+ if use_transaction[0]:
+ if any_errors:
+ conn[0].rollback()
+ else:
+ conn[0].commit()
+ self.hub.threadConnection = None
+ def in_transaction():
+ return use_transaction[0]
+ def get_connection():
+ return conn[0]
+ environ['sqlobject.get_connection'] = get_connection
+ environ['sqlobject.abort'] = abort
+ environ['sqlobject.begin'] = begin
+ environ['sqlobject.in_transaction'] = in_transaction
+ return catch_errors(self.app, environ, start_response,
+ error_callback=error, ok_callback=ok)
+
Property changes on: SQLObject/trunk/sqlobject/wsgi_middleware.py
___________________________________________________________________
Name: svn:eol-style
+ native
|