iro/model/dbdefer.py
changeset 302 3f4bdea2abbf
parent 294 0e75bd39767d
child 312 42fd5075a5d1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/model/dbdefer.py	Thu Sep 27 17:15:46 2012 +0200
@@ -0,0 +1,92 @@
+# Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net>
+# 
+# This file is part of Iro.
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+# #Software, and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+from decorator import FunctionMaker
+import sqlalchemy
+
+from .pool import runInDBPool
+from .utils import WithSession
+
+import inspect
+
+class DBDefer(object):
+    '''a twisted sqlalchemy connector.
+    
+    This is used as a Decorator class.
+    It adds a session parameter to the function with a valid session.
+    If the session parmaeter is used in calling this function only calls a commit after running the function, instead of createing a new session.'''
+    def __init__(self, engine, autocommit=False):
+        """
+        :param `sqlalchemy.engine.base.Engine` engine: a valid sqlalchemy engine object (normally created via :func:`sqlalchemy.create_engine`).
+        :param boolean autocommit: autocommit after running the function.
+        """
+        self.autocommit=autocommit
+        self.engine = engine
+
+    def __call__(self, func):
+        @runInDBPool
+        def wrapper(func,*a, **kw):
+            i = argspec.args.index("session")
+            ab = a[:i]
+            ae = a[i:-1]
+            if isinstance(a[-1],sqlalchemy.orm.session.Session):
+                al = ab + (a[-1],) + ae
+                ret = func(*al, **kw)
+                if self.autocommit:
+                    a[-1].commit()
+                return ret
+            else:
+                with WithSession(self.engine, self.autocommit) as session:
+                    al = ab + (session,) + ae
+                    return func(*al, **kw)
+
+        caller=func
+        argspec = inspect.getargspec(caller)
+        args =[i for i in argspec.args if i != "session" ]
+        sargs=", ".join(args)       
+        if sargs:
+            sargs+=", session"
+        else:
+            sargs="session"
+        defaults = argspec.defaults
+        if not defaults:
+            defaults = (None,)
+        else:
+            defaults += (None,)
+        evaldict = caller.func_globals.copy()
+        evaldict['_call_'] = func
+        evaldict['decorator'] = wrapper
+        wrap = FunctionMaker.create(
+              '%s(%s)' % (caller.__name__, sargs), 
+              'return decorator(_call_, %s)' % sargs,
+              evaldict, defaults=defaults, undecorated=caller, __wrapped__=caller,
+              doc=caller.__doc__, module=caller.__module__, addsource=True)
+        return wrap
+
+dbdefer=DBDefer(None)
+"""the decorator to use. Use :func:`setEngine` to set a valid engine after program has started."""
+
+def setEngine(engine,autocommit=False): 
+    """set the engine and autocommit for the decorator (see :class:`DBDefer`)."""
+    dbdefer.engine = engine
+    dbdefer.autocommit = autocommit
+
+