class SolidPgConnection: """Class representing solid connections to a PostgreSQL database. Underlying the connection is a classic PyGreSQL pg API database connection which is reset if the connection is lost or used too often. Thus the resulting connection is more solid ("tough and self-healing"). If you want the connection to be persistent in a threaded environment, then you should not deal with this class directly, but use either the PooledPg module or the PersistentPg module to get the connections. """ def __init__(self, maxusage=0, setsession=None, *args, **kwargs): """Create a "tough" PostgreSQL connection. maxusage: maximum usage limit for the underlying PygreSQL connection (number of uses, 0 or False means unlimited usage) When this limit is reached, the connection is automatically reset. setsession: optional list of SQL commands that may serve to prepare the session, e.g. ["set datestyle to ...", "set time zone ..."] args, kwargs: the parameters that shall be used to establish the PostgreSQL connections with PyGreSQL using pg.DB() """ self._maxusage = maxusage self._setsession_sql = setsession self._usage = 0 self._con = PgConnection(*args, **kwargs) self._setsession() def _setsession(self): """Execute the SQL commands for session preparation.""" if self._setsession_sql: for sql in self._setsession_sql: self._con.query(sql) def close(self): """Close the tough connection. You are allowed to close a tough connection. It will not complain if you close it more than once. """ try: self._con.close() self._usage = 0 except: pass def reopen(self): """Reopen the tough connection. It will not complain if the connection cannot be reopened.""" try: self._con.reopen() self._setsession() self._usage = 0 except: pass def reset(self): """Reset the tough connection. If a reset is not possible, tries to reopen the connection. It will not complain if the connection is already closed. """ try: self._con.reset() self._setsession() self._usage = 0 except: self.reopen() def _get_tough_method(self, method): """Return a "tough" version of a connection class method. The tough version checks whether the connection is bad (lost) and automatically and transparently tries to reset the connection if this is the case (for instance, the database has been restarted). """ def tough_method(*args, **kwargs): try: # check whether connection status is bad if not self._con.db.status: raise AttributeError if self._maxusage: # or connection used too often if self._usage >= self._maxusage: raise AttributeError except: self.reset() # then reset the connection try: r = method(*args, **kwargs) # try connection method except: # error in query if self._con.db.status: # if it was not a connection problem raise # then propagate the error else: # otherwise self.reset() # reset the connection r = method(*args, **kwargs) # and try one more time self._usage += 1 return r return tough_method def __getattr__(self, name): """Inherit the members of the standard connection class. Some methods are made "tougher" than in the standard version. """ attr = getattr(self._con, name) if name in ('query', 'get', 'insert', 'update', 'delete') \ or name.startswith('get_'): attr = self._get_tough_method(attr) return attr
class SteadyPgConnection: """Class representing steady connections to a PostgreSQL database. Underlying the connection is a classic PyGreSQL pg API database connection which is reset if the connection is lost or used too often. Thus the resulting connection is steadier ("tough and self-healing"). If you want the connection to be persistent in a threaded environment, then you should not deal with this class directly, but use either the PooledPg module or the PersistentPg module to get the connections. """ version = __version__ def __init__(self, maxusage=None, setsession=None, closeable=True, *args, **kwargs): """Create a "tough" PostgreSQL connection. maxusage: maximum usage limit for the underlying PyGreSQL connection (number of uses, 0 or None means unlimited usage) When this limit is reached, the connection is automatically reset. setsession: optional list of SQL commands that may serve to prepare the session, e.g. ["set datestyle to ...", "set time zone ..."] closeable: if this is set to false, then closing the connection will be silently ignored, but by default the connection can be closed args, kwargs: the parameters that shall be used to establish the PostgreSQL connections with PyGreSQL using pg.DB() """ # basic initialization to make finalizer work self._con = None self._closed = True # proper initialization of the connection if maxusage is None: maxusage = 0 if not isinstance(maxusage, int): raise TypeError("'maxusage' must be an integer value.") self._maxusage = maxusage self._setsession_sql = setsession self._closeable = closeable self._con = PgConnection(*args, **kwargs) self._transaction = False self._closed = False self._setsession() self._usage = 0 def _setsession(self): """Execute the SQL commands for session preparation.""" if self._setsession_sql: for sql in self._setsession_sql: self._con.query(sql) def _close(self): """Close the tough connection. You can always close a tough connection with this method and it will not complain if you close it more than once. """ if not self._closed: try: self._con.close() except Exception: pass self._transaction = False self._closed = True def close(self): """Close the tough connection. You are allowed to close a tough connection by default and it will not complain if you close it more than once. You can disallow closing connections by setting the closeable parameter to something false. In this case, closing tough connections will be silently ignored. """ if self._closeable: self._close() elif self._transaction: self.reset() def reopen(self): """Reopen the tough connection. It will not complain if the connection cannot be reopened. """ try: self._con.reopen() except Exception: if self._transcation: self._transaction = False try: self._con.query('rollback') except Exception: pass else: self._transaction = False self._closed = False self._setsession() self._usage = 0 def reset(self): """Reset the tough connection. If a reset is not possible, tries to reopen the connection. It will not complain if the connection is already closed. """ try: self._con.reset() self._transaction = False self._setsession() self._usage = 0 except Exception: try: self.reopen() except Exception: try: self.rollback() except Exception: pass def begin(self, sql=None): """Begin a transaction.""" self._transaction = True try: begin = self._con.begin except AttributeError: return self._con.query(sql or 'begin') else: # use existing method if available if sql: return begin(sql=sql) else: return begin() def end(self, sql=None): """Commit the current transaction.""" self._transaction = False try: end = self._con.end except AttributeError: return self._con.query(sql or 'end') else: if sql: return end(sql=sql) else: return end() def commit(self, sql=None): """Commit the current transaction.""" self._transaction = False try: commit = self._con.commit except AttributeError: return self._con.query(sql or 'commit') else: if sql: return commit(sql=sql) else: return commit() def rollback(self, sql=None): """Rollback the current transaction.""" self._transaction = False try: rollback = self._con.rollback except AttributeError: return self._con.query(sql or 'rollback') else: if sql: return rollback(sql=sql) else: return rollback() def _get_tough_method(self, method): """Return a "tough" version of a connection class method. The tough version checks whether the connection is bad (lost) and automatically and transparently tries to reset the connection if this is the case (for instance, the database has been restarted). """ def tough_method(*args, **kwargs): transaction = self._transaction if not transaction: try: # check whether connection status is bad if not self._con.db.status: raise AttributeError if self._maxusage: # or connection used too often if self._usage >= self._maxusage: raise AttributeError except Exception: self.reset() # then reset the connection try: result = method(*args, **kwargs) # try connection method except Exception: # error in query if transaction: # inside a transaction self._transaction = False raise # propagate the error elif self._con.db.status: # if it was not a connection problem raise # then propagate the error else: # otherwise self.reset() # reset the connection result = method(*args, **kwargs) # and try one more time self._usage += 1 return result return tough_method def __getattr__(self, name): """Inherit the members of the standard connection class. Some methods are made "tougher" than in the standard version. """ if self._con: attr = getattr(self._con, name) if (name in ('query', 'get', 'insert', 'update', 'delete') or name.startswith('get_')): attr = self._get_tough_method(attr) return attr else: raise InvalidConnection def __del__(self): """Delete the steady connection.""" try: self._close() # make sure the connection is closed except Exception: pass
class SteadyPgConnection: """Class representing steady connections to a PostgreSQL database. Underlying the connection is a classic PyGreSQL pg API database connection which is reset if the connection is lost or used too often. Thus the resulting connection is steadier ("tough and self-healing"). If you want the connection to be persistent in a threaded environment, then you should not deal with this class directly, but use either the PooledPg module or the PersistentPg module to get the connections. """ def __init__(self, maxusage=0, setsession=None, *args, **kwargs): """Create a "tough" PostgreSQL connection. maxusage: maximum usage limit for the underlying PygreSQL connection (number of uses, 0 or False means unlimited usage) When this limit is reached, the connection is automatically reset. setsession: optional list of SQL commands that may serve to prepare the session, e.g. ["set datestyle to ...", "set time zone ..."] args, kwargs: the parameters that shall be used to establish the PostgreSQL connections with PyGreSQL using pg.DB() """ self._maxusage = maxusage self._setsession_sql = setsession self._closeable = 1 self._usage = 0 self._con = PgConnection(*args, **kwargs) self._setsession() def _setsession(self): """Execute the SQL commands for session preparation.""" if self._setsession_sql: for sql in self._setsession_sql: self._con.query(sql) def _close(self): """Close the tough connection. You can always close a tough connection with this method and it will not complain if you close it more than once. """ try: self._con.close() self._usage = 0 except: pass def close(self): """Close the tough connection. You are allowed to close a tough connection by default and it will not complain if you close it more than once. You can disallow closing connections by setting the _closeable attribute to 0 or False. In this case, closing a connection will be silently ignored. """ if self._closeable: self._close() def reopen(self): """Reopen the tough connection. It will not complain if the connection cannot be reopened.""" try: self._con.reopen() self._setsession() self._usage = 0 except: pass def reset(self): """Reset the tough connection. If a reset is not possible, tries to reopen the connection. It will not complain if the connection is already closed. """ try: self._con.reset() self._setsession() self._usage = 0 except: self.reopen() def _get_tough_method(self, method): """Return a "tough" version of a connection class method. The tough version checks whether the connection is bad (lost) and automatically and transparently tries to reset the connection if this is the case (for instance, the database has been restarted). """ def tough_method(*args, **kwargs): try: # check whether connection status is bad if not self._con.db.status: raise AttributeError if self._maxusage: # or connection used too often if self._usage >= self._maxusage: raise AttributeError except: self.reset() # then reset the connection try: r = method(*args, **kwargs) # try connection method except: # error in query if self._con.db.status: # if it was not a connection problem raise # then propagate the error else: # otherwise self.reset() # reset the connection r = method(*args, **kwargs) # and try one more time self._usage += 1 return r return tough_method def __getattr__(self, name): """Inherit the members of the standard connection class. Some methods are made "tougher" than in the standard version. """ attr = getattr(self._con, name) if name in ('query', 'get', 'insert', 'update', 'delete') \ or name.startswith('get_'): attr = self._get_tough_method(attr) return attr