Antony Lesuisse (OpenERP) has proposed merging 
lp:~openerp-dev/openobject-server/trunk-xmlrpc-1-vmt into lp:openobject-server.

Requested reviews:
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-xmlrpc-1-vmt/+merge/77774

xmlrpc versioning and old style api exceptions
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-xmlrpc-1-vmt/+merge/77774
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-server/trunk-xmlrpc-1-vmt.
=== modified file 'openerp/exceptions.py'
--- openerp/exceptions.py	2011-09-28 22:05:39 +0000
+++ openerp/exceptions.py	2011-10-01 00:54:24 +0000
@@ -33,7 +33,7 @@
 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 @@
     def __init__(self, msg, tb):
         self.message = msg
         self.traceback = tb
-        self.args = (msg, tb)
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== modified file 'openerp/netsvc.py'
--- openerp/netsvc.py	2011-09-29 00:21:57 +0000
+++ openerp/netsvc.py	2011-10-01 00:54:24 +0000
@@ -63,10 +63,7 @@
 
 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

=== modified file 'openerp/osv/osv.py'
--- openerp/osv/osv.py	2011-09-29 22:49:19 +0000
+++ openerp/osv/osv.py	2011-10-01 00:54:24 +0000
@@ -34,8 +34,12 @@
 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 @@
                     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:

=== modified file 'openerp/service/web_services.py'
--- openerp/service/web_services.py	2011-09-29 00:21:57 +0000
+++ openerp/service/web_services.py	2011-10-01 00:54:24 +0000
@@ -49,6 +49,8 @@
     procedures to be called. Each method has its own arguments footprint.
 """
 
+RPC_VERSION_1 = {'server_version': '6.1', 'protocol_version': 1}
+
 # This should be moved to openerp.modules.db, along side initialize().
 def _initialize_db(serv, id, db_name, demo, lang, user_password):
     cr = None
@@ -377,7 +379,7 @@
             return res or False
         elif method in ['about', 'timezone_get', 'get_server_environment',
                         'login_message','get_stats', 'check_connectivity',
-                        'list_http_services']:
+                        'list_http_services', 'version']:
             pass
         elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel', 'get_os_time', 'get_sqlcount']:
             passwd = params[0]
@@ -389,6 +391,9 @@
         fn = getattr(self, 'exp_'+method)
         return fn(*params)
 
+    def exp_version(self):
+        return RPC_VERSION_1
+
     def exp_about(self, extended=False):
         """Return information about the OpenERP Server.
 

=== modified file 'openerp/wsgi.py'
--- openerp/wsgi.py	2011-09-30 09:37:49 +0000
+++ openerp/wsgi.py	2011-10-01 00:54:24 +0000
@@ -47,13 +47,23 @@
 # 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_CLIENT_ERROR = 1 # again, indistinguishable from app. error.
 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):
+# The new (6.1) versioned RPC paths.
+XML_RPC_PATH = '/openerp/xmlrpc'
+XML_RPC_PATH_1 = '/openerp/xmlrpc/1'
+JSON_RPC_PATH = '/openerp/jsonrpc'
+JSON_RPC_PATH_1 = '/openerp/jsonrpc/1'
+
+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,41 +80,81 @@
     try:
         result = openerp.netsvc.dispatch_rpc(service, method, params)
         response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
-    except openerp.exceptions.Warning, e:
+    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):
+    if isinstance(e, openerp.osv.osv.except_osv): # legacy
+        fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value))
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    elif isinstance(e, openerp.exceptions.Warning):
         fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_WARNING, str(e))
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
-    except openerp.exceptions.AccessError, e:
+    elif isinstance (e, openerp.exceptions.AccessError):
         fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_ACCESS_ERROR, str(e))
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
-    except openerp.exceptions.AccessDenied, e:
+    elif isinstance(e, openerp.exceptions.AccessDenied):
         fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_ACCESS_DENIED, str(e))
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
-    except openerp.exceptions.DeferredException, e:
+    elif isinstance(e, openerp.exceptions.DeferredException):
         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 Exception, e:
+        fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    else:
+        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):
+    if isinstance(e, openerp.osv.osv.except_osv):
+        fault = xmlrpclib.Fault('warning -- ' + e.name + '\n\n' + e.value, '')
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    elif isinstance(e, openerp.exceptions.Warning):
+        fault = xmlrpclib.Fault('warning -- Warning\n\n' + str(e), '')
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    elif isinstance(e, openerp.exceptions.AccessError):
+        fault = xmlrpclib.Fault('warning -- AccessError\n\n' + str(e), '')
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    elif isinstance(e, openerp.exceptions.AccessDenied):
+        fault = xmlrpclib.Fault('AccessDenied', str(e))
+        response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
+    elif isinstance(e, openerp.exceptions.DeferredException):
+        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)
+    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)
+        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):
+def wsgi_xmlrpc_1(environ, start_response):
     """ The main OpenERP WSGI handler."""
-    if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/openerp/6.1/xmlrpc'):
+    if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith(XML_RPC_PATH_1):
         length = int(environ['CONTENT_LENGTH'])
         data = environ['wsgi.input'].read(length)
 
         params, method = xmlrpclib.loads(data)
 
-        path = environ['PATH_INFO'][len('/openerp/6.1/xmlrpc'):]
+        path = environ['PATH_INFO'][len(XML_RPC_PATH_1):]
         if path.startswith('/'): path = path[1:]
         if path.endswith('/'): p = path[:-1]
         path = path.split('/')
@@ -125,11 +175,44 @@
             service, db_name = path
             params = (db_name,) + params
 
-            if service == 'model':
-                service = 'object'
-            return xmlrpc_return(start_response, service, method, params)
-
-        # TODO the body has been read, need to raise an exception (not return None).
+            return xmlrpc_return(start_response, service, method, params)
+
+        # A db segment and a model segment must be given.
+        elif len(path) == 3 and path[0] == 'model':
+            service, db_name, model_name = path
+            params = (db_name,) + params[:2] + (model_name,) + params[2:]
+            service = 'object'
+            return xmlrpc_return(start_response, service, method, params)
+
+        # The body has been read, need to raise an exception (not return None).
+        fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_CLIENT_ERROR, '')
+        response = xmlrpclib.dumps(fault, allow_none=None, encoding=None)
+        start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
+        return [response]
+
+def wsgi_xmlrpc(environ, start_response):
+    """ WSGI handler to return the versions."""
+    if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith(XML_RPC_PATH):
+        length = int(environ['CONTENT_LENGTH'])
+        data = environ['wsgi.input'].read(length)
+
+        params, method = xmlrpclib.loads(data)
+
+        path = environ['PATH_INFO'][len(XML_RPC_PATH):]
+        if path.startswith('/'): path = path[1:]
+        if path.endswith('/'): p = path[:-1]
+        path = path.split('/')
+
+        # All routes are hard-coded.
+
+        if len(path) == 1 and path[0] == '' and method in ('version',):
+            return xmlrpc_return(start_response, 'common', method, ())
+
+        # The body has been read, need to raise an exception (not return None).
+        fault = xmlrpclib.Fault(XML_RPC_FAULT_CODE_CLIENT_ERROR, '')
+        response = xmlrpclib.dumps(fault, allow_none=None, encoding=None)
+        start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
+        return [response]
 
 def legacy_wsgi_xmlrpc(environ, start_response):
     if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
@@ -138,7 +221,7 @@
         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
@@ -309,6 +392,7 @@
 
     # Try all handlers until one returns some result (i.e. not None).
     wsgi_handlers = [
+        wsgi_xmlrpc_1,
         wsgi_xmlrpc,
         wsgi_jsonrpc,
         legacy_wsgi_xmlrpc,

=== added directory 'tests/addons'
=== added directory 'tests/addons/test_exceptions'
=== added file 'tests/addons/test_exceptions/__init__.py'
--- tests/addons/test_exceptions/__init__.py	1970-01-01 00:00:00 +0000
+++ tests/addons/test_exceptions/__init__.py	2011-10-01 00:54:24 +0000
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+import models
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'tests/addons/test_exceptions/__openerp__.py'
--- tests/addons/test_exceptions/__openerp__.py	1970-01-01 00:00:00 +0000
+++ tests/addons/test_exceptions/__openerp__.py	2011-10-01 00:54:24 +0000
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+{
+    'name': 'test-exceptions',
+    'version': '0.1',
+    'category': 'Tests',
+    'description': """A module to generate exceptions.""",
+    'author': 'OpenERP SA',
+    'maintainer': 'OpenERP SA',
+    'website': 'http://www.openerp.com',
+    'depends': ['base'],
+    'data': ['view.xml'],
+    'installable': True,
+    'active': False,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'tests/addons/test_exceptions/models.py'
--- tests/addons/test_exceptions/models.py	1970-01-01 00:00:00 +0000
+++ tests/addons/test_exceptions/models.py	2011-10-01 00:54:24 +0000
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+import openerp
+
+class m(openerp.osv.osv.Model):
+    """ This model exposes a few methods that will raise the different
+        exceptions that must be handled by the server (and its RPC layer)
+        and the clients.
+    """
+    _name = 'test.exceptions.model'
+
+    def generate_except_osv(self, cr, uid, ids, context=None):
+        # title is ignored in the new (6.1) exceptions
+        raise openerp.osv.osv.except_osv('title', 'description')
+
+    def generate_except_orm(self, cr, uid, ids, context=None):
+        # title is ignored in the new (6.1) exceptions
+        raise openerp.osv.orm.except_orm('title', 'description')
+
+    def generate_warning(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.Warning('description')
+
+    def generate_access_denied(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.AccessDenied()
+
+    def generate_access_error(self, cr, uid, ids, context=None):
+        raise openerp.exceptions.AccessError('description')
+
+    def generate_exc_access_denied(self, cr, uid, ids, context=None):
+        raise Exception('AccessDenied')
+
+    def generate_undefined(self, cr, uid, ids, context=None):
+        self.surely_undefined_sumbol
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'tests/addons/test_exceptions/view.xml'
--- tests/addons/test_exceptions/view.xml	1970-01-01 00:00:00 +0000
+++ tests/addons/test_exceptions/view.xml	2011-10-01 00:54:24 +0000
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_test_exceptions_model" model="ir.ui.view">
+            <field name="name">Test exceptions</field>
+            <field name="model">test.exceptions.model</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Test exceptions">
+                      <label string="Each button generates a specific exception on the server. The text on the right is the expected representation of the exception when displayed on the client."/>
+                      <group colspan="8" col="8">
+                          <separator string="" colspan="8"/>
+                          <button name="generate_except_osv" string="except_osv" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_except_orm" string="except_orm" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_warning" string="Warning" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Warning-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_denied" string="AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_access_error" string="AccessError" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access rights error-description"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_exc_access_denied" string="Exc AccessDenied" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Access denied-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <button name="generate_undefined" string="Undefined" type="object" icon="gtk-ok" colspan="1"/>
+                          <label string="Server error-traceback"/>
+                      </group>
+                      <group colspan="8" col="8">
+                          <separator string="" colspan="8"/>
+                          <label colspan="6" width="220"/>
+                          <button special="cancel" string="Cancel" icon="gtk-cancel" colspan="1"/>
+                      </group>
+                </form>
+           </field>
+        </record>
+
+        <record id="action_test_exceptions" model="ir.actions.act_window">
+            <field name="name">Test exceptions</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">test.exceptions.model</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="target">new</field>
+        </record>
+
+        <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests"/>
+
+        <menuitem id="menu_test_exceptions" parent="base.menu_tests" name="Test exceptions"/>
+
+        <menuitem id="menu_test_exceptions_leaf"
+            name="Test exceptions"
+            action="action_test_exceptions"
+            parent="menu_test_exceptions"/>
+    </data>
+</openerp>

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to