dbapi.py
changeset 6279 42079f752a9c
parent 6142 8bc6eac1fac1
parent 6257 7eb5f1aed728
child 6293 df44d7163582
--- a/dbapi.py	Tue Sep 14 08:48:44 2010 +0200
+++ b/dbapi.py	Thu Sep 16 18:56:35 2010 +0200
@@ -457,6 +457,12 @@
                                                  time() - tstart, clock() - cstart))
         return rset
 
+def check_not_closed(func):
+    def decorator(self, *args, **kwargs):
+        if self._closed is not None:
+            raise ProgrammingError('Closed connection')
+        return func(self, *args, **kwargs)
+    return decorator
 
 class Connection(object):
     """DB-API 2.0 compatible Connection object for CubicWeb
@@ -497,59 +503,15 @@
             self.rollback()
             return False #propagate the exception
 
-    def _txid(self, cursor=None): # XXX could now handle various isolation level!
-        # return a dict as bw compat trick
-        return {'txid': currentThread().getName()}
-
-    def request(self):
-        return DBAPIRequest(self.vreg, DBAPISession(self))
-
-    def check(self):
-        """raise `BadConnectionId` if the connection is no more valid, else
-        return its latest activity timestamp.
-        """
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
-        return self._repo.check_session(self.sessionid)
-
-    def set_session_props(self, **props):
-        """raise `BadConnectionId` if the connection is no more valid"""
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
-        self._repo.set_session_props(self.sessionid, props)
-
-    def get_shared_data(self, key, default=None, pop=False, txdata=False):
-        """return value associated to key in the session's data dictionary or
-        session's transaction's data if `txdata` is true.
-
-        If pop is True, value will be removed from the dictionnary.
+    def __del__(self):
+        """close the remote connection if necessary"""
+        if self._closed is None and self._close_on_del:
+            try:
+                self.close()
+            except:
+                pass
 
-        If key isn't defined in the dictionnary, value specified by the
-        `default` argument will be returned.
-        """
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
-        return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
-
-    def set_shared_data(self, key, value, txdata=False):
-        """set value associated to `key` in shared data
-
-        if `txdata` is true, the value will be added to the repository session's
-        transaction's data which are cleared on commit/rollback of the current
-        transaction.
-        """
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
-        return self._repo.set_shared_data(self.sessionid, key, value, txdata)
-
-    def get_schema(self):
-        """Return the schema currently used by the repository.
-
-        This is NOT part of the DB-API.
-        """
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
-        return self._repo.get_schema()
+    # connection initialization methods ########################################
 
     def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True):
         config = self.vreg.config
@@ -605,20 +567,18 @@
         if sitetitle is not None:
             self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
 
+    @check_not_closed
     def source_defs(self):
         """Return the definition of sources used by the repository.
 
         This is NOT part of the DB-API.
         """
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
         return self._repo.source_defs()
 
+    @check_not_closed
     def user(self, req=None, props=None):
         """return the User object associated to this connection"""
         # cnx validity is checked by the call to .user_info
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
         eid, login, groups, properties = self._repo.user_info(self.sessionid,
                                                               props)
         if req is None:
@@ -634,19 +594,103 @@
         user.cw_attr_cache['login'] = login # cache login
         return user
 
-    def __del__(self):
-        """close the remote connection if necessary"""
-        if self._closed is None and self._close_on_del:
-            try:
-                self.close()
-            except:
-                pass
+    @check_not_closed
+    def check(self):
+        """raise `BadConnectionId` if the connection is no more valid, else
+        return its latest activity timestamp.
+        """
+        self._repo.check_session(self.sessionid)
+
+    def _txid(self, cursor=None): # XXX could now handle various isolation level!
+        # return a dict as bw compat trick
+        return {'txid': currentThread().getName()}
+
+    def request(self):
+        return DBAPIRequest(self.vreg, DBAPISession(self))
+
+    # session data methods #####################################################
+
+    @check_not_closed
+    def set_session_props(self, **props):
+        """raise `BadConnectionId` if the connection is no more valid"""
+        self._repo.set_session_props(self.sessionid, props)
+
+    @check_not_closed
+    def get_shared_data(self, key, default=None, pop=False, txdata=False):
+        """return value associated to key in the session's data dictionary or
+        session's transaction's data if `txdata` is true.
+
+        If pop is True, value will be removed from the dictionnary.
 
+        If key isn't defined in the dictionnary, value specified by the
+        `default` argument will be returned.
+        """
+        return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
+
+    @check_not_closed
+    def set_shared_data(self, key, value, txdata=False):
+        """set value associated to `key` in shared data
+
+        if `txdata` is true, the value will be added to the repository
+        session's query data which are cleared on commit/rollback of the current
+        transaction.
+        """
+        return self._repo.set_shared_data(self.sessionid, key, value, txdata)
+
+    # meta-data accessors ######################################################
+
+    @check_not_closed
+    def get_schema(self):
+        """Return the schema currently used by the repository."""
+        return self._repo.get_schema()
+
+    @check_not_closed
+    def get_option_value(self, option):
+        """return the value for `option` in the repository configuration."""
+        return self._repo.get_option_value(option)
+
+    @check_not_closed
     def describe(self, eid):
-        if self._closed is not None:
-            raise ProgrammingError('Closed connection')
         return self._repo.describe(self.sessionid, eid, **self._txid())
 
+    # db-api like interface ####################################################
+
+    @check_not_closed
+    def commit(self):
+        """Commit pending transaction for this connection to the repository.
+
+        may raises `Unauthorized` or `ValidationError` if we attempted to do
+        something we're not allowed to for security or integrity reason.
+
+        If the transaction is undoable, a transaction id will be returned.
+        """
+        return self._repo.commit(self.sessionid, **self._txid())
+
+    @check_not_closed
+    def rollback(self):
+        """This method is optional since not all databases provide transaction
+        support.
+
+        In case a database does provide transactions this method causes the the
+        database to roll back to the start of any pending transaction.  Closing
+        a connection without committing the changes first will cause an implicit
+        rollback to be performed.
+        """
+        self._repo.rollback(self.sessionid, **self._txid())
+
+    @check_not_closed
+    def cursor(self, req=None):
+        """Return a new Cursor Object using the connection.
+
+        On pyro connection, you should get cursor after calling if
+        load_appobjects method if desired (which you should call if you intend
+        to use ORM abilities).
+        """
+        if req is None:
+            req = self.request()
+        return self.cursor_class(self, self._repo, req=req)
+
+    @check_not_closed
     def close(self):
         """Close the connection now (rather than whenever __del__ is called).
 
@@ -656,52 +700,13 @@
         connection.  Note that closing a connection without committing the
         changes first will cause an implicit rollback to be performed.
         """
-        if self._closed:
-            raise ProgrammingError('Connection is already closed')
         self._repo.close(self.sessionid, **self._txid())
         del self._repo # necessary for proper garbage collection
         self._closed = 1
 
-    def commit(self):
-        """Commit pending transaction for this connection to the repository.
-
-        may raises `Unauthorized` or `ValidationError` if we attempted to do
-        something we're not allowed to for security or integrity reason.
-
-        If the transaction is undoable, a transaction id will be returned.
-        """
-        if not self._closed is None:
-            raise ProgrammingError('Connection is already closed')
-        return self._repo.commit(self.sessionid, **self._txid())
-
-    def rollback(self):
-        """This method is optional since not all databases provide transaction
-        support.
-
-        In case a database does provide transactions this method causes the the
-        database to roll back to the start of any pending transaction.  Closing
-        a connection without committing the changes first will cause an implicit
-        rollback to be performed.
-        """
-        if not self._closed is None:
-            raise ProgrammingError('Connection is already closed')
-        self._repo.rollback(self.sessionid, **self._txid())
-
-    def cursor(self, req=None):
-        """Return a new Cursor Object using the connection.
-
-        On pyro connection, you should get cursor after calling if
-        load_appobjects method if desired (which you should call if you intend
-        to use ORM abilities).
-        """
-        if self._closed is not None:
-            raise ProgrammingError('Can\'t get cursor on closed connection')
-        if req is None:
-            req = self.request()
-        return self.cursor_class(self, self._repo, req=req)
-
     # undo support ############################################################
 
+    @check_not_closed
     def undoable_transactions(self, ueid=None, req=None, **actionfilters):
         """Return a list of undoable transaction objects by the connection's
         user, ordered by descendant transaction time.
@@ -734,6 +739,7 @@
             txinfo.req = req
         return txinfos
 
+    @check_not_closed
     def transaction_info(self, txuuid, req=None):
         """Return transaction object for the given uid.
 
@@ -748,6 +754,7 @@
         txinfo.req = req
         return txinfo
 
+    @check_not_closed
     def transaction_actions(self, txuuid, public=True):
         """Return an ordered list of action effectued during that transaction.
 
@@ -761,6 +768,7 @@
         return self._repo.transaction_actions(self.sessionid, txuuid, public,
                                               **self._txid())
 
+    @check_not_closed
     def undo_transaction(self, txuuid):
         """Undo the given transaction. Return potential restoration errors.