Because CacheMiss calls str() on the record it's given, it's not possible to override __str__ and get field values: on the first cache miss (= the first field access) we're going to blow the stack: * any random field is accessed * and faults * which creates a CacheMiss * which stringifies the record * which accesses a field * which faults * which creates a CacheMiss * etc... Fix: * move the basic Model's stringification to repr(), which is implicitly called on str() if no __str__ is specified, for historical reasons we were doing the opposite * use repr() when initialising CacheMiss closes odoo/odoo#42966 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
151 lines
3.9 KiB
Python
151 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
"""The Odoo Exceptions module defines a few core exception types.
|
|
|
|
Those types are understood by the RPC layer.
|
|
Any other exception type bubbling until the RPC layer will be
|
|
treated as a 'Server error'.
|
|
|
|
.. note::
|
|
If you consider introducing new exceptions,
|
|
check out the :mod:`odoo.addons.test_exceptions` module.
|
|
"""
|
|
|
|
import logging
|
|
from inspect import currentframe
|
|
from .tools.func import frame_codeinfo
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
# kept for backward compatibility
|
|
class except_orm(Exception):
|
|
def __init__(self, name, value=None):
|
|
if type(self) == except_orm:
|
|
caller = frame_codeinfo(currentframe(), 1)
|
|
_logger.warning('except_orm is deprecated. Please use specific exceptions like UserError or AccessError. Caller: %s:%s', *caller)
|
|
self.name = name
|
|
self.value = value
|
|
self.args = (name, value)
|
|
|
|
def __str__(self):
|
|
if not self.value:
|
|
return str(self.name)
|
|
else:
|
|
return super().__str__()
|
|
|
|
|
|
class UserError(except_orm):
|
|
"""Generic error managed by the client.
|
|
|
|
Typically when the user tries to do something that has no sense given the current
|
|
state of a record.
|
|
"""
|
|
def __init__(self, msg):
|
|
super(UserError, self).__init__(msg, value='')
|
|
|
|
|
|
# deprecated due to collision with builtins, kept for compatibility
|
|
Warning = UserError
|
|
|
|
|
|
class RedirectWarning(Exception):
|
|
""" Warning with a possibility to redirect the user instead of simply
|
|
displaying the warning message.
|
|
|
|
:param int action_id: id of the action where to perform the redirection
|
|
:param str button_text: text to put on the button that will trigger
|
|
the redirection.
|
|
"""
|
|
# using this RedirectWarning won't crash if used as an except_orm
|
|
@property
|
|
def name(self):
|
|
return self.args[0]
|
|
|
|
|
|
class AccessDenied(Exception):
|
|
"""Login/password error.
|
|
|
|
.. note::
|
|
|
|
No traceback.
|
|
|
|
.. admonition:: Example
|
|
|
|
When you try to log with a wrong password.
|
|
"""
|
|
|
|
def __init__(self, message='Access denied'):
|
|
super(AccessDenied, self).__init__(message)
|
|
self.with_traceback(None)
|
|
self.__cause__ = None
|
|
self.traceback = ('', '', '')
|
|
|
|
|
|
class AccessError(except_orm):
|
|
"""Access rights error.
|
|
|
|
.. admonition:: Example
|
|
|
|
When you try to read a record that you are not allowed to.
|
|
"""
|
|
|
|
def __init__(self, msg):
|
|
super(AccessError, self).__init__(msg)
|
|
|
|
|
|
class CacheMiss(except_orm, KeyError):
|
|
"""Missing value(s) in cache.
|
|
|
|
.. admonition:: Example
|
|
|
|
When you try to read a value in a flushed cache.
|
|
"""
|
|
|
|
def __init__(self, record, field):
|
|
super(CacheMiss, self).__init__("%r.%s" % (record, field.name))
|
|
|
|
|
|
class MissingError(except_orm):
|
|
"""Missing record(s).
|
|
|
|
.. admonition:: Example
|
|
|
|
When you try to write on a deleted record.
|
|
"""
|
|
|
|
def __init__(self, msg):
|
|
super(MissingError, self).__init__(msg)
|
|
|
|
|
|
class ValidationError(except_orm):
|
|
"""Violation of python constraints.
|
|
|
|
.. admonition:: Example
|
|
|
|
When you try to create a new user with a login which already exist in the db.
|
|
"""
|
|
|
|
def __init__(self, msg):
|
|
super(ValidationError, self).__init__(msg)
|
|
|
|
|
|
class DeferredException(Exception):
|
|
""" Exception object holding a traceback for asynchronous reporting.
|
|
|
|
Some RPC calls (database creation and report generation) happen with
|
|
an initial request followed by multiple, polling requests. This class
|
|
is used to store the possible exception occuring in the thread serving
|
|
the first request, and is then sent to a polling request.
|
|
|
|
('Traceback' is misleading, this is really a exc_info() triple.)
|
|
"""
|
|
def __init__(self, msg, tb):
|
|
self.message = msg
|
|
self.traceback = tb
|
|
|
|
|
|
class QWebException(Exception):
|
|
pass
|