# HG changeset patch # User Sylvain Thénault # Date 1284629117 -7200 # Node ID d33509b79efeb505927feb37529e644d4984cc45 # Parent afc7575684923d9df7029304d667b1b09017d30f [dbapi] reorganize Connection methods and use a @check_not_closed decorator diff -r afc757568492 -r d33509b79efe dbapi.py --- a/dbapi.py Wed Sep 15 18:43:14 2010 +0200 +++ b/dbapi.py Thu Sep 16 11:25:17 2010 +0200 @@ -459,6 +459,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 @@ -499,51 +505,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""" - if self._closed is not None: - raise ProgrammingError('Closed connection') - 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 __del__(self): + """close the remote connection if necessary""" + if self._closed is None and self._close_on_del: + try: + self.close() + except: + pass - def get_shared_data(self, key, default=None, pop=False): - """return value associated to `key` in shared data""" - if self._closed is not None: - raise ProgrammingError('Closed connection') - return self._repo.get_shared_data(self.sessionid, key, default, pop) - - def set_shared_data(self, key, value, querydata=False): - """set value associated to `key` in shared data - - if `querydata` is true, the value will be added to the repository - session's query data which are cleared on commit/rollback of the current - transaction, and won't be available through the connexion, only on the - repository side. - """ - if self._closed is not None: - raise ProgrammingError('Closed connection') - return self._repo.set_shared_data(self.sessionid, key, value, querydata) - - 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 @@ -599,20 +569,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: @@ -628,19 +596,90 @@ user['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""" + 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): + """return value associated to `key` in shared data""" + return self._repo.get_shared_data(self.sessionid, key, default, pop) + + @check_not_closed + def set_shared_data(self, key, value, querydata=False): + """set value associated to `key` in shared data + + if `querydata` is true, the value will be added to the repository + session's query data which are cleared on commit/rollback of the current + transaction, and won't be available through the connexion, only on the + repository side. + """ + return self._repo.set_shared_data(self.sessionid, key, value, querydata) + + # 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 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). @@ -650,52 +689,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. @@ -728,6 +728,7 @@ txinfo.req = req return txinfos + @check_not_closed def transaction_info(self, txuuid, req=None): """Return transaction object for the given uid. @@ -742,6 +743,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. @@ -755,6 +757,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.