[DB-SIG] (Database Abstraction Layer) Wrapping DBAPI Exceptions

Kevin Jacobs jacobs at penguin.theopalgroup.com
Fri Sep 26 07:31:31 EDT 2003


On Fri, 26 Sep 2003, Peter L. Buschman wrote:
> The next thing I want to do is wrap the DPABI exceptions of the underlying 
> driver and raise my own equivalents ( which will handle
> exception arguments consistently... ) whenever they occur.  Here is the 
> strategy that comes to mind, but I am wondering how the rest of you might
> handle a case like this?

My DB-API abstraction layer virtualizes exceptions by patching the base
classes of the driver exception classes:

abstract.py:

  class Warning                  (StandardError)       : pass
  class Error                    (StandardError)       : pass
  class   InterfaceError           (Error)             : pass
  class   DatabaseError            (Error)             : pass
  class     DataError                (DatabaseError)   : pass
  class     OperationalError         (DatabaseError)   : pass
  class     IntegrityError           (DatabaseError)   : pass
  class     InternalError            (DatabaseError)   : pass
  class     ProgrammingError         (DatabaseError)   : pass
  class     NotSupportedError        (DatabaseError)   : pass

  dbapi_exceptions = [ 'Warning',
                       'Error',
                       'InterfaceError',
                       'DatabaseError',
                       'DataError',
                       'OperationalError',
                       'IntegrityError',
                       'InternalError',
                       'ProgrammingError',
                       'NotSupportedError' ]

  def __import_exceptions(module):
    for e in dbapi_exceptions:
      sub_exception    = getattr(module, e)
      abstract_exception = globals()[e]
      sub_exception.__bases__ += (abstract_exception,)

Whenever I import a native DB-API driver, I call __import_exceptions, e.g.:

  import psycopg, MySQLdb, Sybase, PoPy, pgdb, MSSQL # ...
  from   pyPgSQL import PgSQL

  __import_exceptions(psycopg)
  __import_exceptions(MySQLdb)
  __import_exceptions(Sybase)
  __import_exceptions(PoPy)
  __import_exceptions(pgdb)
  __import_exceptions(MSSQL)
  __import_exceptions(PgSQL)

Now I can catch any DB-API exception from the above modules without knowing
which one generated it.

  def query(dbcon, sql):
    cursor = dbcon.cursor()
    cursor.execute(sql)
    return cursor.fetchall()

  def get_accounts(dbcon):
    try:
      return query(dbcon, 'SELECT ACCOUNTID FROM ACCOUNTS;')
    except abstract.ProgrammingError:
      # Do not trap errors due to bad SQL
      raise
    except abstract.DatabaseError:
      # All other database related errors return empty results
      return []

Clearly, this example is a little contrived, but it gets the point across. 
The above routines do not need to know which underlying driver they are
connected to to be able to catch exceptions.  Of course, there are a
thousand _other_ things that are not abstracted in this example.

Feel free to reuse the above idea.  Just mention me in the credits
somewhere.  ;)

-Kevin


-- 
--
Kevin Jacobs
The OPAL Group - Enterprise Systems Architect
Voice: (440) 871-6725 x 19         E-mail: jacobs at theopalgroup.com
Fax:   (440) 871-6722              WWW:    http://www.theopalgroup.com/




More information about the DB-SIG mailing list