| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- # (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)
|