| # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) |
| # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php |
| # (c) 2005 Clark C. Evans |
| # This module is part of the Python Paste Project and is released under |
| # the MIT License: http://www.opensource.org/licenses/mit-license.php |
| """ |
| Middleware related to transactions and database connections. |
| |
| At this time it is very basic; but will eventually sprout all that |
| two-phase commit goodness that I don't need. |
| |
| .. note:: |
| |
| This is experimental, and will change in the future. |
| """ |
| from paste.httpexceptions import HTTPException |
| from wsgilib import catch_errors |
| |
| class TransactionManagerMiddleware(object): |
| |
| def __init__(self, application): |
| self.application = application |
| |
| def __call__(self, environ, start_response): |
| environ['paste.transaction_manager'] = manager = Manager() |
| # This makes sure nothing else traps unexpected exceptions: |
| environ['paste.throw_errors'] = True |
| return catch_errors(self.application, environ, start_response, |
| error_callback=manager.error, |
| ok_callback=manager.finish) |
| |
| class Manager(object): |
| |
| def __init__(self): |
| self.aborted = False |
| self.transactions = [] |
| |
| def abort(self): |
| self.aborted = True |
| |
| def error(self, exc_info): |
| self.aborted = True |
| self.finish() |
| |
| def finish(self): |
| for trans in self.transactions: |
| if self.aborted: |
| trans.rollback() |
| else: |
| trans.commit() |
| |
| |
| class ConnectionFactory(object): |
| """ |
| Provides a callable interface for connecting to ADBAPI databases in |
| a WSGI style (using the environment). More advanced connection |
| factories might use the REMOTE_USER and/or other environment |
| variables to make the connection returned depend upon the request. |
| """ |
| def __init__(self, module, *args, **kwargs): |
| #assert getattr(module,'threadsaftey',0) > 0 |
| self.module = module |
| self.args = args |
| self.kwargs = kwargs |
| |
| # deal with database string quoting issues |
| self.quote = lambda s: "'%s'" % s.replace("'","''") |
| if hasattr(self.module,'PgQuoteString'): |
| self.quote = self.module.PgQuoteString |
| |
| def __call__(self, environ=None): |
| conn = self.module.connect(*self.args, **self.kwargs) |
| conn.__dict__['module'] = self.module |
| conn.__dict__['quote'] = self.quote |
| return conn |
| |
| def BasicTransactionHandler(application, factory): |
| """ |
| Provides a simple mechanism for starting a transaction based on the |
| factory; and for either committing or rolling back the transaction |
| depending on the result. It checks for the response's current |
| status code either through the latest call to start_response; or |
| through a HTTPException's code. If it is a 100, 200, or 300; the |
| transaction is committed; otherwise it is rolled back. |
| """ |
| def basic_transaction(environ, start_response): |
| conn = factory(environ) |
| environ['paste.connection'] = conn |
| should_commit = [500] |
| def finalizer(exc_info=None): |
| if exc_info: |
| if isinstance(exc_info[1], HTTPException): |
| should_commit.append(exc_info[1].code) |
| if should_commit.pop() < 400: |
| conn.commit() |
| else: |
| try: |
| conn.rollback() |
| except: |
| # TODO: check if rollback has already happened |
| return |
| conn.close() |
| def basictrans_start_response(status, headers, exc_info = None): |
| should_commit.append(int(status.split(" ")[0])) |
| return start_response(status, headers, exc_info) |
| return catch_errors(application, environ, basictrans_start_response, |
| finalizer, finalizer) |
| return basic_transaction |
| |
| __all__ = ['ConnectionFactory', 'BasicTransactionHandler'] |
| |
| if '__main__' == __name__ and False: |
| from pyPgSQL import PgSQL |
| factory = ConnectionFactory(PgSQL, database="testing") |
| conn = factory() |
| curr = conn.cursor() |
| curr.execute("SELECT now(), %s" % conn.quote("B'n\\'gles")) |
| (time, bing) = curr.fetchone() |
| print(bing, time) |
| |