From f82c6503b7a194010f0a0bf3d29790bef04b898e Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Fri, 30 Sep 2011 10:50:12 +0200 Subject: [PATCH] [FIX] xmlrpc: handle old/new exceptions with old/new clients. bzr revid: vmt@openerp.com-20110930085012-pqzzb3qwvdwu9hxy --- openerp/exceptions.py | 3 +-- openerp/netsvc.py | 5 +--- openerp/osv/osv.py | 12 +++++---- openerp/wsgi.py | 63 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/openerp/exceptions.py b/openerp/exceptions.py index d12910c3448..fbf52c7f3a9 100644 --- a/openerp/exceptions.py +++ b/openerp/exceptions.py @@ -33,7 +33,7 @@ class Warning(Exception): class AccessDenied(Exception): """ Login/password error. No message, no traceback. """ def __init__(self): - super(AccessDenied, self).__init__('AccessDenied.') + super(AccessDenied, self).__init__('Access denied.') self.traceback = ('', '', '') class AccessError(Exception): @@ -52,6 +52,5 @@ class DeferredException(Exception): def __init__(self, msg, tb): self.message = msg self.traceback = tb - self.args = (msg, tb) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/netsvc.py b/openerp/netsvc.py index 1d0b2a25726..1d4bf0c2b58 100644 --- a/openerp/netsvc.py +++ b/openerp/netsvc.py @@ -63,10 +63,7 @@ def close_socket(sock): def abort_response(dummy_1, description, dummy_2, details): # TODO Replace except_{osv,orm} with these directly. - if description == 'AccessError': - raise openerp.exceptions.AccessError(details) - else: - raise openerp.exceptions.Warning(details) + raise openerp.osv.osv.except_osv(description, detail) class Service(object): """ Base class for *Local* services diff --git a/openerp/osv/osv.py b/openerp/osv/osv.py index 821c52a55ef..b3f2a045589 100644 --- a/openerp/osv/osv.py +++ b/openerp/osv/osv.py @@ -34,8 +34,12 @@ from openerp.tools.translate import translate from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel import openerp.exceptions -# For backward compatibility -except_osv = openerp.exceptions.Warning +# Deprecated. +class except_osv(Exception): + def __init__(self, name, value): + self.name = name + self.value = value + self.args = (name, value) service = None @@ -115,9 +119,7 @@ class object_proxy(): raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.') return f(self, dbname, *args, **kwargs) except orm.except_orm, inst: - if inst.name == 'AccessError': - self.logger.debug("AccessError", exc_info=True) - netsvc.abort_response(1, inst.name, 'warning', inst.value) + raise except_osv(inst.name, inst.value) except except_osv: raise except IntegrityError, inst: diff --git a/openerp/wsgi.py b/openerp/wsgi.py index e5aa378bf04..b334c72b3ef 100644 --- a/openerp/wsgi.py +++ b/openerp/wsgi.py @@ -48,12 +48,15 @@ import service.websrv_lib as websrv_lib # User code must use the exceptions defined in ``openerp.exceptions`` (not # create directly ``xmlrpclib.Fault`` objects). XML_RPC_FAULT_CODE_APPLICATION_ERROR = 1 +# Unused, deferred errors are indistinguishable from normal application +# errors. We keep them so we can use the word 'indistinguishable' twice +# in the same comment. XML_RPC_FAULT_CODE_DEFERRED_APPLICATION_ERROR = 2 XML_RPC_FAULT_CODE_ACCESS_DENIED = 3 XML_RPC_FAULT_CODE_ACCESS_ERROR = 4 XML_RPC_FAULT_CODE_WARNING = 5 -def xmlrpc_return(start_response, service, method, params): +def xmlrpc_return(start_response, service, method, params, legacy_exceptions=False): """ Helper to call a service's method with some params, using a wsgi-supplied ``start_response`` callback. @@ -70,6 +73,20 @@ def xmlrpc_return(start_response, service, method, params): try: result = openerp.netsvc.dispatch_rpc(service, method, params) response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None) + except Exception, e: + if legacy_exceptions: + response = xmlrpc_handle_exception_legacy(e) + else: + response = xmlrpc_handle_exception(e) + start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))]) + return [response] + +def xmlrpc_handle_exception(e): + try: + raise e + except openerp.osv.osv.except_osv, e: # legacy + fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value)) + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) except openerp.exceptions.Warning, e: fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_WARNING, str(e)) response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) @@ -84,17 +101,47 @@ def xmlrpc_return(start_response, service, method, params): # 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) + fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info) + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + except Exception, e: + if hasattr(e, 'message') and e.message == 'AccessDenied': # legacy + fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_ACCESS_DENIED, str(e)) + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + else: + 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) + return response + +def xmlrpc_handle_exception_legacy(e): + try: + raise e + except openerp.osv.osv.except_osv, e: + fault = xmlrpclib.Fault('warning -- ' + e.name + '\n\n' + e.value, '') + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + except openerp.exceptions.Warning, e: + fault = xmlrpclib.Fault('warning -- Warning\n\n' + str(e), '') + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + except openerp.exceptions.AccessError, e: + fault = xmlrpclib.Fault('warning -- AccessError\n\n' + str(e), '') + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + except openerp.exceptions.AccessDenied, e: + fault = xmlrpclib.Fault('AccessDenied', str(e)) + response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) + except openerp.exceptions.DeferredException, e: + info = e.traceback + formatted_info = "".join(traceback.format_exception(*info)) + fault = xmlrpclib.Fault(openerp.tools.ustr(e.message), formatted_info) response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) 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) + fault = xmlrpclib.Fault(openerp.tools.exception_to_unicode(e), 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] + return response def wsgi_xmlrpc(environ, start_response): """ The main OpenERP WSGI handler.""" @@ -138,7 +185,7 @@ def legacy_wsgi_xmlrpc(environ, start_response): path = environ['PATH_INFO'][len('/xmlrpc/'):] # expected to be one of db, object, ... params, method = xmlrpclib.loads(data) - return xmlrpc_return(start_response, path, method, params) + return xmlrpc_return(start_response, path, method, params, True) def wsgi_jsonrpc(environ, start_response): pass