[WIP] try to wrap database error into a custom exception

authorLaurent Peuch <cortex@worlddomination.be>
changeset3fc5a08db936
branchdefault
phasedraft
hiddenno
parent revision#a4d465a3e77d fix(ci): manually remove the .tox/doc directory
child revision<not specified>
files modified by this revision
cubicweb/_exceptions.py
cubicweb/server/sources/native.py
# HG changeset patch
# User Laurent Peuch <cortex@worlddomination.be>
# Date 1558427437 -7200
# Tue May 21 10:30:37 2019 +0200
# Node ID 3fc5a08db936409ff7efdf6eb195908f89ab7a27
# Parent a4d465a3e77d07cf6a79c121c10b2d6484cd7468
[WIP] try to wrap database error into a custom exception

diff --git a/cubicweb/_exceptions.py b/cubicweb/_exceptions.py
@@ -15,11 +15,10 @@
1  #
2  # You should have received a copy of the GNU Lesser General Public License along
3  # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
4  """Exceptions shared by different cubicweb packages."""
5 
6 -
7  from logilab.common.decorators import cachedproperty
8  from logilab.common.registry import RegistryException
9 
10  from yams import ValidationError  # noqa: F401
11 
@@ -85,10 +84,32 @@
12  class UnknownEid(RepositoryError):
13      """the eid is not defined in the system tables"""
14      msg = 'No entity with eid %s in the repository'
15 
16 
17 +class DatabaseQueryError(Exception):
18 +    """generic error when a database query failed"""
19 +
20 +    def __init__(self, query, args, cnx, exception):
21 +        self.cnx = cnx
22 +        message = (
23 +            "Error when executing the query: '%s' with args '%s' and context '%s':\n%s"
24 +        ) % (query, args, cnx, exception)
25 +
26 +        # magic, we want DatabaseQueryError to still be catchable as a
27 +        # exception raised by cursor.execute but we can't predict if psycopg2
28 +        # or other dependencies will be installed so we dynamically add the
29 +        # exception class to our parents to avoid breaking old "except
30 +        # Integiry" code for example
31 +        try:
32 +            self.__class__.__bases__ = (exception.__class__,) + self.__class__.__bases__
33 +        except TypeError as e:
34 +            pass
35 +
36 +        super().__init__(message)
37 +
38 +
39  class UniqueTogetherError(RepositoryError):
40      """raised when a unique_together constraint caused an IntegrityError"""
41 
42      def __init__(self, session, **kwargs):
43          self.session = session
diff --git a/cubicweb/server/sources/native.py b/cubicweb/server/sources/native.py
@@ -36,11 +36,12 @@
44  from logilab.database import get_db_helper, sqlgen
45 
46  from yams.schema import role_name
47 
48  from cubicweb import (UnknownEid, AuthenticationError, ValidationError, Binary,
49 -                      UniqueTogetherError, UndoTransactionException, ViolatedConstraint)
50 +                      UniqueTogetherError, UndoTransactionException, ViolatedConstraint,
51 +                      DatabaseQueryError)
52  from cubicweb import transaction as tx, server, neg_role, _
53  from cubicweb.utils import QueryCache
54  from cubicweb.debug import emit_to_debug_channel
55  from cubicweb.schema import VIRTUAL_RTYPES
56  from cubicweb.cwconfig import CubicWebNoAppConfiguration
@@ -749,11 +750,15 @@
57                      mo = re.match('^constraint (cstr.*) failed$', arg)
58                      if mo is not None:
59                          # sqlite3 (old)
60                          raise ViolatedConstraint(cnx, cstrname=mo.group(1),
61                                                   query=query)
62 -            raise
63 +            try:
64 +                exception = DatabaseQueryError(query, args, cnx.cnxset.cnx, ex)
65 +            except TypeError:
66 +                raise
67 +            raise exception
68          finally:
69              query_debug_informations["time"] = (time.time() - start) * 1000
70              emit_to_debug_channel("sql", query_debug_informations)
71          return cursor
72