Author: phd
Date: 2006-10-04 10:57:44 -0600 (Wed, 04 Oct 2006)
New Revision: 1981
Added:
SQLObject/trunk/sqlobject/dberrors.py
SQLObject/trunk/sqlobject/tests/test_exceptions.py
Modified:
SQLObject/trunk/sqlobject/__init__.py
SQLObject/trunk/sqlobject/mysql/mysqlconnection.py
SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py
SQLObject/trunk/sqlobject/tests/dbtest.py
SQLObject/trunk/sqlobject/tests/test_indexes.py
Log:
A patch by Mircea Amarascu <mi...@ag...> - a hierarchy of exceptions.
Modified: SQLObject/trunk/sqlobject/__init__.py
===================================================================
--- SQLObject/trunk/sqlobject/__init__.py 2006-10-04 16:19:06 UTC (rev 1980)
+++ SQLObject/trunk/sqlobject/__init__.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -5,6 +5,7 @@
from joins import *
from index import *
from dbconnection import connectionForURI
+import dberrors
## Each of these imports allows the driver to install itself
## Then we set up some backward compatibility
Added: SQLObject/trunk/sqlobject/dberrors.py
===================================================================
--- SQLObject/trunk/sqlobject/dberrors.py (rev 0)
+++ SQLObject/trunk/sqlobject/dberrors.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -0,0 +1,21 @@
+"""dberrors: database exception classes for SQLObject.
+
+ These classes are dictated by the DB API v2.0:
+
+ http://www.python.org/topics/database/DatabaseAPI-2.0.html
+"""
+
+class Error(StandardError): pass
+class Warning(StandardError): pass
+
+class InterfaceError(Error): pass
+class DatabaseError(Error): pass
+
+class InternalError(DatabaseError): pass
+class OperationalError(DatabaseError): pass
+class ProgrammingError(DatabaseError): pass
+class IntegrityError(DatabaseError): pass
+class DataError(DatabaseError): pass
+class NotSupportedError(DatabaseError): pass
+
+class DuplicateEntryError(IntegrityError): pass
Modified: SQLObject/trunk/sqlobject/mysql/mysqlconnection.py
===================================================================
--- SQLObject/trunk/sqlobject/mysql/mysqlconnection.py 2006-10-04 16:19:06 UTC (rev 1980)
+++ SQLObject/trunk/sqlobject/mysql/mysqlconnection.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -1,7 +1,16 @@
from sqlobject.dbconnection import DBAPI
+from sqlobject.dberrors import *
from sqlobject import col
MySQLdb = None
+class ErrorMessage(str):
+ def __new__(cls, e):
+ obj = str.__new__(cls, e[1])
+ obj.code = int(e[0])
+ obj.module = e.__module__
+ obj.exception = e.__class__.__name__
+ return obj
+
class MySQLConnection(DBAPI):
supportTransactions = False
@@ -54,9 +63,9 @@
conn = self.module.connect(host=self.host, port=self.port,
db=self.db, user=self.user, passwd=self.password, **self.kw)
except self.module.OperationalError, e:
- raise self.module.OperationalError(
+ raise OperationalError(
"%s; used connection string: host=%s, port=%s, db=%s, user=%s, pwd=%s" % (
- e, self.host, self.port, self.db, self.user, self.password)
+ e, self.host, self.port, self.db, self.user, self.password)
)
if hasattr(conn, 'autocommit'):
@@ -79,11 +88,33 @@
else:
return cursor.execute(query)
except MySQLdb.OperationalError, e:
- if e.args[0] == 2013: # SERVER_LOST error
+ if e.args[0] in (2006, 20013): # SERVER_GONE or SERVER_LOST error
if self.debug:
self.printDebug(conn, str(e), 'ERROR')
else:
- raise
+ raise OperationalError(ErrorMessage(e))
+ except MySQLdb.IntegrityError, e:
+ msg = ErrorMessage(e)
+ if e.args[0] == 1062:
+ raise DuplicateEntryError(msg)
+ else:
+ raise IntegrityError(msg)
+ except MySQLdb.InternalError, e:
+ raise InternalError(ErrorMessage(e))
+ except MySQLdb.ProgrammingError, e:
+ raise ProgrammingError(ErrorMessage(e))
+ except MySQLdb.DataError, e:
+ raise DataError(ErrorMessage(e))
+ except MySQLdb.NotSupportedError, e:
+ raise NotSupportedError(ErrorMessage(e))
+ except MySQLdb.DatabaseError, e:
+ raise DatabaseError(ErrorMessage(e))
+ except MySQLdb.InterfaceError, e:
+ raise InterfaceError(ErrorMessage(e))
+ except MySQLdb.Warning, e:
+ raise Warning(ErrorMessage(e))
+ except MySQLdb.Error, e:
+ raise Error(ErrorMessage(e))
def _queryInsertID(self, conn, soInstance, id, names, values):
table = soInstance.sqlmeta.table
@@ -136,8 +167,8 @@
# which is not always True (for an embedded application, e.g.)
self.query('DESCRIBE %s' % (tableName))
return True
- except MySQLdb.ProgrammingError, e:
- if e.args[0] == 1146: # ER_NO_SUCH_TABLE
+ except ProgrammingError, e:
+ if e.code == 1146: # ER_NO_SUCH_TABLE
return False
raise
Modified: SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py
===================================================================
--- SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py 2006-10-04 16:19:06 UTC (rev 1980)
+++ SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -1,11 +1,20 @@
from sqlobject.dbconnection import DBAPI
from sqlobject.col import popKey
+from sqlobject.dberrors import *
import thread
sqlite = None
using_sqlite2 = False
sqlite2_Binary = None
+class ErrorMessage(str):
+ def __new__(cls, e):
+ obj = str.__new__(cls, e[0])
+ obj.code = None
+ obj.module = e.__module__
+ obj.exception = e.__class__.__name__
+ return obj
+
class SQLiteConnection(DBAPI):
supportTransactions = True
@@ -158,6 +167,36 @@
return self._memoryConn
return sqlite.connect(self.filename, **self._connOptions)
+ def _executeRetry(self, conn, cursor, query):
+ if self.debug:
+ self.printDebug(conn, query, 'QueryR')
+ try:
+ return cursor.execute(query)
+ except self.module.OperationalError, e:
+ raise OperationalError(ErrorMessage(e))
+ except self.module.IntegrityError, e:
+ msg = ErrorMessage(e)
+ if msg.startswith('column') and msg.endswith('not unique'):
+ raise DuplicateEntryError(msg)
+ else:
+ raise IntegrityError(msg)
+ except self.module.InternalError, e:
+ raise InternalError(ErrorMessage(e))
+ except self.module.ProgrammingError, e:
+ raise ProgrammingError(ErrorMessage(e))
+ except self.module.DataError, e:
+ raise DataError(ErrorMessage(e))
+ except self.module.NotSupportedError, e:
+ raise NotSupportedError(ErrorMessage(e))
+ except self.module.DatabaseError, e:
+ raise DatabaseError(ErrorMessage(e))
+ except self.module.InterfaceError, e:
+ raise InterfaceError(ErrorMessage(e))
+ except self.module.Warning, e:
+ raise Warning(ErrorMessage(e))
+ except self.module.Error, e:
+ raise Error(ErrorMessage(e))
+
def _queryInsertID(self, conn, soInstance, id, names, values):
table = soInstance.sqlmeta.table
idName = soInstance.sqlmeta.idName
@@ -168,7 +207,7 @@
q = self._insertSQL(table, names, values)
if self.debug:
self.printDebug(conn, q, 'QueryIns')
- c.execute(q)
+ self._executeRetry(conn, c, q)
# lastrowid is a DB-API extension from "PEP 0249":
if id is None:
id = int(c.lastrowid)
Modified: SQLObject/trunk/sqlobject/tests/dbtest.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/dbtest.py 2006-10-04 16:19:06 UTC (rev 1980)
+++ SQLObject/trunk/sqlobject/tests/dbtest.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -33,6 +33,7 @@
"""
supportsMatrix = {
'+restrictedEnum': 'postgres',
+ '+exceptions': 'mysql sqlite',
'-transactions': 'mysql',
'-dropTableCascade': 'sybase mssql',
'-fromDatabase': 'sqlite sybase firebird',
Added: SQLObject/trunk/sqlobject/tests/test_exceptions.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_exceptions.py (rev 0)
+++ SQLObject/trunk/sqlobject/tests/test_exceptions.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -0,0 +1,17 @@
+from sqlobject import *
+from sqlobject.dberrors import DuplicateEntryError
+from sqlobject.tests.dbtest import *
+
+########################################
+## Table aliases and self-joins
+########################################
+
+class TestException(SQLObject):
+ name = StringCol(unique=True)
+
+def test_exceptions():
+ if not supports("exceptions"):
+ return
+ setupClass(TestException)
+ TestException(name="test")
+ raises(DuplicateEntryError, TestException, name="test")
Modified: SQLObject/trunk/sqlobject/tests/test_indexes.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_indexes.py 2006-10-04 16:19:06 UTC (rev 1980)
+++ SQLObject/trunk/sqlobject/tests/test_indexes.py 2006-10-04 16:57:44 UTC (rev 1981)
@@ -1,4 +1,5 @@
from sqlobject import *
+from sqlobject.dberrors import *
from sqlobject.tests.dbtest import *
########################################
@@ -29,7 +30,10 @@
mod = SOIndex1._connection.module
try:
SOIndex1(name='blah', number=0)
- except (mod.ProgrammingError, mod.IntegrityError, mod.OperationalError, mod.DatabaseError):
+ except (
+ mod.ProgrammingError, mod.IntegrityError, mod.OperationalError, mod.DatabaseError,
+ ProgrammingError, IntegrityError, OperationalError, DatabaseError
+ ):
# expected
pass
else:
|