diff options
author | Marc Abramowitz <marc@marc-abramowitz.com> | 2015-04-30 16:42:17 -0700 |
---|---|---|
committer | Marc Abramowitz <marc@marc-abramowitz.com> | 2015-04-30 16:42:17 -0700 |
commit | 12a3f1f4cfa7f88478dc1b0e949fcc095b9fc804 (patch) | |
tree | ddb8079523d846f0b074437fc33fa5e28b508183 /paste/transaction.py | |
download | paste-git-eliminate_cgi_parse_qsl.tar.gz |
Replace cgi.parse_qsl w/ six.moves.urllib.parse.parse_sqleliminate_cgi_parse_qsl_2eliminate_cgi_parse_qsl
because `cgi.parse_qsl` is deprecated, according to
https://docs.python.org/2/library/cgi.html#cgi.parse_qsl
Diffstat (limited to 'paste/transaction.py')
-rw-r--r-- | paste/transaction.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/paste/transaction.py b/paste/transaction.py new file mode 100644 index 0000000..1347acd --- /dev/null +++ b/paste/transaction.py @@ -0,0 +1,120 @@ +# (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) + |