Olemis Lang avatar Olemis Lang committed 8479cf0 Merge

Merge: Oops ! Updating patch considering osimons' latest changesets

Comments (0)

Files changed (2)

t5437/t5437-protocol_api-r7194.diff

 new file mode 100644
 --- /dev/null
 +++ b/trunk/tracrpc/json_rpc.py
-@@ -0,0 +1,207 @@
+@@ -0,0 +1,205 @@
 +# -*- coding: utf-8 -*-
 +"""
 +License: BSD
 +    def rpc_process(self, req, content_type):
 +        """ Handles JSON-RPC requests """
 +        if not json:
-+            self.log.debug("RPC(json) call ignored (not available).")
-+            self._send_response(req, "Error: JSON-RPC not available.\n",
-+                                content_type)
++            raise RPCError("RPC(json) error. JSON not available.")
 +        try:
 +            data = json.load(req, cls=TracRpcJSONDecoder)
 +        except Exception, e:
 new file mode 100644
 --- /dev/null
 +++ b/trunk/tracrpc/tests/api.py
-@@ -0,0 +1,78 @@
+@@ -0,0 +1,107 @@
 +# -*- coding: utf-8 -*-
 +"""
 +License: BSD
 +            os.unlink(provider)
 +            rpc_testenv.restart()
 +
++    def test_general_provider_error(self):
++        # Make a new plugin and restart server
++        provider = os.path.join(rpc_testenv.tracdir, 'plugins', 'DummyProvider.py')
++        open(provider, 'w').write(
++            "from trac.core import *\n"
++            "from tracrpc.api import *\n"
++            "class DummyProvider(Component):\n"
++            "    implements(IRPCProtocol)\n"
++            "    def rpc_info(self):\n"
++            "        yield ('TEST-RPC', 'No Docs!')\n"
++            "    def rpc_match(self):\n"
++            "        yield ('rpc', 'application/x-tracrpc-test')\n"
++            "    def rpc_process(self, req, content_type):\n"
++            "        raise RPCError('No good.')")
++        rpc_testenv.restart()
++        # Make the request
++        try:
++            req = urllib2.Request(rpc_testenv.url_anon,
++                        headers={'Content-Type': 'application/x-tracrpc-test'})
++            resp = urllib2.urlopen(req)
++        except urllib2.HTTPError, e:
++            self.assertEquals(500, e.code)
++            self.assertEquals("No good.", e.fp.read())
++            self.assertTrue(e.hdrs['Content-Type'].startswith('text/plain'))
++        finally:
++            # Clean up so that provider don't affect further tests
++            os.unlink(provider)
++            rpc_testenv.restart()
++
 +def suite():
 +    return unittest.makeSuite(ProtocolProviderTestCase)
 +
 diff --git a/trunk/tracrpc/tests/ticket.py b/trunk/tracrpc/tests/ticket.py
 --- a/trunk/tracrpc/tests/ticket.py
 +++ b/trunk/tracrpc/tests/ticket.py
-@@ -32,7 +32,11 @@
+@@ -10,6 +10,7 @@
+ import xmlrpclib
+ import os
+ import shutil
++import time
+ 
+ from tracrpc.tests import rpc_testenv
+ 
+@@ -32,7 +33,11 @@
                    [['action_reassign_reassign_owner', 'admin', []]]],
                    ['accept', 'accept',
                    "The owner will change from (none) to admin. Next status will be 'accepted'.", []]]
          self.admin.ticket.delete(tid)
  
      def test_getAvailableActions_DeleteTicket(self):
-@@ -41,9 +45,11 @@
+@@ -41,9 +46,11 @@
          self.assertEquals(False,
                  'delete' in self.admin.ticket.getAvailableActions(tid))
          env = rpc_testenv.get_trac_environment()
          env.config.set('ticket', 'workflow',
                  'ConfigurableTicketWorkflow,DeleteTicketActionController')
          env.config.save()
-@@ -57,6 +63,9 @@
+@@ -57,6 +64,9 @@
          rpc_testenv.restart()
          self.assertEquals(False,
                  'delete' in self.admin.ticket.getAvailableActions(tid))
  
      def test_FineGrainedSecurity(self):
          self.assertEquals(1, self.admin.ticket.create('abc', '123', {}))
+@@ -101,7 +111,19 @@
+         self.assertEquals(0, self.admin.ticket.delete(1))
+         self.assertEquals(0, self.admin.ticket.delete(2))
+         self.assertEquals(0, self.admin.ticket.delete(3))
+-        
++
++    def test_getRecentChanges(self):
++        tid1 = self.admin.ticket.create("ticket_getRecentChanges", "one", {})
++        time.sleep(1)
++        tid2 = self.admin.ticket.create("ticket_getRecentChanges", "two", {})
++        _id, created, modified, attributes = self.admin.ticket.get(tid2)
++        changes = self.admin.ticket.getRecentChanges(created)
++        try:
++            self.assertEquals(changes, [tid2])
++        finally:
++            self.admin.ticket.delete(tid1)
++            self.admin.ticket.delete(tid2)
++
+ def suite():
+     return unittest.makeSuite(RpcTicketTestCase)
+ 
 diff --git a/trunk/tracrpc/tests/wiki.py b/trunk/tracrpc/tests/wiki.py
 --- a/trunk/tracrpc/tests/wiki.py
 +++ b/trunk/tracrpc/tests/wiki.py
-@@ -9,11 +9,11 @@
+@@ -9,11 +9,12 @@
  
  import xmlrpclib
  import os
 -from StringIO import StringIO
++import time
  
  from trac.util.compat import sorted
  
  
  class RpcWikiTestCase(unittest.TestCase):
      
+@@ -46,6 +47,19 @@
+         # List attachments again
+         self.assertEquals([], self.admin.wiki.listAttachments('TitleIndex'))
+ 
++    def test_getRecentChanges(self):
++        self.admin.wiki.putPage('WikiOne', 'content one', {})
++        time.sleep(1)
++        self.admin.wiki.putPage('WikiTwo', 'content two', {})
++        attrs2 = self.admin.wiki.getPageInfo('WikiTwo')
++        changes = self.admin.wiki.getRecentChanges(attrs2['lastModified'])
++        self.assertEquals(1, len(changes))
++        self.assertEquals('WikiTwo', changes[0]['name'])
++        self.assertEquals('admin', changes[0]['author'])
++        self.assertEquals(1, changes[0]['version'])
++        self.admin.wiki.deletePage('WikiOne')
++        self.admin.wiki.deletePage('WikiTwo')
++
+ def suite():
+     return unittest.makeSuite(RpcWikiTestCase)
+ 
 diff --git a/trunk/tracrpc/tests/xml.py b/trunk/tracrpc/tests/xml_rpc.py
 rename from trunk/tracrpc/tests/xml.py
 rename to trunk/tracrpc/tests/xml_rpc.py
 diff --git a/trunk/tracrpc/ticket.py b/trunk/tracrpc/ticket.py
 --- a/trunk/tracrpc/ticket.py
 +++ b/trunk/tracrpc/ticket.py
-@@ -6,10 +6,15 @@
+@@ -6,25 +6,27 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  
  import trac.ticket.model as model
  import trac.ticket.query as query
  from trac.ticket.api import TicketSystem
-@@ -18,13 +23,10 @@
+ from trac.ticket.notification import TicketNotifyEmail
+ from trac.ticket.web_ui import TicketModule
  from trac.web.chrome import add_warning
- from trac.util.datefmt import to_datetime, to_timestamp, utc
+-from trac.util.datefmt import to_datetime, to_timestamp, utc
++from trac.util.datefmt import to_datetime, utc
  
 -import genshi
 +from tracrpc.api import IXMLRPCHandler, expose_rpc, Binary
-+from tracrpc.util import StringIO
++from tracrpc.util import StringIO, to_utimestamp
  
 -import inspect
 -import xmlrpclib
                 self.putAttachment)
          yield (None, ((bool, int, str),), self.deleteAttachment)
          yield ('TICKET_VIEW', ((list,),), self.getTicketFields)
+@@ -68,7 +70,7 @@
+ 
+     def getRecentChanges(self, req, since):
+         """Returns a list of IDs of tickets that have changed since timestamp."""
+-        since = to_timestamp(since)
++        since = to_utimestamp(since)
+         db = self.env.get_db_cnx()
+         cursor = db.cursor()
+         cursor.execute('SELECT id FROM ticket'
 @@ -243,13 +245,13 @@
          """ returns the content of an attachment. """
          attachment = Attachment(self.env, 'ticket', ticket, filename)
  
  try:
      # Method only available in Trac 0.11.3 or higher
-@@ -44,3 +32,13 @@
+@@ -44,3 +32,21 @@
      from trac.util.text import empty
  except ImportError:
      empty = None
 +def prepare_docs(text, indent=4):
 +    r"""Remove leading whitespace"""
 +    return ''.join(l[indent:] for l in text.splitlines(True))
++
++try:
++    # Micro-second support added to 0.12dev r9210
++    from trac.util.datefmt import to_utimestamp, from_utimestamp
++except ImportError:
++    from trac.util.datefmt import to_timestamp, to_datetime, utc
++    to_utimestamp = to_timestamp
++    from_utimestamp = lambda x: to_datetime(x, utc)
 diff --git a/trunk/tracrpc/web_ui.py b/trunk/tracrpc/web_ui.py
 --- a/trunk/tracrpc/web_ui.py
 +++ b/trunk/tracrpc/web_ui.py
-@@ -6,305 +6,133 @@
+@@ -6,305 +6,142 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  
  from trac.core import *
 -from trac.perm import PermissionError
 -from trac.util.datefmt import utc
--from trac.util.text import to_unicode
+ from trac.util.text import to_unicode
 +from trac.util.translation import _
 +from trac.web.api import RequestDone, HTTPUnsupportedMediaType
  from trac.web.main import IRequestHandler
 +        content_type = req.get_header('Content-Type') or 'text/html'
 +        if protocol:
 +            # Perform the method call
-+            protocol.rpc_process(req, content_type)
-+            raise RequestDone
++            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))
 +        elif accepts_mimetype(req, 'text/html'):
 +            return self._dump_docs(req)
 +        else:
 +            # Attempt at API call gone wrong. Raise a plain-text 415 error
 +            body = "No protocol matching Content-Type '%s' at path '%s'." % (
 +                                                content_type, req.path_info)
++            self.log.error(body)
 +            req.send_error(None, template='', content_type='text/plain',
 +                    status=HTTPUnsupportedMediaType.code, env=None, data=body)
  
 diff --git a/trunk/tracrpc/wiki.py b/trunk/tracrpc/wiki.py
 --- a/trunk/tracrpc/wiki.py
 +++ b/trunk/tracrpc/wiki.py
-@@ -6,22 +6,21 @@
+@@ -6,22 +6,20 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  
 +from trac.attachment import Attachment
  from trac.core import *
 -from trac.resource import Resource
+-from trac.util.datefmt import to_timestamp, to_datetime, utc
 +from trac.resource import Resource, ResourceNotFound
- from trac.util.datefmt import to_timestamp, to_datetime, utc
  from trac.wiki.api import WikiSystem
  from trac.wiki.model import WikiPage
  from trac.wiki.formatter import wiki_to_html
  
 -from tracrpc.api import IXMLRPCHandler, expose_rpc
 +from tracrpc.api import IXMLRPCHandler, expose_rpc, Binary
-+from tracrpc.util import StringIO
++from tracrpc.util import StringIO, to_utimestamp, from_utimestamp
 +
 +__all__ = ['WikiRPC']
  
  class WikiRPC(Component):
      """Superset of the
-@@ -36,7 +35,7 @@
+@@ -36,7 +34,7 @@
          return 'wiki'
  
      def xmlrpc_methods(self):
          yield ('WIKI_VIEW', ((int,),), self.getRPCVersionSupported)
          yield (None, ((str, str), (str, str, int),), self.getPage)
          yield (None, ((str, str, int),), self.getPage, 'getPageVersion')
-@@ -47,10 +46,10 @@
+@@ -47,10 +45,10 @@
          yield (None, ((dict, str, int),), self.getPageInfo, 'getPageInfoVersion')
          yield (None, ((bool, str, str, dict),), self.putPage)
          yield (None, ((list, str),), self.listAttachments)
                                 self.putAttachmentEx)
          yield (None, ((bool, str),(bool, str, int)), self.deletePage)
          yield (None, ((bool, str),), self.deleteAttachment)
+@@ -58,12 +56,12 @@
+         yield ('WIKI_VIEW', ((str, str),), self.wikiToHtml)
+ 
+     def _page_info(self, name, when, author, version, comment):
+-        return dict(name=name, lastModified=to_datetime(when, utc),
++        return dict(name=name, lastModified=when,
+                     author=author, version=int(version), comment=comment)
+ 
+     def getRecentChanges(self, req, since):
+         """ Get list of changed pages since timestamp """
+-        since = to_timestamp(since)
++        since = to_utimestamp(since)
+         wiki_realm = Resource('wiki')
+         db = self.env.get_db_cnx()
+         cursor = db.cursor()
+@@ -73,7 +71,8 @@
+         for name, when, author, version, comment in cursor:
+             if 'WIKI_VIEW' in req.perm(wiki_realm(id=name, version=version)):
+                 result.append(
+-                    self._page_info(name, when, author, version, comment))
++                    self._page_info(name, from_utimestamp(when),
++                                    author, version, comment))
+         return result
+ 
+     def getRPCVersionSupported(self, req):
 @@ -90,7 +89,7 @@
              msg = 'Wiki page "%s" does not exist' % pagename
              if version is not None:

t5437\t5437-protocol_api_v2-r7194.diff

-New API methods and exceptions. JSON protocol refactored and tested ?
+RPC Protocol API version 2
 
-diff -r d083179296d3 trunk/tracrpc/api.py
---- a/trunk/tracrpc/api.py	Fri Mar 12 22:05:22 2010 -0500
-+++ b/trunk/tracrpc/api.py	Sat Mar 13 19:27:36 2010 -0500
+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	Sat Mar 13 23:45:54 2010 -0500
 @@ -21,13 +21,34 @@
      """ RPC Binary type. Currently == xmlrpclib.Binary. """
      pass
  
  class IXMLRPCHandler(Interface):
  
-diff -r d083179296d3 trunk/tracrpc/json_rpc.py
---- a/trunk/tracrpc/json_rpc.py	Fri Mar 12 22:05:22 2010 -0500
-+++ b/trunk/tracrpc/json_rpc.py	Sat Mar 13 19:27:36 2010 -0500
+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	Sat Mar 13 23:45:54 2010 -0500
 @@ -5,8 +5,9 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  class JsonRpcProtocol(Component):
      r"""
      Example `POST` request using `curl` with `Content-Type` header
-@@ -126,36 +133,40 @@
+@@ -126,34 +133,40 @@
          # Legacy path - provided for backwards compatibility:
          yield ('jsonrpc', 'application/json')
  
 +    def parse_rpc_request(self, req, content_type):
 +        """ Parse JSON-RPC requests"""
          if not json:
-             self.log.debug("RPC(json) call ignored (not available).")
--            self._send_response(req, "Error: JSON-RPC not available.\n",
--                                content_type)
+-            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', '')
++                # 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" % \
-+            self.log.error("RPC(json) decode error %s", \
-                     exception_to_unicode(e, traceback=True))
+-                    exception_to_unicode(e, traceback=True))
 -            response = json.dumps(self._json_error(e, -32700),
 -                                    cls=TracRpcJSONEncoder)
 -            self._send_response(req, response + '\n', content_type)
 -        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):
              try: # JSON encoding
                  self.log.debug("RPC(json) result: %s" % repr(response))
                  response = json.dumps(response, cls=TracRpcJSONEncoder)
-@@ -168,7 +179,13 @@
+@@ -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)
  
      # Internal methods
  
-@@ -180,14 +197,11 @@
+@@ -178,14 +197,11 @@
          req.end_headers()
          req.write(response)
  
              return self._json_error(e, r_id=r_id)
  
      def _json_error(self, e, c=None, r_id=None):
-diff -r d083179296d3 trunk/tracrpc/web_ui.py
---- a/trunk/tracrpc/web_ui.py	Fri Mar 12 22:05:22 2010 -0500
-+++ b/trunk/tracrpc/web_ui.py	Sat Mar 13 19:27:36 2010 -0500
+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	Sat Mar 13 23:45:54 2010 -0500
 @@ -6,6 +6,10 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  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.main import IRequestHandler
-@@ -20,7 +26,8 @@
+@@ -21,7 +27,8 @@
                              add_stylesheet, add_script, add_ctxtnav
  from trac.wiki.formatter import wiki_to_oneliner
  
  from tracrpc.util import accepts_mimetype
  
  __all__ = ['RPCWeb']
-@@ -56,7 +63,7 @@
-         content_type = req.get_header('Content-Type') or 'text/html'
-         if protocol:
+@@ -59,13 +66,7 @@
              # Perform the method call
--            protocol.rpc_process(req, content_type)
+             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)
-             raise RequestDone
          elif accepts_mimetype(req, 'text/html'):
              return self._dump_docs(req)
-@@ -67,6 +74,8 @@
+         else:
+@@ -76,6 +77,8 @@
              req.send_error(None, template='', content_type='text/plain',
                      status=HTTPUnsupportedMediaType.code, env=None, data=body)
  
      def _dump_docs(self, req):
          # Dump RPC documentation
          req.perm.require('XML_RPC') # Need at least XML_RPC
-@@ -119,6 +128,47 @@
+@@ -128,6 +131,54 @@
              return "Error rendering protocol documentation. " \
                         "Contact your '''Trac''' administrator for details"
  
 +            self.log.exception("RPC(%s) Error", proto_id)
 +            protocol.send_rpc_error(req, rpcreq, e)
 +        except Exception, e :
-+            self.log.exception("RPC(%s) Unknown protocol error", proto_id)
++            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 = "Can not send response for '%s'\n\n%s" % (method_name, stackTrace)
++        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)
++                            env=None, data=body, 
++                            status=HTTPUnsupportedMediaType.code)
 +
      # ITemplateProvider methods
  
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.