Olemis Lang avatar Olemis Lang committed da4c9fd

Fixing problem with backslash in patch name (Hg Windows bug ?)

Comments (0)

Files changed (2)

t5437/t5437-protocol_api_v2-r7194.diff

+RPC Protocol API version 2
+
+diff -r a475cf08e780 trunk/tracrpc/api.py
+--- a/trunk/tracrpc/api.py	Sat Mar 13 19:59:29 2010 -0500
++++ b/trunk/tracrpc/api.py	Sun Mar 14 02:27:58 2010 -0500
+@@ -21,13 +21,34 @@
+     """ RPC Binary type. Currently == xmlrpclib.Binary. """
+     pass
+ 
++#----------------------------------------------------------------
++# RPC Exception classes
++#----------------------------------------------------------------
+ class RPCError(TracError):
+     """ Error class for general RPC-related errors. """
+-    pass
+ 
+ class MethodNotFound(RPCError):
+     """ Error to raise when requested method is not found. """
+-    pass
++
++class _CompositeRpcError(RPCError):
++    def __init__(self, details, title=None, show_traceback=False):
++        if isinstance(details, Exception):
++          self._exc = details
++          message = unicode(details)
++        else :
++          self._exc = None
++          message = details
++        RPCError.__init__(self, message, title, show_traceback)
++    def __unicode__(self):
++        return u"%s details : %s" % (self.__class__.__name__, self.message)
++
++class ProtocolException(_CompositeRpcError):
++    """Protocol could not handle RPC request. Usually this means 
++    that the request has some sort of syntactic error, a library 
++    needed to parse the RPC call is not available, or similar errors."""
++
++class ServiceException(_CompositeRpcError):
++    """The called method threw an exception. Helpful to identify bugs ;o)"""
+ 
+ RPC_TYPES = {int: 'int', bool: 'boolean', str: 'string', float: 'double',
+              datetime: 'dateTime.iso8601', Binary: 'base64',
+@@ -63,8 +84,57 @@
+                    (/login)?/<path_item>. Answer to 'rpc' only if possible.
+         content_type: Starts-with check of 'Content-Type' request header. """
+ 
+-    def rpc_process(req, content_type):
+-        """ Process the RPC request and finalize response. """
++    def parse_rpc_request(req, content_type):
++        """ Parse RPC requests. 
++        
++        req          :        HTTP request object
++        content_type :        Input MIME type
++        
++        Return a dictionary with the following keys set. All the other 
++        values included in this mapping will be ignored by the core 
++        RPC subsystem, will be protocol-specific, and SHOULD NOT be 
++        needed in order to invoke a given method.
++        (TODO: reuse `req` ?)
++        
++        method  (MANDATORY): target method name (e.g. 'ticket.get')
++        params  (OPTIONAL) : a tuple containing input positional arguments
++        headers (OPTIONAL) : if the protocol supports custom headers set 
++                              by the client, then this value SHOULD be a 
++                              dictionary binding `header name` to `value`. 
++                              However, protocol handlers as well as target 
++                              RPC methods *MUST (SHOULD ?) NOT* rely on 
++                              specific values assigned to a particular 
++                              header in order to send a response back 
++                              to the client.
++        mimetype           : request MIME-type. This value will be set 
++                              by core RPC components after calling 
++                              this method so, please, ignore
++        
++        If the request cannot be parsed this method *MUST* raise 
++        an instance of `ProtocolException` wrapping another exception 
++        containing details about the failure.
++        """
++
++    def send_rpc_result(req, rpcreq, result):
++        """Serialize the result of the RPC call and send it back to 
++        the client.
++        
++        rpcreq  : The same object returned by `parse_rpc_request` 
++                  (see above).
++        result  : The value returned by the target RPC method
++        """
++
++    def send_rpc_error(req, rpcreq, e):
++        """Send a fault message back to the caller. Exception type 
++        and message are used for this purpose. This message *SHOULD* 
++        handle `RPCError`, `PermissionError`, and `ResourceNotFound` 
++        and subclasses. This method is called from within an exception 
++        handler.
++        
++        rpcreq  : The same object returned by `parse_rpc_request` 
++                  (see above).
++        e       : exception object describing the failure
++        """
+ 
+ class IXMLRPCHandler(Interface):
+ 
+diff -r a475cf08e780 trunk/tracrpc/json_rpc.py
+--- a/trunk/tracrpc/json_rpc.py	Sat Mar 13 19:59:29 2010 -0500
++++ b/trunk/tracrpc/json_rpc.py	Sun Mar 14 02:27:58 2010 -0500
+@@ -5,8 +5,9 @@
+ (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
+ """
+ 
++import datetime
++from itertools import izip
+ import re
+-import datetime
+ from types import GeneratorType
+ 
+ from trac.core import *
+@@ -16,7 +17,7 @@
+ from trac.util.text import to_unicode
+ 
+ from tracrpc.api import IRPCProtocol, XMLRPCSystem, Binary, \
+-        RPCError, MethodNotFound
++        RPCError, MethodNotFound, ProtocolException
+ from tracrpc.util import exception_to_unicode, empty, prepare_docs
+ 
+ __all__ = ['JsonRpcProtocol']
+@@ -92,6 +93,12 @@
+         obj = json.JSONDecoder.decode(self, obj, *args, **kwargs)
+         return self._normalize(obj)
+ 
++class JsonProtocolException(ProtocolException):
++    """Impossible to handle JSON-RPC request."""
++    def __init__(self, details, code=-32603, title=None, show_traceback=False):
++        ProtocolException.__init__(self, details, title, show_traceback)
++        self.code = code
++
+ class JsonRpcProtocol(Component):
+     r"""
+     Example `POST` request using `curl` with `Content-Type` header
+@@ -126,34 +133,40 @@
+         # Legacy path - provided for backwards compatibility:
+         yield ('jsonrpc', 'application/json')
+ 
+-    def rpc_process(self, req, content_type):
+-        """ Handles JSON-RPC requests """
++    def parse_rpc_request(self, req, content_type):
++        """ Parse JSON-RPC requests"""
+         if not json:
+-            raise RPCError("RPC(json) error. JSON not available.")
++            self.log.debug("RPC(json) call ignored (not available).")
++            # TODO: Specify correponding error code
++            raise JsonProtocolException("Error: JSON-RPC not available.\n")
+         try:
+             data = json.load(req, cls=TracRpcJSONDecoder)
++            if data.get('method') == 'system.multicall':
++                # Prepare for multicall
++                for signature in data.itervalues() :
++                    signature['methodName'] = signature.get('method', '')
++            return data
+         except Exception, e:
+             # Abort with exception - no data can be read
+-            self.log.error("RPC(json) decode error %s" % \
+-                    exception_to_unicode(e, traceback=True))
+-            response = json.dumps(self._json_error(e, -32700),
+-                                    cls=TracRpcJSONEncoder)
+-            self._send_response(req, response + '\n', content_type)
+-            return
+-        self.log.debug("RPC(json) call by '%s': %s" % (req.authname, data))
+-        args = data.get('params') or []
+-        r_id = data.get('id', None)
+-        method = data.get('method', '')
++            self.log.error("RPC(json) decode error %s", 
++                              exception_to_unicode(e, traceback=True))
++            raise JsonProtocolException(e, -32700)
++
++    def send_rpc_result(self, req, rpcreq, result):
++        """Send JSON-RPC response back to the caller."""
++        r_id = rpcreq.get('id')
+         try:
+-            req.perm.require('XML_RPC') # Need at least XML_RPC
+-            if method == 'system.multicall': # Custom multicall
+-                results = []
+-                for mc in args:
+-                    results.append(self._json_call(req, mc.get('method', ''),
+-                        mc.get('params') or [], mc.get('id') or r_id))
+-                response = {'result': results, 'error': None, 'id': r_id}
++            if rpcreq.get('method') == 'system.multicall': 
++                # Custom multicall
++                args = rpcreq.get('params') or []
++                mcresults = [self._json_result(value, sig.get('id') or r_id) \
++                              for sig, value in izip(args, result)]
++                
++                # TODO: Which one is better ?
++                # response = self._json_result(req, mcresults, r_id)
++                response = {'result': mcresults, 'error': None, 'id': r_id}
+             else:
+-                response = self._json_call(req, method, args, r_id)
++                response = self._json_result(result, r_id)
+             try: # JSON encoding
+                 self.log.debug("RPC(json) result: %s" % repr(response))
+                 response = json.dumps(response, cls=TracRpcJSONEncoder)
+@@ -166,7 +179,13 @@
+             response = json.dumps(self._json_error(e, r_id=r_id),
+                             cls=TracRpcJSONEncoder)
+         self.log.debug("RPC(json) encoded result: %s" % response)
+-        self._send_response(req, response + '\n', content_type)
++        self._send_response(req, response + '\n', rpcreq['mimetype'])
++
++    def send_rpc_error(self, req, rpcreq, e):
++        """Send a JSON-RPC fault message back to the caller. """
++        # TODO : Check error codes and RPC exceptions
++        response = json.dumps(self._json_error(e), cls=TracRpcJSONEncoder)
++        self._send_response(req, response + '\n', rpcreq['mimetype'])
+ 
+     # Internal methods
+ 
+@@ -178,14 +197,11 @@
+         req.end_headers()
+         req.write(response)
+ 
+-    def _json_call(self, req, method, args, r_id=None):
+-        """ Call method and create response dictionary. """
+-        try:
+-            result = (XMLRPCSystem(self.env).get_method(method)(req, args))[0]
+-            if isinstance(result, GeneratorType):
+-                result = list(result)
++    def _json_result(self, result, r_id=None):
++        """ Create JSON-RPC response dictionary. """
++        if not isinstance(result, Exception):
+             return {'result': result, 'error': None, 'id': r_id}
+-        except Exception, e:
++        else :
+             return self._json_error(e, r_id=r_id)
+ 
+     def _json_error(self, e, c=None, r_id=None):
+diff -r a475cf08e780 trunk/tracrpc/web_ui.py
+--- a/trunk/tracrpc/web_ui.py	Sat Mar 13 19:59:29 2010 -0500
++++ b/trunk/tracrpc/web_ui.py	Sun Mar 14 02:27:58 2010 -0500
+@@ -6,6 +6,10 @@
+ (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
+ """
+ 
++import sys
++from traceback import format_exc
++from types import GeneratorType
++
+ from pkg_resources import resource_filename
+ 
+ from genshi.builder import tag
+@@ -13,15 +17,19 @@
+ from genshi.template.text import TextTemplate
+ 
+ from trac.core import *
++from trac.perm import PermissionError
++from trac.resource import ResourceNotFound
+ from trac.util.text import to_unicode
+ from trac.util.translation import _
+-from trac.web.api import RequestDone, HTTPUnsupportedMediaType
++from trac.web.api import RequestDone, HTTPUnsupportedMediaType, \
++                          HTTPInternalError
+ from trac.web.main import IRequestHandler
+ from trac.web.chrome import ITemplateProvider, INavigationContributor, \
+                             add_stylesheet, add_script, add_ctxtnav
+ from trac.wiki.formatter import wiki_to_oneliner
+ 
+-from tracrpc.api import XMLRPCSystem, IRPCProtocol
++from tracrpc.api import XMLRPCSystem, IRPCProtocol, ProtocolException, \
++                          RPCError, ServiceException
+ from tracrpc.util import accepts_mimetype
+ 
+ __all__ = ['RPCWeb']
+@@ -59,13 +67,7 @@
+             # Perform the method call
+             self.log.debug("RPC incoming request of content type '%s' " \
+                     "dispatched to %s" % (content_type, repr(protocol)))
+-            try:
+-                protocol.rpc_process(req, content_type)
+-            except Exception, e:
+-                # Unhandled protocol exception
+-                self.log.error("Unhandled protocol error: %s" % to_unicode(e))
+-                req.send_error(None, template='', content_type='text/plain',
+-                        status=500, env=None, data=to_unicode(e))
++            self._rpc_process(req, protocol, content_type)
+         elif accepts_mimetype(req, 'text/html'):
+             return self._dump_docs(req)
+         else:
+@@ -76,6 +78,8 @@
+             req.send_error(None, template='', content_type='text/plain',
+                     status=HTTPUnsupportedMediaType.code, env=None, data=body)
+ 
++    # Internal methods
++
+     def _dump_docs(self, req):
+         # Dump RPC documentation
+         req.perm.require('XML_RPC') # Need at least XML_RPC
+@@ -128,6 +132,53 @@
+             return "Error rendering protocol documentation. " \
+                        "Contact your '''Trac''' administrator for details"
+ 
++    def _rpc_process(self, req, protocol, content_type):
++        """Process incoming RPC request and finalize response."""
++        req.perm.require('XML_RPC') # Need at least XML_RPC
++        proto_id = protocol.rpc_info()[0]
++        rpcreq = {'mimetype': content_type}
++        try :
++            self.log.debug("RPC(%s) call by '%s'", proto_id, req.authname)
++            rpcreq = protocol.parse_rpc_request(req, content_type)
++            rpcreq['mimetype'] = content_type
++            method_name = rpcreq.get('method')
++            if method_name is None :
++                raise ProtocolException('Missing method name')
++            args = rpcreq.get('params') or []
++            self.log.debug("RPC(%s) call by '%s' %s", proto_id, \
++                                              req.authname, method_name)
++            try :
++                result = (XMLRPCSystem(self.env).get_method(method_name)(req, args))[0]
++                if isinstance(result, GeneratorType):
++                    result = list(result)
++            except (RPCError, PermissionError, ResourceNotFound), e:
++                raise
++            except Exception:
++                e, tb = sys.exc_info()[-2:]
++                raise ServiceException(e), None, tb
++            else :
++                protocol.send_rpc_result(req, rpcreq, result)
++        except (RPCError, PermissionError, ResourceNotFound), e:
++            self.log.exception("RPC(%s) Error", proto_id)
++            protocol.send_rpc_error(req, rpcreq, e)
++        except Exception, e :
++            self.log.exception("RPC(%s) Unhandled protocol error", proto_id)
++            self._send_unknown_error(req, rpcreq, e)
++
++    def _send_unknown_error(self, req, rpcreq, e):
++        """Last recourse if protocol cannot handle the RPC request | error"""
++
++        # TODO: This is the previous implementation. Please review.
++#        self.log.error("Unhandled protocol error: %s" % to_unicode(e))
++#        req.send_error(None, template='', content_type='text/plain',
++#                status=500, env=None, data=to_unicode(e))
++
++        stackTrace = format_exc()
++        method_name = rpcreq and rpcreq.get('method') or '(undefined)'
++        body = "Unhandled protocol error calling '%s'\n\n%s" % (method_name, stackTrace)
++        req.send_error(None, template='', content_type='text/plain',
++                            env=None, data=body, status=HTTPInternalError.code)
++
+     # ITemplateProvider methods
+ 
+     def get_htdocs_dirs(self):
+diff -r a475cf08e780 trunk/tracrpc/xml_rpc.py
+--- a/trunk/tracrpc/xml_rpc.py	Sat Mar 13 19:59:29 2010 -0500
++++ b/trunk/tracrpc/xml_rpc.py	Sun Mar 14 02:27:58 2010 -0500
+@@ -19,7 +19,7 @@
+ from trac.util.text import to_unicode
+ 
+ from tracrpc.api import XMLRPCSystem, IRPCProtocol, Binary, \
+-        RPCError, MethodNotFound
++        RPCError, MethodNotFound, ProtocolException, ServiceException
+ from tracrpc.util import empty, prepare_docs
+ 
+ __all__ = ['XmlRpcProtocol']
+@@ -81,41 +81,46 @@
+         yield ('xmlrpc', 'application/xml')
+         yield ('xmlrpc', 'text/xml')
+ 
+-    def rpc_process(self, req, content_type):
+-        """ Handles XML-RPC requests """
++    def parse_rpc_request(self, req, content_type):
++        """ Parse XML-RPC requests."""
+         try:
+             args, method = xmlrpclib.loads(
+                         req.read(int(req.get_header('Content-Length'))))
+         except Exception, e:
+             self.log.debug("RPC(xml) parse error: %s", to_unicode(e))
+-            self._send_response(req,
+-                    xmlrpclib.dumps(
+-                        xmlrpclib.Fault(-32700, to_unicode(e))), content_type)
+-            return
+-        self.log.debug("RPC(xml) call by '%s', method '%s' with args: %s" \
+-                                    % (req.authname, method, repr(args)))
+-        args = self._normalize_xml_input(args)
+-        try:
+-            req.perm.require('XML_RPC') # Need at least XML_RPC
+-            result = XMLRPCSystem(self.env).get_method(method)(req, args)
+-            self.env.log.debug("RPC(xml) '%s' result: %s" % (
+-                                                        method, repr(result)))
+-            result = tuple(self._normalize_xml_output(result))
+-            self._send_response(req,
+-                    xmlrpclib.dumps(result, methodresponse=True), content_type)
+-        except MethodNotFound, e:
+-            self._send_response(req,
+-                    xmlrpclib.dumps(
+-                        xmlrpclib.Fault(-32601, to_unicode(e))), content_type)
+-        except PermissionError, e:
+-            self._send_response(req,
+-                    xmlrpclib.dumps(
+-                        xmlrpclib.Fault(403, to_unicode(e))), content_type)
+-        except ResourceNotFound, e:
+-            self._send_response(req,
+-                    xmlrpclib.dumps(
+-                        xmlrpclib.Fault(404, to_unicode(e))), content_type)
+-        except Exception, e:
++            raise ProtocolException(xmlrpclib.Fault(-32700, to_unicode(e)))
++        else :
++            self.log.debug("RPC(xml) call by '%s', method '%s' with args: %s" \
++                                        % (req.authname, method, repr(args)))
++            args = self._normalize_xml_input(args)
++            return {'method' : method, 'params' : args}
++
++    def send_rpc_result(self, req, rpcreq, result):
++        """Send the result of the XML-RPC call back to the client."""
++        method = rpcreq.get('method')
++        self.log.debug("RPC(xml) '%s' result: %s" % (
++                                                    method, repr(result)))
++        result = tuple(self._normalize_xml_output([result]))
++        self._send_response(req,
++                xmlrpclib.dumps(result, methodresponse=True), rpcreq['mimetype'])
++
++    def send_rpc_error(self, req, rpcreq, e):
++        """Send an XML-RPC fault message back to the caller"""
++        fault = None
++        if isinstance(e, ProtocolException):
++            fault = e._exc
++        elif isinstance(e, ServiceException):
++            e = e._exc
++        elif isinstance(e, MethodNotFound):
++            fault = xmlrpclib.Fault(-32601, to_unicode(e))
++        elif isinstance(e, PermissionError):
++            fault = xmlrpclib.Fault(403, to_unicode(e))
++        elif isinstance(e, ResourceNotFound):
++            fault = xmlrpclib.Fault(404, to_unicode(e))
++
++        if fault is not None :
++            self._send_response(req, xmlrpclib.dumps(fault), rpcreq['mimetype'])
++        else :
+             self.log.error(e)
+             import traceback
+             from tracrpc.util import StringIO

t5437\t5437-protocol_api_v2-r7194.diff

-RPC Protocol API version 2
-
-diff -r a475cf08e780 trunk/tracrpc/api.py
---- a/trunk/tracrpc/api.py	Sat Mar 13 19:59:29 2010 -0500
-+++ b/trunk/tracrpc/api.py	Sun Mar 14 02:27:58 2010 -0500
-@@ -21,13 +21,34 @@
-     """ RPC Binary type. Currently == xmlrpclib.Binary. """
-     pass
- 
-+#----------------------------------------------------------------
-+# RPC Exception classes
-+#----------------------------------------------------------------
- class RPCError(TracError):
-     """ Error class for general RPC-related errors. """
--    pass
- 
- class MethodNotFound(RPCError):
-     """ Error to raise when requested method is not found. """
--    pass
-+
-+class _CompositeRpcError(RPCError):
-+    def __init__(self, details, title=None, show_traceback=False):
-+        if isinstance(details, Exception):
-+          self._exc = details
-+          message = unicode(details)
-+        else :
-+          self._exc = None
-+          message = details
-+        RPCError.__init__(self, message, title, show_traceback)
-+    def __unicode__(self):
-+        return u"%s details : %s" % (self.__class__.__name__, self.message)
-+
-+class ProtocolException(_CompositeRpcError):
-+    """Protocol could not handle RPC request. Usually this means 
-+    that the request has some sort of syntactic error, a library 
-+    needed to parse the RPC call is not available, or similar errors."""
-+
-+class ServiceException(_CompositeRpcError):
-+    """The called method threw an exception. Helpful to identify bugs ;o)"""
- 
- RPC_TYPES = {int: 'int', bool: 'boolean', str: 'string', float: 'double',
-              datetime: 'dateTime.iso8601', Binary: 'base64',
-@@ -63,8 +84,57 @@
-                    (/login)?/<path_item>. Answer to 'rpc' only if possible.
-         content_type: Starts-with check of 'Content-Type' request header. """
- 
--    def rpc_process(req, content_type):
--        """ Process the RPC request and finalize response. """
-+    def parse_rpc_request(req, content_type):
-+        """ Parse RPC requests. 
-+        
-+        req          :        HTTP request object
-+        content_type :        Input MIME type
-+        
-+        Return a dictionary with the following keys set. All the other 
-+        values included in this mapping will be ignored by the core 
-+        RPC subsystem, will be protocol-specific, and SHOULD NOT be 
-+        needed in order to invoke a given method.
-+        (TODO: reuse `req` ?)
-+        
-+        method  (MANDATORY): target method name (e.g. 'ticket.get')
-+        params  (OPTIONAL) : a tuple containing input positional arguments
-+        headers (OPTIONAL) : if the protocol supports custom headers set 
-+                              by the client, then this value SHOULD be a 
-+                              dictionary binding `header name` to `value`. 
-+                              However, protocol handlers as well as target 
-+                              RPC methods *MUST (SHOULD ?) NOT* rely on 
-+                              specific values assigned to a particular 
-+                              header in order to send a response back 
-+                              to the client.
-+        mimetype           : request MIME-type. This value will be set 
-+                              by core RPC components after calling 
-+                              this method so, please, ignore
-+        
-+        If the request cannot be parsed this method *MUST* raise 
-+        an instance of `ProtocolException` wrapping another exception 
-+        containing details about the failure.
-+        """
-+
-+    def send_rpc_result(req, rpcreq, result):
-+        """Serialize the result of the RPC call and send it back to 
-+        the client.
-+        
-+        rpcreq  : The same object returned by `parse_rpc_request` 
-+                  (see above).
-+        result  : The value returned by the target RPC method
-+        """
-+
-+    def send_rpc_error(req, rpcreq, e):
-+        """Send a fault message back to the caller. Exception type 
-+        and message are used for this purpose. This message *SHOULD* 
-+        handle `RPCError`, `PermissionError`, and `ResourceNotFound` 
-+        and subclasses. This method is called from within an exception 
-+        handler.
-+        
-+        rpcreq  : The same object returned by `parse_rpc_request` 
-+                  (see above).
-+        e       : exception object describing the failure
-+        """
- 
- class IXMLRPCHandler(Interface):
- 
-diff -r a475cf08e780 trunk/tracrpc/json_rpc.py
---- a/trunk/tracrpc/json_rpc.py	Sat Mar 13 19:59:29 2010 -0500
-+++ b/trunk/tracrpc/json_rpc.py	Sun Mar 14 02:27:58 2010 -0500
-@@ -5,8 +5,9 @@
- (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
- """
- 
-+import datetime
-+from itertools import izip
- import re
--import datetime
- from types import GeneratorType
- 
- from trac.core import *
-@@ -16,7 +17,7 @@
- from trac.util.text import to_unicode
- 
- from tracrpc.api import IRPCProtocol, XMLRPCSystem, Binary, \
--        RPCError, MethodNotFound
-+        RPCError, MethodNotFound, ProtocolException
- from tracrpc.util import exception_to_unicode, empty, prepare_docs
- 
- __all__ = ['JsonRpcProtocol']
-@@ -92,6 +93,12 @@
-         obj = json.JSONDecoder.decode(self, obj, *args, **kwargs)
-         return self._normalize(obj)
- 
-+class JsonProtocolException(ProtocolException):
-+    """Impossible to handle JSON-RPC request."""
-+    def __init__(self, details, code=-32603, title=None, show_traceback=False):
-+        ProtocolException.__init__(self, details, title, show_traceback)
-+        self.code = code
-+
- class JsonRpcProtocol(Component):
-     r"""
-     Example `POST` request using `curl` with `Content-Type` header
-@@ -126,34 +133,40 @@
-         # Legacy path - provided for backwards compatibility:
-         yield ('jsonrpc', 'application/json')
- 
--    def rpc_process(self, req, content_type):
--        """ Handles JSON-RPC requests """
-+    def parse_rpc_request(self, req, content_type):
-+        """ Parse JSON-RPC requests"""
-         if not json:
--            raise RPCError("RPC(json) error. JSON not available.")
-+            self.log.debug("RPC(json) call ignored (not available).")
-+            # TODO: Specify correponding error code
-+            raise JsonProtocolException("Error: JSON-RPC not available.\n")
-         try:
-             data = json.load(req, cls=TracRpcJSONDecoder)
-+            if data.get('method') == 'system.multicall':
-+                # Prepare for multicall
-+                for signature in data.itervalues() :
-+                    signature['methodName'] = signature.get('method', '')
-+            return data
-         except Exception, e:
-             # Abort with exception - no data can be read
--            self.log.error("RPC(json) decode error %s" % \
--                    exception_to_unicode(e, traceback=True))
--            response = json.dumps(self._json_error(e, -32700),
--                                    cls=TracRpcJSONEncoder)
--            self._send_response(req, response + '\n', content_type)
--            return
--        self.log.debug("RPC(json) call by '%s': %s" % (req.authname, data))
--        args = data.get('params') or []
--        r_id = data.get('id', None)
--        method = data.get('method', '')
-+            self.log.error("RPC(json) decode error %s", 
-+                              exception_to_unicode(e, traceback=True))
-+            raise JsonProtocolException(e, -32700)
-+
-+    def send_rpc_result(self, req, rpcreq, result):
-+        """Send JSON-RPC response back to the caller."""
-+        r_id = rpcreq.get('id')
-         try:
--            req.perm.require('XML_RPC') # Need at least XML_RPC
--            if method == 'system.multicall': # Custom multicall
--                results = []
--                for mc in args:
--                    results.append(self._json_call(req, mc.get('method', ''),
--                        mc.get('params') or [], mc.get('id') or r_id))
--                response = {'result': results, 'error': None, 'id': r_id}
-+            if rpcreq.get('method') == 'system.multicall': 
-+                # Custom multicall
-+                args = rpcreq.get('params') or []
-+                mcresults = [self._json_result(value, sig.get('id') or r_id) \
-+                              for sig, value in izip(args, result)]
-+                
-+                # TODO: Which one is better ?
-+                # response = self._json_result(req, mcresults, r_id)
-+                response = {'result': mcresults, 'error': None, 'id': r_id}
-             else:
--                response = self._json_call(req, method, args, r_id)
-+                response = self._json_result(result, r_id)
-             try: # JSON encoding
-                 self.log.debug("RPC(json) result: %s" % repr(response))
-                 response = json.dumps(response, cls=TracRpcJSONEncoder)
-@@ -166,7 +179,13 @@
-             response = json.dumps(self._json_error(e, r_id=r_id),
-                             cls=TracRpcJSONEncoder)
-         self.log.debug("RPC(json) encoded result: %s" % response)
--        self._send_response(req, response + '\n', content_type)
-+        self._send_response(req, response + '\n', rpcreq['mimetype'])
-+
-+    def send_rpc_error(self, req, rpcreq, e):
-+        """Send a JSON-RPC fault message back to the caller. """
-+        # TODO : Check error codes and RPC exceptions
-+        response = json.dumps(self._json_error(e), cls=TracRpcJSONEncoder)
-+        self._send_response(req, response + '\n', rpcreq['mimetype'])
- 
-     # Internal methods
- 
-@@ -178,14 +197,11 @@
-         req.end_headers()
-         req.write(response)
- 
--    def _json_call(self, req, method, args, r_id=None):
--        """ Call method and create response dictionary. """
--        try:
--            result = (XMLRPCSystem(self.env).get_method(method)(req, args))[0]
--            if isinstance(result, GeneratorType):
--                result = list(result)
-+    def _json_result(self, result, r_id=None):
-+        """ Create JSON-RPC response dictionary. """
-+        if not isinstance(result, Exception):
-             return {'result': result, 'error': None, 'id': r_id}
--        except Exception, e:
-+        else :
-             return self._json_error(e, r_id=r_id)
- 
-     def _json_error(self, e, c=None, r_id=None):
-diff -r a475cf08e780 trunk/tracrpc/web_ui.py
---- a/trunk/tracrpc/web_ui.py	Sat Mar 13 19:59:29 2010 -0500
-+++ b/trunk/tracrpc/web_ui.py	Sun Mar 14 02:27:58 2010 -0500
-@@ -6,6 +6,10 @@
- (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
- """
- 
-+import sys
-+from traceback import format_exc
-+from types import GeneratorType
-+
- from pkg_resources import resource_filename
- 
- from genshi.builder import tag
-@@ -13,15 +17,19 @@
- from genshi.template.text import TextTemplate
- 
- from trac.core import *
-+from trac.perm import PermissionError
-+from trac.resource import ResourceNotFound
- from trac.util.text import to_unicode
- from trac.util.translation import _
--from trac.web.api import RequestDone, HTTPUnsupportedMediaType
-+from trac.web.api import RequestDone, HTTPUnsupportedMediaType, \
-+                          HTTPInternalError
- from trac.web.main import IRequestHandler
- from trac.web.chrome import ITemplateProvider, INavigationContributor, \
-                             add_stylesheet, add_script, add_ctxtnav
- from trac.wiki.formatter import wiki_to_oneliner
- 
--from tracrpc.api import XMLRPCSystem, IRPCProtocol
-+from tracrpc.api import XMLRPCSystem, IRPCProtocol, ProtocolException, \
-+                          RPCError, ServiceException
- from tracrpc.util import accepts_mimetype
- 
- __all__ = ['RPCWeb']
-@@ -59,13 +67,7 @@
-             # Perform the method call
-             self.log.debug("RPC incoming request of content type '%s' " \
-                     "dispatched to %s" % (content_type, repr(protocol)))
--            try:
--                protocol.rpc_process(req, content_type)
--            except Exception, e:
--                # Unhandled protocol exception
--                self.log.error("Unhandled protocol error: %s" % to_unicode(e))
--                req.send_error(None, template='', content_type='text/plain',
--                        status=500, env=None, data=to_unicode(e))
-+            self._rpc_process(req, protocol, content_type)
-         elif accepts_mimetype(req, 'text/html'):
-             return self._dump_docs(req)
-         else:
-@@ -76,6 +78,8 @@
-             req.send_error(None, template='', content_type='text/plain',
-                     status=HTTPUnsupportedMediaType.code, env=None, data=body)
- 
-+    # Internal methods
-+
-     def _dump_docs(self, req):
-         # Dump RPC documentation
-         req.perm.require('XML_RPC') # Need at least XML_RPC
-@@ -128,6 +132,53 @@
-             return "Error rendering protocol documentation. " \
-                        "Contact your '''Trac''' administrator for details"
- 
-+    def _rpc_process(self, req, protocol, content_type):
-+        """Process incoming RPC request and finalize response."""
-+        req.perm.require('XML_RPC') # Need at least XML_RPC
-+        proto_id = protocol.rpc_info()[0]
-+        rpcreq = {'mimetype': content_type}
-+        try :
-+            self.log.debug("RPC(%s) call by '%s'", proto_id, req.authname)
-+            rpcreq = protocol.parse_rpc_request(req, content_type)
-+            rpcreq['mimetype'] = content_type
-+            method_name = rpcreq.get('method')
-+            if method_name is None :
-+                raise ProtocolException('Missing method name')
-+            args = rpcreq.get('params') or []
-+            self.log.debug("RPC(%s) call by '%s' %s", proto_id, \
-+                                              req.authname, method_name)
-+            try :
-+                result = (XMLRPCSystem(self.env).get_method(method_name)(req, args))[0]
-+                if isinstance(result, GeneratorType):
-+                    result = list(result)
-+            except (RPCError, PermissionError, ResourceNotFound), e:
-+                raise
-+            except Exception:
-+                e, tb = sys.exc_info()[-2:]
-+                raise ServiceException(e), None, tb
-+            else :
-+                protocol.send_rpc_result(req, rpcreq, result)
-+        except (RPCError, PermissionError, ResourceNotFound), e:
-+            self.log.exception("RPC(%s) Error", proto_id)
-+            protocol.send_rpc_error(req, rpcreq, e)
-+        except Exception, e :
-+            self.log.exception("RPC(%s) Unhandled protocol error", proto_id)
-+            self._send_unknown_error(req, rpcreq, e)
-+
-+    def _send_unknown_error(self, req, rpcreq, e):
-+        """Last recourse if protocol cannot handle the RPC request | error"""
-+
-+        # TODO: This is the previous implementation. Please review.
-+#        self.log.error("Unhandled protocol error: %s" % to_unicode(e))
-+#        req.send_error(None, template='', content_type='text/plain',
-+#                status=500, env=None, data=to_unicode(e))
-+
-+        stackTrace = format_exc()
-+        method_name = rpcreq and rpcreq.get('method') or '(undefined)'
-+        body = "Unhandled protocol error calling '%s'\n\n%s" % (method_name, stackTrace)
-+        req.send_error(None, template='', content_type='text/plain',
-+                            env=None, data=body, status=HTTPInternalError.code)
-+
-     # ITemplateProvider methods
- 
-     def get_htdocs_dirs(self):
-diff -r a475cf08e780 trunk/tracrpc/xml_rpc.py
---- a/trunk/tracrpc/xml_rpc.py	Sat Mar 13 19:59:29 2010 -0500
-+++ b/trunk/tracrpc/xml_rpc.py	Sun Mar 14 02:27:58 2010 -0500
-@@ -19,7 +19,7 @@
- from trac.util.text import to_unicode
- 
- from tracrpc.api import XMLRPCSystem, IRPCProtocol, Binary, \
--        RPCError, MethodNotFound
-+        RPCError, MethodNotFound, ProtocolException, ServiceException
- from tracrpc.util import empty, prepare_docs
- 
- __all__ = ['XmlRpcProtocol']
-@@ -81,41 +81,46 @@
-         yield ('xmlrpc', 'application/xml')
-         yield ('xmlrpc', 'text/xml')
- 
--    def rpc_process(self, req, content_type):
--        """ Handles XML-RPC requests """
-+    def parse_rpc_request(self, req, content_type):
-+        """ Parse XML-RPC requests."""
-         try:
-             args, method = xmlrpclib.loads(
-                         req.read(int(req.get_header('Content-Length'))))
-         except Exception, e:
-             self.log.debug("RPC(xml) parse error: %s", to_unicode(e))
--            self._send_response(req,
--                    xmlrpclib.dumps(
--                        xmlrpclib.Fault(-32700, to_unicode(e))), content_type)
--            return
--        self.log.debug("RPC(xml) call by '%s', method '%s' with args: %s" \
--                                    % (req.authname, method, repr(args)))
--        args = self._normalize_xml_input(args)
--        try:
--            req.perm.require('XML_RPC') # Need at least XML_RPC
--            result = XMLRPCSystem(self.env).get_method(method)(req, args)
--            self.env.log.debug("RPC(xml) '%s' result: %s" % (
--                                                        method, repr(result)))
--            result = tuple(self._normalize_xml_output(result))
--            self._send_response(req,
--                    xmlrpclib.dumps(result, methodresponse=True), content_type)
--        except MethodNotFound, e:
--            self._send_response(req,
--                    xmlrpclib.dumps(
--                        xmlrpclib.Fault(-32601, to_unicode(e))), content_type)
--        except PermissionError, e:
--            self._send_response(req,
--                    xmlrpclib.dumps(
--                        xmlrpclib.Fault(403, to_unicode(e))), content_type)
--        except ResourceNotFound, e:
--            self._send_response(req,
--                    xmlrpclib.dumps(
--                        xmlrpclib.Fault(404, to_unicode(e))), content_type)
--        except Exception, e:
-+            raise ProtocolException(xmlrpclib.Fault(-32700, to_unicode(e)))
-+        else :
-+            self.log.debug("RPC(xml) call by '%s', method '%s' with args: %s" \
-+                                        % (req.authname, method, repr(args)))
-+            args = self._normalize_xml_input(args)
-+            return {'method' : method, 'params' : args}
-+
-+    def send_rpc_result(self, req, rpcreq, result):
-+        """Send the result of the XML-RPC call back to the client."""
-+        method = rpcreq.get('method')
-+        self.log.debug("RPC(xml) '%s' result: %s" % (
-+                                                    method, repr(result)))
-+        result = tuple(self._normalize_xml_output([result]))
-+        self._send_response(req,
-+                xmlrpclib.dumps(result, methodresponse=True), rpcreq['mimetype'])
-+
-+    def send_rpc_error(self, req, rpcreq, e):
-+        """Send an XML-RPC fault message back to the caller"""
-+        fault = None
-+        if isinstance(e, ProtocolException):
-+            fault = e._exc
-+        elif isinstance(e, ServiceException):
-+            e = e._exc
-+        elif isinstance(e, MethodNotFound):
-+            fault = xmlrpclib.Fault(-32601, to_unicode(e))
-+        elif isinstance(e, PermissionError):
-+            fault = xmlrpclib.Fault(403, to_unicode(e))
-+        elif isinstance(e, ResourceNotFound):
-+            fault = xmlrpclib.Fault(404, to_unicode(e))
-+
-+        if fault is not None :
-+            self._send_response(req, xmlrpclib.dumps(fault), rpcreq['mimetype'])
-+        else :
-             self.log.error(e)
-             import traceback
-             from tracrpc.util import StringIO
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.