[IMP] xmlrpc: somewhat streamlined the exception types/

exception handling code.
OpenERPDispatcherException which was simply wrapping an exception together with
an exc_info is removed.

bzr revid: vmt@openerp.com-20110927102246-87ibftawukc2d8lc
This commit is contained in:
Vo Minh Thu 2011-09-27 12:22:46 +02:00
parent e8dc0edbaa
commit 08cfadaff0
5 changed files with 57 additions and 41 deletions

View File

@ -19,6 +19,14 @@
#
##############################################################################
""" OpenERP core exceptions.
This module defines a few 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'.
"""
class Warning(Exception):
pass
@ -32,5 +40,19 @@ class AccessDenied(Exception):
class AccessError(Exception):
""" Access rights error. """
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
self.args = (msg, tb)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -370,11 +370,6 @@ class Server:
def _close_socket(self):
close_socket(self.socket)
class OpenERPDispatcherException(Exception):
def __init__(self, exception, traceback):
self.exception = exception
self.traceback = traceback
def replace_request_password(args):
# password is always 3rd argument in a request, we replace it in RPC logs
# so it's easier to forward logs for diagnostics/debugging purposes...
@ -421,13 +416,18 @@ def dispatch_rpc(service_name, method, params):
raise
except openerp.exceptions.Warning:
raise
except openerp.exceptions.DeferredException, e:
_log('exception', tools.exception_to_unicode(e))
post_mortem(e.traceback)
raise
except Exception, e:
_log('exception', tools.exception_to_unicode(e))
tb = getattr(e, 'traceback', sys.exc_info())
tb_s = "".join(traceback.format_exception(*tb))
if tools.config['debug_mode'] and isinstance(tb[2], types.TracebackType):
import pdb
pdb.post_mortem(tb[2])
raise OpenERPDispatcherException(e, tb_s)
post_mortem(sys.exc_info())
raise
def post_mortem(info):
if tools.config['debug_mode'] and isinstance(info[2], types.TracebackType):
import pdb
pdb.post_mortem(info[2])
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -65,21 +65,13 @@ class TinySocketClientThread(threading.Thread):
except socket.timeout:
#terminate this channel because other endpoint is gone
break
except netsvc.OpenERPDispatcherException, e:
try:
new_e = Exception(tools.exception_to_unicode(e.exception)) # avoid problems of pickeling
logging.getLogger('web-services').debug("netrpc: rpc-dispatching exception", exc_info=True)
ts.mysend(new_e, exception=True, traceback=e.traceback)
except Exception:
#terminate this channel if we can't properly send back the error
logging.getLogger('web-services').exception("netrpc: cannot deliver exception message to client")
break
except Exception, e:
try:
new_e = Exception(tools.exception_to_unicode(e)) # avoid problems of pickeling
tb = getattr(e, 'traceback', sys.exc_info())
tb_s = "".join(traceback.format_exception(*tb))
logging.getLogger('web-services').debug("netrpc: communication-level exception", exc_info=True)
ts.mysend(e, exception=True, traceback=tb_s)
ts.mysend(new_e, exception=True, traceback=tb_s)
break
except Exception, ex:
#terminate this channel if we can't properly send back the error

View File

@ -162,7 +162,7 @@ class db(netsvc.ExportService):
self.actions.pop(id)
return (1.0, users)
else:
e = self.actions[id]['exception']
e = self.actions[id]['exception'] # TODO this seems wrong: actions[id]['traceback'] is set, but not 'exception'.
self.actions.pop(id)
raise Exception, e
@ -634,12 +634,6 @@ class wizard(netsvc.ExportService):
# False -> True
#
class ExceptionWithTraceback(Exception):
def __init__(self, msg, tb):
self.message = msg
self.traceback = tb
self.args = (msg, tb)
class report_spool(netsvc.ExportService):
def __init__(self, name='report'):
netsvc.ExportService.__init__(self, name)
@ -678,7 +672,7 @@ class report_spool(netsvc.ExportService):
(result, format) = obj.create(cr, uid, ids, datas, context)
if not result:
tb = sys.exc_info()
self._reports[id]['exception'] = ExceptionWithTraceback('RML is not available at specified location or not enough data to print!', tb)
self._reports[id]['exception'] = openerp.exceptions.DeferredException('RML is not available at specified location or not enough data to print!', tb)
self._reports[id]['result'] = result
self._reports[id]['format'] = format
self._reports[id]['state'] = True
@ -690,9 +684,9 @@ class report_spool(netsvc.ExportService):
logger.notifyChannel('web-services', netsvc.LOG_ERROR,
'Exception: %s\n%s' % (str(exception), tb_s))
if hasattr(exception, 'name') and hasattr(exception, 'value'):
self._reports[id]['exception'] = ExceptionWithTraceback(tools.ustr(exception.name), tools.ustr(exception.value))
self._reports[id]['exception'] = openerp.exceptions.DeferredException(tools.ustr(exception.name), tools.ustr(exception.value))
else:
self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb)
self._reports[id]['exception'] = openerp.exceptions.DeferredException(tools.exception_to_unicode(exception), tb)
self._reports[id]['state'] = True
cr.commit()
cr.close()
@ -721,7 +715,7 @@ class report_spool(netsvc.ExportService):
(result, format) = obj.create(cr, uid, ids, datas, context)
if not result:
tb = sys.exc_info()
self._reports[id]['exception'] = ExceptionWithTraceback('RML is not available at specified location or not enough data to print!', tb)
self._reports[id]['exception'] = openerp.exceptions.DeferredException('RML is not available at specified location or not enough data to print!', tb)
self._reports[id]['result'] = result
self._reports[id]['format'] = format
self._reports[id]['state'] = True
@ -733,9 +727,9 @@ class report_spool(netsvc.ExportService):
logger.notifyChannel('web-services', netsvc.LOG_ERROR,
'Exception: %s\n%s' % (str(exception), tb_s))
if hasattr(exception, 'name') and hasattr(exception, 'value'):
self._reports[id]['exception'] = ExceptionWithTraceback(tools.ustr(exception.name), tools.ustr(exception.value))
self._reports[id]['exception'] = openerp.exceptions.DeferredException(tools.ustr(exception.name), tools.ustr(exception.value))
else:
self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb)
self._reports[id]['exception'] = openerp.exceptions.DeferredException(tools.exception_to_unicode(exception), tb)
self._reports[id]['state'] = True
cr.commit()
cr.close()

View File

@ -36,6 +36,7 @@ import signal
import sys
import threading
import time
import traceback
import openerp
import openerp.modules
@ -46,7 +47,8 @@ import service.websrv_lib as websrv_lib
# constants are also defined client-side and must remain in sync.
# User code must use the exceptions defined in ``openerp.exceptions`` (not
# create directly ``xmlrpclib.Fault`` objects).
XML_RPC_FAULT_CODE_APPLICATION_ERROR = 2
XML_RPC_FAULT_CODE_APPLICATION_ERROR = 1
XML_RPC_FAULT_CODE_DEFERRED_APPLICATION_ERROR = 2
XML_RPC_FAULT_CODE_ACCESS_DENIED = 3
XML_RPC_FAULT_CODE_WARNING = 4
XML_RPC_FAULT_CODE_ACCESS_ERROR = 5
@ -77,13 +79,19 @@ def xmlrpc_return(start_response, service, method, params):
except openerp.exceptions.AccessDenied, e:
fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_ACCESS_DENIED, str(e))
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
except openerp.netsvc.OpenERPDispatcherException, e:
# TODO collapse this case with the next one.
fault = xmlrpclib.Fault(2, openerp.tools.exception_to_unicode(e.exception) + '\n' + e.traceback)
except openerp.exceptions.DeferredException, e:
info = e.traceback
# Which one is the best ?
formatted_info = "".join(traceback.format_exception(*info))
#formatted_info = openerp.tools.exception_to_unicode(e) + '\n' + info
fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_DEFERRED_APPLICATION_ERROR, formatted_info)
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
except:
exc_type, exc_value, exc_tb = sys.exc_info()
fault = xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value))
except Exception, e:
info = sys.exc_info()
# Which one is the best ?
formatted_info = "".join(traceback.format_exception(*info))
#formatted_info = openerp.tools.exception_to_unicode(e) + '\n' + info
fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)
response = xmlrpclib.dumps(fault, allow_none=None, encoding=None)
start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
return [response]