[Checkins] SVN: z3c.jsonrpc/trunk/ Added initial implementation

Roger Ineichen roger at projekt01.ch
Fri Nov 30 10:52:56 EST 2007


Log message for revision 82062:
  Added initial implementation

Changed:
  A   z3c.jsonrpc/trunk/CHANGES.txt
  A   z3c.jsonrpc/trunk/README.txt
  A   z3c.jsonrpc/trunk/bootstrap.py
  A   z3c.jsonrpc/trunk/buildout.cfg
  A   z3c.jsonrpc/trunk/setup.py
  A   z3c.jsonrpc/trunk/src/z3c/__init__.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
  A   z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt

-=-
Added: z3c.jsonrpc/trunk/CHANGES.txt
===================================================================
--- z3c.jsonrpc/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.jsonrpc/trunk/CHANGES.txt	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+Version 0.5.0 (unreleased)
+-------------------------
+
+- Initial Release


Property changes on: z3c.jsonrpc/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/README.txt
===================================================================
--- z3c.jsonrpc/trunk/README.txt	                        (rev 0)
+++ z3c.jsonrpc/trunk/README.txt	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+This package provides an JSON-RPC server implementation for Zope3.


Property changes on: z3c.jsonrpc/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/bootstrap.py
===================================================================
--- z3c.jsonrpc/trunk/bootstrap.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/bootstrap.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75940 2007-05-24 14:45:00Z srichter $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: z3c.jsonrpc/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/buildout.cfg
===================================================================
--- z3c.jsonrpc/trunk/buildout.cfg	                        (rev 0)
+++ z3c.jsonrpc/trunk/buildout.cfg	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,16 @@
+[buildout]
+develop = .
+          externals/z3c.json
+parts = test checker coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.jsonrpc [test]
+
+[checker]
+recipe = lovely.recipe:importchecker
+path = src/z3c/jsonrpc
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage

Added: z3c.jsonrpc/trunk/setup.py
===================================================================
--- z3c.jsonrpc/trunk/setup.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/setup.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,80 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id:$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+    name='z3c.jsonrpc',
+    version='0.5.0dev',
+    author = "Roger Ineichen and the Zope Community",
+    author_email = "zope3-dev at zope.org",
+    description = "JSON RPC server and client implementation for Zope3",
+    long_description=(
+        read('README.txt')
+        + '\n\n' +
+        read('CHANGES.txt')
+        ),
+    license = "ZPL 2.1",
+    keywords = "zope3 z3c json rpc json-rpc server client",
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+    url = 'http://cheeseshop.python.org/pypi/z3c.jsonrpc',
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c'],
+    extras_require = dict(
+        test = [
+            'z3c.coverage',
+            'z3c.layer',
+            'zope.app.testing',
+            'zope.testbrowser',
+            'zope.testing',
+            ],
+        ),
+    install_requires = [
+        'setuptools',
+        'z3c.json',
+        'zope.app.component',
+        'zope.app.publication',
+        'zope.app.twisted',
+        'zope.app.wsgi',
+        'zope.component',
+        'zope.configuration',
+        'zope.i18n',
+        'zope.interface',
+        'zope.location',
+        'zope.publisher',
+        'zope.schema',
+        'zope.security',
+        'zope.securitypolicy',
+        'zope.traversing',
+        ],
+    zip_safe = False,
+)


Property changes on: z3c.jsonrpc/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/__init__.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)


Property changes on: z3c.jsonrpc/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,273 @@
+=======
+JSONRPC
+=======
+
+JSON is javascript object notation. JSON-RPC performs the same service
+as XML-RPC, except the transport is JSON instead of XML.
+
+Many thanks to Jim Washington for the work on zif.jsonserver. This project uses
+many code writen by Jim. I implemented an additional python JSONRPC proxy which 
+can comunicate with the server. This means we can use this library for call 
+JSON from python to python. The JSON-RPC proxy uses similar patterns like the 
+XML-RPC implementation.
+
+There is also a additional xmlhttp and json javascript implementation wich
+offers a JSON-RPC proxy implementation for JavaScript.
+
+This project provides the proposed request type "application/json". The request
+type "application/json-rpc" is supported as long it is not officialy deprecated.
+
+The goal of this project is to provide a JSON-RPC implementation. Simply 
+Browser views wihch handle JSON calls with a BrowserRequest are not supported
+by this package. I'm still not sure if this is good or bad and in which 
+direction I will go with this package.
+
+Some of my goals are right now, but can change in the future if I'll understand
+all the concepts arround JSON, e.g. JSPON, JSONP, CrossSite etc:
+
+- provide a secure way to handle JSON calls from client to server.
+  I hope we can implement JSONRequest some days. CrosSite seems to use a
+  intereting concept
+
+- Simple pythonic implementation
+
+- Use together with JQuery (see http://www.jquery.org).
+
+- No other dependency then JQuery and basic zope packages.
+
+- well tested (this is not the case for JavaScript right now)
+
+
+About JSON
+----------
+
+See www.json.org for more information about JSON.
+
+See http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html for more information
+about the JSON 1.1 specification.
+
+
+What this package can't do
+--------------------------
+
+JSON and this package have different limitations. This package can right now
+not handle the following tasks:
+
+- Handle fileupload
+
+- Handle GET request
+
+Note that the JSONRPCRequest implementation is based on the IHTTPRequest, this
+means that there is no other browser page available if you call them in 
+python, e.g. getMultiAdapter((context, request), name='myViewName'). This is
+explicit done this way. If you like to use content form such browser pages
+in a JSON request/call, you can inherit your skin form IJSONRPCLayer and
+IBrowserRequest and register your views for this custom layer.
+
+
+JSON-RPC server
+---------------
+
+The JSON server looks for content-type "application/json", and handles those 
+requests as JSON-RPC. The official mime-type for JSON is "application/json"
+Old content types ``application/json-rpc`` are supported too.
+
+Let's define a content object:
+
+  >>> import zope.interface
+  >>> class IDemoContent(zope.interface.Interface):
+  ...     """Demo content interface."""
+
+  >>> import persistent
+  >>> class DemoContent(persistent.Persistent):
+  ...     """Demo content."""
+  ...     zope.interface.implements(IDemoContent)
+
+And define a JSONRPC method view:
+
+  >>> from z3c.jsonrpc import publisher
+  >>> class DemoView(publisher.MethodPublisher):
+  ...     """Sample JSON view."""
+  ... 
+  ...     def hello(self):
+  ...         return u"Hello World"
+  ... 
+  ...     def greeting(self, name):
+  ...         return u"Hello %s" % name
+  ... 
+  ...     def kwarguments(self, prefix, foo=None, bar=None):
+  ...         # Note; keyword arguments can be found in request.form
+  ...         return u"%s %s %s" % (prefix, foo, bar)
+  ... 
+  ...     def showId(self):
+  ...         return u"The json id is: %s" % self.request.jsonId
+  ... 
+  ...     def forceValueError(self):
+  ...         raise ValueError('Something was wrong in server method.')
+
+Make them available under the fake package ``jsonsamples``:
+
+  >>> import sys
+  >>> sys.modules['custom'] = type('Module', (), {})()
+  >>> sys.modules['custom'].IDemoContent = IDemoContent
+  >>> sys.modules['custom'].DemoContent = DemoContent
+  >>> sys.modules['custom'].DemoView = DemoView
+
+Let's show how we can register a jsonrpc view:
+
+  >>> from zope.configuration import xmlconfig
+  >>> import z3c.jsonrpc
+  >>> context = xmlconfig.file('meta.zcml', z3c.jsonrpc)
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsonrpc
+  ...       for="custom.IDemoContent"
+  ...       class="custom.DemoView"
+  ...       permission="zope.Public"
+  ...       methods="hello greeting kwarguments showId forceValueError"
+  ...       layer="z3c.jsonrpc.testing.IJSONRPCTestSkin"
+  ...       />
+  ... </configure>
+  ... """, context)
+
+Now we will setup a content object in our site:
+
+  >>> site  = getRootFolder()
+  >>> content = DemoContent()
+  >>> site['content'] = content
+
+Now we can call the method from our JSONRPC view:
+
+  >>> from z3c.jsonrpc import testing
+  >>> request = testing.TestRequest()
+  >>> demoView = DemoView(content, request)
+  >>> demoView.hello()
+  u'Hello World'
+
+But this is not intuitive. Let's see how we can traverse to the method ``hello``
+with the traverser:
+
+  >>> from z3c.jsonrpc.publisher import MethodTraverser
+  >>> methodTraverser = MethodTraverser(demoView, request)
+  >>> methodTraverser.publishTraverse(request, 'hello')()
+  u'Hello World'
+
+Now we try to access the JSON-RPC view method with a test browser. As you can 
+see, there is no view accessible. This is because the JSONRPC view is not a 
+browser view and is not traversable. The error shows that the request factory
+falls back to the browser request factory:
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.addHeader('Accept-Language', 'en')
+  >>> browser.addHeader('Content-Type', 'application/json')
+  >>> siteURL = 'http://localhost/++skin++JSONRPCTestSkin'
+  >>> browser.open(siteURL + '/content/hello')
+  Traceback (most recent call last):
+  ...
+  NotFound: Object: <zope.app.folder.folder.Folder...: u'++skin++JSONRPCTestSkin'
+
+
+Testing
+-------
+
+If you need to test a JSONRPC view you can use the test proxy like shown
+below in the ``JSON-RPC proxy`` section.
+
+
+JSON-RPC proxy
+--------------
+
+The jsonrpc package provides also a JSON-RPC proxy implementation. This 
+implementation is similar to the one known from xmlrpclib except that it can
+handle JSON instead of XML.
+
+Let's try to call our method called ``hello`` we defined before:
+
+  >>> from z3c.jsonrpc.testing import JSONRPCTestProxy
+  >>> proxy = JSONRPCTestProxy(siteURL + '/content')
+  >>> proxy.hello()
+  u'Hello World'
+
+Now let's make a remote procedure call with a argument:
+
+  >>> proxy.greeting(u'Jessy')
+  u'Hello Jessy'
+
+Now let's make a remote procedure call with a kw arguments. Note that this 
+key word arguments are stored in the request.form. But this doesn't change 
+anything because this varaibles are also accessible like form variables e.g. 
+self.request['foo'].Also note that we don't support the **kw signature:
+
+  >>> proxy.kwarguments('Hello', foo=u'FOO', bar=u'BAR')
+  u'Hello FOO BAR'
+
+There is also a ``id`` in the json response. Let's use such a json request id
+in our JSONRPCProxy:
+
+  >>> proxy = JSONRPCTestProxy(siteURL + '/content', jsonId = u'my id')
+  >>> proxy.showId()
+  u'The json id is: my id'
+
+The proxy also knows this id as jsonId:
+
+  >>> proxy.jsonId
+  u'my id'
+
+See what's happen if the server raises a Exception:
+
+  >>> proxy.forceValueError()
+  Traceback (most recent call last):
+  ...
+  ResponseError: Check proxy.error for error message
+
+and the error message is:
+
+  >>> proxy.error
+  u'ValueError: Something was wrong in server method.'
+
+And now we force a ResponseError with a fake JSONReader. But first we
+need to replace our IJSONReader utility:
+
+  >>> from z3c.json.interfaces import IJSONReader
+  >>> sm = site.getSiteManager()
+  >>> fakeJSONReader = testing.ForceResponseErrorJSONReader()
+  >>> sm.registerUtility(fakeJSONReader, IJSONReader)
+
+also setup the site hook:
+
+  >>> from zope.app.component import hooks
+  >>> hooks.setSite(site)
+
+and just call a method this will now raise a ResponseError:
+
+  >>> proxy = JSONRPCTestProxy(siteURL + '/content')
+  >>> proxy.hello()
+  Traceback (most recent call last):
+  ...
+  ResponseError: Unacceptable JSON expression: {"id":"jsonrpc", "method":"hello", "no-params"}
+
+the error message is stored in the proxy too:
+
+  >>> proxy.error
+  u'Unacceptable JSON expression: {"id":"jsonrpc", "method":"hello", "no-params"}'
+
+
+Transport
+~~~~~~~~~
+
+We used the JSONRPCTestProxy here for testing. This JSON-RPC proxy is a wrapper
+for the original JSONRPCProxy and adds handleErrors support and a special 
+Transport layer which uses a testing caller. You can use one of the different 
+Transport layers defined in the z3c.json.transport module in real usecases 
+together with the default JSONRPCProxy implementation.
+
+
+cleanup
+-------
+
+Now we need to clean up the custom module.
+
+  >>> del sys.modules['custom']
\ No newline at end of file


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+  z3c.jsonrpc-*.zcml
+</data-files>

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+
+import zope.component
+from zope.publisher.interfaces import NotFound
+from z3c.jsonrpc.interfaces import IMethodPublisher
+
+
+class JSONRPCTraversablePage(object):
+    """Mixin for JSON-RPC traversable views, pages, forms etc.
+    
+    Make sure if you inherit from this class that no other super class
+    will override the publishTraverse method.
+    """
+
+    def publishTraverse(self, request, name):
+        view = zope.component.queryMultiAdapter((self, request), name=name)
+        if view is None or not IMethodPublisher.providedBy(view):
+            raise NotFound(self, name, request)
+        return view


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,70 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="zope">
+
+
+  <interface
+      interface="z3c.jsonrpc.layer.IJSONRPCLayer"
+      type="zope.publisher.interfaces.browser.IBrowserSkinType"
+      />
+
+
+  <!-- JSON-RPC server -->
+  <publisher
+      name="Z3C JSONRPC"
+      factory=".publication.JSONRPCFactory"
+      methods="POST"
+      mimetypes="application/json application/json-rpc"
+      priority="30"
+      />
+
+  <!-- publisher -->
+  <view
+      for="zope.interface.Interface"
+      type=".interfaces.IJSONRPCRequest"
+      provides=".interfaces.IJSONRPCPublisher"
+      factory="zope.app.publication.traversers.SimpleComponentTraverser"
+      permission="zope.Public"
+      />
+
+  <view
+      for="zope.app.container.interfaces.IItemContainer"
+      type=".interfaces.IJSONRPCRequest"
+      provides=".interfaces.IJSONRPCPublisher"
+      factory="zope.app.container.traversal.ItemTraverser"
+      permission="zope.Public"
+      />
+
+  <view
+      for="zope.app.container.interfaces.IReadContainer"
+      type=".interfaces.IJSONRPCRequest"
+      provides=".interfaces.IJSONRPCPublisher"
+      factory="zope.app.container.traversal.ContainerTraverser"
+      permission="zope.Public"
+      />
+
+  <view
+      for=".interfaces.IMethodPublisher"
+      type=".interfaces.IJSONRPCRequest"
+      provides=".interfaces.IJSONRPCPublisher"
+      factory=".publisher.MethodTraverser"
+      permission="zope.Public"
+      />
+
+
+  <!-- skin namespace -->
+  <view
+      name="skin" type="z3c.jsonrpc.interfaces.IJSONRPCRequest"
+      provides="zope.traversing.interfaces.ITraversable" for="*"
+      factory=".namespace.skin"
+      />
+
+
+  <!-- error handling -->
+  <defaultView
+      for="zope.interface.common.interfaces.IException"
+      type=".layer.IJSONRPCLayer"
+      name="error"
+      />
+
+</configure>


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,72 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:meta="http://namespaces.zope.org/meta"
+           xmlns:browser="http://namespaces.zope.org/browser"
+           xmlns:z3c="http://namespaces.zope.org/z3c"
+           i18n_domain="zope">
+
+  <include package="zope.i18n" file="meta.zcml" />
+  <include package="zope.app.component" file="meta.zcml" />
+  <include package="zope.app.component.browser" file="meta.zcml" />
+  <include package="zope.app.container.browser" file="meta.zcml" /> 
+  <include package="zope.app.form.browser" file="meta.zcml" /> 
+  <include package="zope.app.pagetemplate" file="meta.zcml" />
+  <include package="zope.app.publication" file="meta.zcml" />
+  <include package="zope.app.publisher" file="meta.zcml" />
+  <include package="zope.app.security" file="meta.zcml" />
+  <include package="zope.securitypolicy" file="meta.zcml" />
+
+  <!-- uhh, remove this later -->
+  <browser:menu id="zmi_views" title="Views" />
+  <browser:menu id="zmi_actions" title="Actions" />
+
+  <include package="zope.annotation" />
+  <include package="zope.app.appsetup" /> 
+  <include package="zope.app.component" /> 
+  <include package="zope.app.container" /> 
+  <include package="zope.app.error" />
+  <include package="zope.app.folder" /> 
+  <include package="zope.app.i18n" /> 
+  <include package="zope.app.publication" />
+  <include package="zope.app.publisher.browser" />
+  <include package="zope.app.publisher" />
+  <include package="zope.app.security" />
+  <include package="zope.app.session" /> 
+  <include package="zope.app.twisted" />
+  <include package="zope.app.wsgi" />
+  <include package="zope.component" />
+  <include package="zope.contentprovider" /> 
+  <include package="zope.location" />
+  <include package="zope.publisher" />
+  <include package="zope.traversing" />
+  <include package="zope.traversing.browser" />
+
+  <securityPolicy
+      component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+      />
+
+  <role id="zope.Manager" title="Manager" />
+  <grantAll role="zope.Manager" />
+  <grantAll role="zope.Anonymous" />
+
+  <principal
+      id="zope.manager"
+      title="Manager"
+      login="Manager"
+      password="password"
+      />
+
+  <grant
+      role="zope.Manager"
+      principal="zope.manager"
+      />
+
+  <include package="z3c.json" />
+  <include package="z3c.jsonrpc" />
+
+  <interface
+      interface="z3c.jsonrpc.testing.IJSONRPCTestSkin"
+      type="z3c.jsonrpc.interfaces.IJSONRPCSkinType"
+      name="JSONRPCTestSkin"
+      />
+
+</configure>


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+from zope.interface.interfaces import IInterface
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces.http import IHTTPApplicationRequest
+from zope.publisher.interfaces.http import IHTTPCredentials
+#from zope.publisher.interfaces.xmlrpc import IXMLRPCPublication
+from zope.publisher.interfaces import IPublication
+from zope.app.publication.interfaces import IRequestFactory
+from zope.publisher.interfaces.http import IHTTPRequest
+
+JSON_CHARSETS = ('utf-8','utf-16', 'utf-32')
+PYTHON_KW_MARKER = "pythonKwMaRkEr"
+
+
+class IMethodPublisher(zope.interface.Interface):
+    """Marker interface for an object that wants to publish methods."""
+
+
+class IJSONRPCRequestFactory(IRequestFactory):
+    """Browser request factory"""
+
+
+class IJSONRPCPublisher(IPublishTraverse):
+    """JSON-RPC Publisher
+    like zope.publisher.interfaces.xmlrpc.IXMLRPCPublisher
+    """
+
+
+class IJSONRPCPublication(IPublication):
+    """Publication for JOSN-RPC-based protocol."""
+
+
+class IJSONRPCSkinType(IInterface):
+    """A skin is a set of layers."""
+
+
+class IJSONRPCApplicationRequest(IHTTPApplicationRequest):
+    """HTTP application request."""
+
+
+class IJSONRPCRequest(IJSONRPCApplicationRequest, IHTTPCredentials,
+    IHTTPRequest):
+    """JSON-RPC request."""
+
+    jsonID = zope.interface.Attribute("""JSON-RPC ID for the request""")


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+
+from zope.publisher.interfaces import browser
+from z3c.jsonrpc import interfaces
+
+
+class IJSONRPCLayer(interfaces.IJSONRPCRequest):
+    """Layer for JSONRPC requests.
+
+    IMPORTANT:
+    If you like to use ZPT templates and call adatapers in MethodPublisher 
+    views, you need to make sure that your layer provides the relevant
+    adapters. This means, you will probably use a layer inherited from 
+    IBrowserRequest and IJSONRPCRequest in your skin.
+
+    Or you can register your icons etc. for the IJSONRequest layer etc. if you
+    need to return content produced by ZPT tempaltes. Also note that icons
+    or other resources are only available in IBrowserRequest or in your custom
+    layer.
+    """


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,14 @@
+<configure
+    xmlns:meta="http://namespaces.zope.org/meta"
+    xmlns="http://namespaces.zope.org/zope">
+    
+  <meta:directive
+       namespace="http://namespaces.zope.org/z3c"
+       name="jsonrpc"
+       schema=".zcml.IJSONRPCDirective"
+       handler=".zcml.jsonrpc"
+       />
+       
+    <meta:provides feature="z3c.jsonrpc" />
+
+</configure>


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,24 @@
+# Make a package.
+
+import zope.component
+import zope.traversing.namespace
+from zope.component.interfaces import ComponentLookupError
+from zope.publisher.http import applySkin
+from zope.traversing.interfaces import TraversalError
+
+from z3c.jsonrpc import interfaces
+
+
+class skin(zope.traversing.namespace.skin):
+    """JSONRPC skin type interface."""
+
+    skin_type = interfaces.IJSONRPCSkinType
+
+    def traverse(self, name, ignored):
+        self.request.shiftNameToApplication()
+        try:
+            skin = zope.component.getUtility(self.skin_type, name)
+        except ComponentLookupError:
+            raise TraversalError("++skin++%s" % name)
+        applySkin(self.request, skin, self.skin_type)
+        return self.context


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+
+from zope.app.publication.http import BaseHTTPPublication
+from zope.app.publication.interfaces import IRequestPublicationFactory
+
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.publisher import JSONRPCRequest
+
+
+class JSONRPCPublication(BaseHTTPPublication):
+    """JSON RPC Publication."""
+
+    zope.interface.implements(interfaces.IJSONRPCPublication)
+
+
+
+class JSONRPCFactory(object):
+    zope.interface.implements(IRequestPublicationFactory)
+    
+    def canHandle(self, environment):
+        return True
+        
+    def __call__(self):
+        request_class = zope.component.queryUtility(
+            interfaces.IJSONRPCRequestFactory, default=JSONRPCRequest)
+        return request_class, JSONRPCPublication


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,268 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import traceback
+import logging
+
+import zope.interface
+import zope.component
+from zope.location.location import Location
+from zope.i18n.interfaces import IUserPreferredCharsets
+from zope.publisher.http import HTTPRequest
+from zope.publisher.http import HTTPResponse
+from zope.publisher.http import getCharsetUsingRequest
+from zope.security.proxy import isinstance
+
+from z3c.json.interfaces import IJSONReader
+from z3c.json.interfaces import IJSONWriter
+from z3c.json.converter import premarshal
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.interfaces import JSON_CHARSETS
+from z3c.jsonrpc.interfaces import PYTHON_KW_MARKER
+
+DEBUG = logging.DEBUG
+logger = logging.getLogger()
+
+
+def intsort(item):
+    return int(item[0])
+
+
+class MethodPublisher(Location):
+    """Base class for JSON-RPC views that publish methods
+       like zope.app.publisher.xmlrpc.MethodPublisher
+    """
+    zope.interface.implements(interfaces.IMethodPublisher)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def __getParent(self):
+        return hasattr(self, '_parent') and self._parent or self.context
+
+    def __setParent(self, parent):
+        self._parent = parent
+
+    __parent__ = property(__getParent, __setParent)
+
+
+class MethodTraverser(object):
+    zope.interface.implements(interfaces.IJSONRPCPublisher)
+
+    __used_for__ = interfaces.IMethodPublisher
+
+    def __init__(self, context, request):
+        self.context = context
+
+    def publishTraverse(self, request, name):
+        return getattr(self.context, name)
+
+
+class JSONRPCRequest(HTTPRequest):
+    """JSON-RPC request implementation based on IHTTPRequest."""
+
+    _jsonId = 'jsonrpc'
+    jsonId = None
+
+    zope.interface.implements(interfaces.IJSONRPCRequest,
+        interfaces.IJSONRPCApplicationRequest)
+
+    def __init__(self, body_instream, environ, response=None):
+        self.form = {}
+        self._args = ()
+        self.charsets = None
+        super(JSONRPCRequest, self).__init__(body_instream, environ, response)
+
+    def _createResponse(self):
+        """return a response"""
+        return JSONRPCResponse()
+
+    def _decode(self, text):
+        """Try to decode the text using one of the available charsets."""
+        if self.charsets is None:
+            envadapter = IUserPreferredCharsets(self)
+            self.charsets = envadapter.getPreferredCharsets() or ['utf-8']
+        for charset in self.charsets:
+            try:
+                text = unicode(text, charset)
+                break
+            except UnicodeError:
+                pass
+        return text
+
+    def processInputs(self):
+        """take the converted request and make useful args of it."""
+        json = zope.component.getUtility(IJSONReader)
+        stream = self._body_instream
+        input = []
+        incoming = stream.read(1000)
+        while incoming:
+            input.append(incoming)
+            incoming = stream.read(1000)
+        input = ''.join(input)
+        # ensure unicode
+        if not isinstance(input, unicode):
+            input = self._decode(input)
+        data = json.read(input)
+        if self.jsonId is None:
+            self.jsonId = data.get('id', self._jsonId)
+        params = data['params']
+
+        if isinstance(params, list):
+            # json-rpc 1.0
+            args = params
+            # now, look for keyword parameters, the old way
+            kwargs = None
+            notPositional = []
+            for k in args:
+                if isinstance(k, dict):
+                    if k.has_key(PYTHON_KW_MARKER):
+                        if isinstance(k[PYTHON_KW_MARKER], dict):
+                            j = k[PYTHON_KW_MARKER]
+                            kwargs = j
+                            notPositional.append(k)
+            if notPositional:
+                for k in notPositional:
+                    args.remove(k)
+            if kwargs:
+                for m in kwargs.keys():
+                    self.form[str(m)] = kwargs[m]
+        elif isinstance(params, dict):
+            # Note: the JSONRPCProxy uses allways a dict for params. This means
+            # we only use this part for extract the data.
+            # json-rpc 1.1 (to be proposed)
+            # get the numeric params for positional params
+            temp_positional = []
+            for key in params:
+                if str(key).isdigit():
+                    temp_positional.append((key, params[key]))
+            temp_positional.sort(key=intsort)
+            args = []
+            # make args from positional args and remove them from params
+            for item in temp_positional:
+                args.append(item[1])
+                del params[item[0]]
+            # drop remaining named params into request.form
+            for named_param in params:
+                # named_param is unicode; python needs string for param names
+                self.form[str(named_param)] = params[named_param]
+        else:
+            raise TypeError, 'Unsupported type for JSON-RPC "params" (%s)' \
+                % type(params)
+        self._args = tuple(args)
+        # make environment, cookies, etc., available to request.get()
+        super(JSONRPCRequest,self).processInputs()
+        self._environ['JSONRPC_MODE'] = True
+
+        # split here on '.' for get path suffix steps
+        functionstr = data['method']
+        function = functionstr.split('.')
+        if function:
+            # translate '.' to '/' in function to represent object traversal.
+            self.setPathSuffix(function)
+
+    def traverse(self, object):
+        return super(JSONRPCRequest, self).traverse(object)
+
+    def keys(self):
+        """See Interface.Common.Mapping.IEnumerableMapping."""
+        d = {}
+        d.update(self._environ)
+        d.update(self._cookies)
+        d.update(self.form)
+        return d.keys()
+
+    def get(self, key, default=None):
+        """See Interface.Common.Mapping.IReadMapping."""
+        marker = object()
+        result = self.form.get(key, marker)
+        if result is not marker:
+            return result
+        return super(JSONRPCRequest, self).get(key, default)
+
+    def __getitem__(self,key):
+        return self.get(key)
+
+
+class JSONRPCResponse(HTTPResponse):
+    """JSON-RPC Response"""
+
+
+    def setResult(self, result):
+        """The result dict contains the following key value pairs
+
+        id -- json request id
+        result -- result or null on error
+        error -- error or null if result is Ok
+
+        """
+        id = self._request.jsonId
+        if id is not None:
+            result = premarshal(result)
+            wrapper = {'id': id}
+            wrapper['result'] = result
+            wrapper['error'] = None
+            json = zope.component.getUtility(IJSONWriter)
+            encoding = getCharsetUsingRequest(self._request)
+            result = json.write(wrapper)
+            body = self._prepareResult(result)
+            super(JSONRPCResponse,self).setResult(body)
+            logger.log(DEBUG, "%s" % result)
+        else:
+            self.setStatus(204)
+            super(JSONRPCResponse,self).setResult('')
+
+    def _prepareResult(self, result):
+        # we've asked json to return unicode; result should be unicode
+        encoding = getCharsetUsingRequest(self._request) or 'utf-8'
+        enc = encoding.lower()
+        if not enc in JSON_CHARSETS:
+            encoding = 'utf-8'
+        # encode outgoing boundary.
+        if isinstance(result, unicode):
+            body = result.encode(encoding)
+            charset = encoding
+        else:
+            # something's wrong. JSON did not return unicode.
+            raise TypeError, "JSON did not return unicode (%s)" % type(result)
+
+        # set content type
+        self.setHeader('content-type', "application/x-javascript;charset=%s" \
+            % charset)
+        return body
+
+    def handleException(self, exc_info):
+        t, value = exc_info[:2]
+        exc_data = []
+        for file, lineno, function, text in traceback.extract_tb(exc_info[2]):
+            exc_data.append("%s %s %s %s %s" % (file, "line",
+                lineno, "in", function))
+            exc_data.append("%s %s" % ( "=>", repr(text)))
+            exc_data.append( "** %s: %s" % exc_info[:2])
+        logger.log(logging.ERROR, "\n".join(exc_data))
+        s = '%s: %s' % (getattr(t, '__name__', t), value)
+        wrapper = {'id': self._request.jsonId}
+        wrapper['result'] = None
+        wrapper['error'] = s
+        json = zope.component.getUtility(IJSONWriter)
+        result = json.write(wrapper)
+        body = self._prepareResult(result)
+        super(JSONRPCResponse, self).setResult(body)
+        logger.log(DEBUG, "Exception: %s" % result)
+        self.setStatus(200)


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,301 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import StringIO
+
+import persistent
+import zope.interface
+from zope.testing import doctest
+from zope.app.testing import functional
+from zope.app.testing.functional import HTTPCaller
+
+from z3c.layer.minimal import IMinimalBrowserLayer
+from z3c.json.interfaces import IJSONReader
+from z3c.json.converter import JSONReader
+from z3c.json.exceptions import ProtocolError
+from z3c.json.proxy import JSONRPCProxy
+from z3c.json.transport import Transport
+from z3c.jsonrpc import layer
+from z3c.jsonrpc.publisher import JSONRPCRequest
+from z3c.jsonrpc.publisher import MethodPublisher
+from z3c.jsonrpc.publication import JSONRPCPublication
+
+
+###############################################################################
+#
+# Test proxy
+#
+###############################################################################
+
+class JSONRPCTestTransport(Transport):
+    """Test transport that delegates to zope.app.testing.functional.HTTPCaller.
+
+    It can be used like a normal transport, including support for basic 
+    authentication.
+    """
+
+    verbose = False
+    handleErrors = True
+
+    def request(self, host, handler, request_body, verbose=0):
+        request = "POST %s HTTP/1.0\n" % (handler,)
+        request += "Content-Length: %i\n" % len(request_body)
+        request += "Content-Type: application/json\n"
+
+        host, extra_headers, x509 = self.get_host_info(host)
+        if extra_headers:
+            request += "Authorization: %s\n" % (
+                dict(extra_headers)["Authorization"],)
+
+        request += "\n" + request_body
+        caller = HTTPCaller()
+        response = caller(request, handle_errors=self.handleErrors)
+
+        errcode = response.getStatus()
+        errmsg = response.getStatusString()
+        # This is not the same way that the normal transport deals with the
+        # headers.
+        headers = response.getHeaders()
+
+        if errcode != 200:
+            raise ProtocolError(host + handler, errcode, errmsg, headers)
+
+        return self._parse_response(
+            StringIO.StringIO(response.getBody()), sock=None)
+
+
+def JSONRPCTestProxy(uri, transport=None, encoding=None,
+                verbose=None, jsonId=None, handleErrors=True):
+    """A factory that creates a server proxy using the ZopeJSONRPCTestTransport 
+    by default."""
+    if verbose is None:
+        verbose = 0
+    if transport is None:
+        transport = JSONRPCTestTransport()
+    if isinstance(transport, JSONRPCTestTransport):
+        transport.handleErrors = handleErrors
+    return JSONRPCProxy(uri, transport, encoding, verbose, jsonId)
+
+
+###############################################################################
+#
+# Test layer
+#
+###############################################################################
+
+functional.defineLayer("JSONRPCTestingLayer", "ftesting.zcml")
+ 
+
+###############################################################################
+#
+# Test component
+#
+###############################################################################
+
+class IJSONRPCTestLayer(layer.IJSONRPCLayer):
+    """JSON-RPC test layer interface used for zcml testing."""
+
+
+class IJSONRPCTestSkin(IJSONRPCTestLayer):
+    """The IJSONRPCTestSkin testing skin based on IJSONRPCLayer."""
+
+
+class IA(zope.interface.Interface):
+    """First content stub interface."""
+
+
+class A(persistent.Persistent):
+    """First content stub."""
+
+    zope.interface.implements(IA)
+
+
+class IB(zope.interface.Interface):
+    """First content stub interface."""
+
+
+class B(persistent.Persistent):
+    """First content stub."""
+
+    zope.interface.implements(IB)
+
+
+class MethodsA(MethodPublisher):
+    """Method publisher test class."""
+
+    def hello(self):
+        return "Hello A World"
+
+
+class MethodsB(MethodPublisher):
+    """Method publisher test class."""
+
+    def hello(self):
+        return "Hello B World"
+
+
+class ForceResponseErrorJSONReader(JSONReader):
+    """JSONReader wich only raise a ResponseError because of bad a bad string.
+    """
+    zope.interface.implements(IJSONReader)
+
+    def read(self, aString, encoding=None):
+        aBadString = u'{"id":"jsonrpc", "method":"hello", "no-params"}'
+        return super(ForceResponseErrorJSONReader, self).read(aBadString, encoding)
+
+
+class TestRequest(JSONRPCRequest):
+    """modeled after zope.publisher.xmlrpc.TestRequest"""
+    def __init__(self, body_instream=None, environ=None,
+                 response=None, **kw):
+
+        _testEnv =  {
+            'SERVER_URL':         'http://127.0.0.1',
+            'HTTP_HOST':          '127.0.0.1',
+            'CONTENT_LENGTH':     '0',
+            'GATEWAY_INTERFACE':  'TestFooInterface/1.0',
+            }
+
+        if environ:
+            _testEnv.update(environ)
+        if kw:
+            _testEnv.update(kw)
+        if body_instream is None:
+            body_instream = StringIO.StringIO('')
+
+        super(TestRequest, self).__init__(
+            body_instream, _testEnv, response)
+
+
+class HTTPCaller(functional.HTTPCaller):
+    """HTTPCaller for JSON."""
+
+    def chooseRequestClass(self, method, path, environment):
+        """Choose and return a request class and a publication class"""
+        request_cls, publication_cls = \
+            super(HTTPCaller, self).chooseRequestClass(method, path,
+                environment)
+        
+        content_type = environment.get('CONTENT_TYPE', '')
+        is_json = content_type.startswith('application/json')
+    
+        if method in ('GET', 'POST', 'HEAD'):
+            if (method == 'POST' and is_json):
+                request_cls = JSONRPCRequest
+                publication_cls = JSONRPCPublication
+    
+        return request_cls, publication_cls
+
+
+###############################################################################
+#
+# Doctest setup
+#
+###############################################################################
+
+def FunctionalDocFileSuite(*paths, **kw):
+    globs = kw.setdefault('globs', {})
+    globs['http'] = HTTPCaller()
+    globs['getRootFolder'] = functional.getRootFolder
+    globs['sync'] = functional.sync
+
+    kw['package'] = doctest._normalize_module(kw.get('package'))
+
+    kwsetUp = kw.get('setUp')
+    def setUp(test):
+        functional.FunctionalTestSetup().setUp()
+
+        if kwsetUp is not None:
+            kwsetUp(test)
+    kw['setUp'] = setUp
+
+    kwtearDown = kw.get('tearDown')
+    def tearDown(test):
+        if kwtearDown is not None:
+            kwtearDown(test)
+        functional.FunctionalTestSetup().tearDown()
+    kw['tearDown'] = tearDown
+
+    if 'optionflags' not in kw:
+        kw['optionflags'] = (doctest.ELLIPSIS
+                             | doctest.REPORT_NDIFF
+                             | doctest.NORMALIZE_WHITESPACE)
+
+    suite = functional.FunctionalDocFileSuite(*paths, **kw)
+    suite.layer = JSONRPCTestingLayer
+    return suite
+
+
+###############################################################################
+#
+# Test helper, make us independent from zope.app.testing
+#
+###############################################################################
+
+# Setup of test text files as modules
+import sys
+
+# Evil hack to make pickling work with classes defined in doc tests
+class NoCopyDict(dict):
+    def copy(self):
+        return self
+
+class FakeModule:
+    """A fake module."""
+    
+    def __init__(self, dict):
+        self.__dict = dict
+
+    def __getattr__(self, name):
+        try:
+            return self.__dict[name]
+        except KeyError:
+            raise AttributeError(name)
+
+
+def setUpTestAsModule(test, name=None):
+    if name is None:
+        if test.globs.haskey('__name__'):
+            name = test.globs['__name__']
+        else:
+            name = test.globs.name
+
+    test.globs['__name__'] = name 
+    test.globs = NoCopyDict(test.globs)
+    sys.modules[name] = FakeModule(test.globs)
+
+
+def tearDownTestAsModule(test):
+    del sys.modules[test.globs['__name__']]
+    test.globs.clear()
+
+
+###############################################################################
+#
+# Unittest setup
+#
+###############################################################################
+
+
+
+def setUp(test):
+    setUpTestAsModule(test, name='README')
+
+
+def tearDown(test):
+    tearDownTestAsModule(test)


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>z3c.jsonrpc.demo</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta http-equiv="cache-control" content="no-cache" />
+<script type="text/javascript" src="xmlhttp.js"
+        tal:attributes="src context/++resource++xmlhttp.js"> </script>
+<script type="text/javascript" src="jsonrpc.js"
+        tal:attributes="src context/++resource++jsonrpc.js"> </script>
+<script type="text/javascript"
+        tal:define="contexturl context/@@absolute_url; 
+                    viewurl request/URL"
+        tal:content="string:
+	var contextURL = '${contexturl}';
+	var viewURL = '${viewurl}';"> 
+</script>
+<script type="text/javascript" language="javascript">
+// helloworld
+function helloworld() {
+    var jsonProxy = new JSONRPC(contextURL);
+    jsonProxy.addMethod('helloworld');
+    response = jsonProxy.helloworld();
+	alert(response);
+}
+// hello
+function hello(name) {
+	var name = document.getElementById('helloInput').value;
+    var jsonProxy = new JSONRPC(contextURL);
+    jsonProxy.addMethod('hello');
+    response = jsonProxy.hello(name);
+	document.getElementById('helloOutput').value = response;
+}
+
+// kwargs
+function kwargs(name) {
+    var jsonProxy = new JSONRPC(contextURL);
+    jsonProxy.addMethod('kwargs');
+	postitional = 'postional value'
+	// firstMNamedMissing (not userd)
+    // secondNamedMissing (not userd)
+	var kws = new Object();
+	kws['thirdNamed'] = 'third value';
+	kws['lastNamed'] = 'last value';
+    response = jsonProxy.kwargs(postitional, kws);
+	document.getElementById('postional').innerHTML = response.postional;
+	document.getElementById('first').innerHTML = response.first;
+	document.getElementById('second').innerHTML = response.second;
+	document.getElementById('third').innerHTML = response.third;
+	document.getElementById('last').lastNamed = response.last;
+}
+
+// call intro
+alertIntro();
+</script>
+<style type="text/css">
+body {
+    font: 11px Verdana, Helvetica, Arial, sans-serif;
+}
+.red {
+    color: red;
+}
+</style>
+</head>
+<body>
+  <h2>JSONRPC demo page</h2>
+  <h3>1. Sample</h3>
+  <h4>Simple *Hello World* sample</h4>
+  <pre>
+function helloworld() {
+    var jsonProxy = new JSONRPC(contextURL);
+    jsonProxy.addMethod('helloworld');
+    response = jsonProxy.helloworld();
+	alert(response);
+}
+  </pre>
+  <div>
+  <input type="button" onClick="helloworld(); return false;" value="Hello world" />
+  </div>
+  <h3>2. Sample</h3>
+  <h4>This sample uses one positional argument</h4>
+  <pre>
+function hello(name) {
+	var name = document.getElementById('helloInput').value;
+    var jsonProxy = new JSONRPC(contextURL);
+    jsonProxy.addMethod('hello');
+    response = jsonProxy.hello(name);
+	document.getElementById('helloOutput').value = response;
+}
+  </pre>
+  <div>
+  <input type="text" onBlur="hello(); return false;" id="helloInput" value="" />
+  <input type="button" onClick="hello(); return false;" value="Add your name to the left field and click here" />
+  </div>
+  <div><input type="text" id="helloOutput" value="" /></div>
+  <h3>3. Sample</h3>
+  <h4>JSONRPC call with postional and named arguments.</h4>
+  <h3><span class="red">(NOTE, this fails in JSONRPC version 1.0)</span></h3>
+  <pre>
+
+  </pre>
+  <div>
+  <input type="button" onClick="kwargs(); return false;" value="Hello world" />
+  </div>
+  <div>positional: <span class="red" id="postional"></span></div>
+  <div>firstNamedMissing:<span class="red" id="first"></span></div>
+  <div>secondNamedMissing: <span class="red" id="second"></span></div>
+  <div>thirdNamed: <span class="red" id="third"></span></div>
+  <div>lastNamed: <span class="red" id="last"></span></div>
+</body>
+</html>


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+from zope.testing import doctest
+from z3c.jsonrpc import testing
+
+
+def test_suite():
+    return unittest.TestSuite((
+        testing.FunctionalDocFileSuite('../README.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite('../zcml.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        ))
+
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+
+import zope.interface
+import zope.component
+from zope.component.testing import PlacelessSetup
+
+from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
+
+from z3c.jsonrpc.publication import JSONRPCFactory
+from z3c.jsonrpc.publication import JSONRPCPublication
+from z3c.jsonrpc import interfaces
+
+
+class DummyRequestFactory(object):
+    def __call__(self, input_stream, env):
+        self.input_stream = input_stream
+        self.env = env
+        return self
+
+    def setPublication(self, pub):
+        self.pub = pub
+
+
+class Test(PlacelessSetup, unittest.TestCase):
+
+    def setUp(self):
+        super(Test, self).setUp()
+        self.__factory = HTTPPublicationRequestFactory(None)
+        self.__env =  {
+            'SERVER_URL':         'http://127.0.0.1',
+            'HTTP_HOST':          '127.0.0.1',
+            'CONTENT_LENGTH':     '0',
+            'GATEWAY_INTERFACE':  'TestFooInterface/1.0'
+            }
+
+    def test_jsonrpcfactory(self):
+        jsonrpcrequestfactory = DummyRequestFactory()
+        zope.interface.directlyProvides(
+            jsonrpcrequestfactory, interfaces.IJSONRPCRequestFactory)
+        zope.component.provideUtility(jsonrpcrequestfactory)
+        env = self.__env
+        factory = JSONRPCFactory()
+        self.assertEqual(factory.canHandle(env), True)
+        request, publication = factory()
+        self.assertEqual(isinstance(request, DummyRequestFactory), True)
+        self.assertEqual(publication, JSONRPCPublication)
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(Test),
+        ))
+
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,195 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+from StringIO import StringIO
+
+import zope.component
+from zope.publisher.base import DefaultPublication
+from zope.publisher.http import HTTPCharsets
+
+from z3c.json.interfaces import IJSONReader
+from z3c.json.converter import JSONReader
+from z3c.jsonrpc.publisher import JSONRPCRequest
+
+
+class Publication(DefaultPublication):
+
+    require_docstrings = 0
+
+    def getDefaultTraversal(self, request, ob):
+        if hasattr(ob, 'browserDefault'):
+            return ob.browserDefault(request)
+        return ob, ()
+
+
+class TestJSONRPCRequest(JSONRPCRequest, HTTPCharsets):
+    """Make sure that our request also implements IHTTPCharsets, so that we do
+    not need to register any adapters."""
+
+    def __init__(self, *args, **kw):
+        self.request = self
+        JSONRPCRequest.__init__(self, *args, **kw)
+
+
+class TestCall:
+    def __init__(self):
+        self.body = '{"id":"httpReq","method":"action","params":[1]}'
+        self.headers = []
+
+
+jsonrpc_call = TestCall()
+
+
+class ParamTestCall:
+    def __init__(self):
+        self.body = '{"id":"httpReq","method":"keyworded","params":[1,{"pythonKwMaRkEr":{"kw1":"aaa"}}]}'
+        self.headers = []
+
+
+class Param1_1SpecTestCall1:
+    def __init__(self):
+        self.body = '{"id":"httpReq","method":"keyworded","params":{"1":1,"kw1":"aaa"}}'
+        self.headers = []
+
+
+class Param1_1SpecTestCall2:
+    def __init__(self):
+        self.body = '{"id":"httpReq","method":"action1_1","params":{"1":1,"kw1":"aaa"}}'
+        self.headers = []
+
+
+class JSONRPCTests(unittest.TestCase):
+    """The only thing different to HTTP is the input processing; so there
+       is no need to redo all the HTTP tests again.
+    """
+
+    _testEnv =  {
+        'PATH_INFO':          '/folder/item2/view/',
+        'QUERY_STRING':       '',
+        'SERVER_URL':         'http://foobar.com',
+        'HTTP_HOST':          'foobar.com',
+        'CONTENT_LENGTH':     '0',
+        'REQUEST_METHOD':     'POST',
+        'HTTP_AUTHORIZATION': 'Should be in accessible',
+        'GATEWAY_INTERFACE':  'TestFooInterface/1.0',
+        'HTTP_OFF_THE_WALL':  "Spam 'n eggs",
+        'HTTP_ACCEPT_CHARSET': 'ISO-8859-1, UTF-8;q=0.66, UTF-16;q=0.33',
+    }
+
+    def setUp(self):
+        super(JSONRPCTests, self).setUp()
+
+        class AppRoot(object):
+            pass
+
+        class Folder(object):
+            pass
+
+        class Item(object):
+
+            def __call__(self, a, b):
+                return "%s, %s" % (`a`, `b`)
+
+            def doit(self, a, b):
+                return 'do something %s %s' % (a, b)
+
+        class View(object):
+
+            def action(self, a, kw1=None):
+                return "Parameter[type: %s; value: %s" %(
+                    type(a).__name__, `a`)
+
+            def keyworded(self, a, kw1="spam"):
+                return "kw1: [type: %s; value: %s]" %(
+                    type(kw1).__name__, `kw1`)
+
+            def action1_1(self, a, kw1=None):
+                return "Parameter[type: %s; value: %s" %(
+                    type(a).__name__, `a`)
+
+        class Item2(object):
+            view = View()
+
+        self.app = AppRoot()
+        self.app.folder = Folder()
+        self.app.folder.item = Item()
+        self.app.folder.item2 = Item2()
+        zope.component.provideUtility(JSONReader(), IJSONReader)
+
+    def _createRequest(self, extra_env={}, body=""):
+        env = self._testEnv.copy()
+        env.update(extra_env)
+        if len(body.body):
+            env['CONTENT_LENGTH'] = str(len(body.body))
+
+        publication = Publication(self.app)
+        instream = StringIO(body.body)
+        request = TestJSONRPCRequest(instream, env)
+        request.setPublication(publication)
+        return request
+
+
+    def testProcessInput(self):
+        req = self._createRequest({}, jsonrpc_call)
+        req.processInputs()
+        self.failUnlessEqual(req._args, (1,))
+        self.failUnlessEqual(tuple(req._path_suffix), ('action',))
+
+
+    def testTraversal(self):
+        req = self._createRequest({}, jsonrpc_call)
+        req.processInputs()
+        action = req.traverse(self.app)
+        self.failUnlessEqual(action(*req._args),
+                             "Parameter[type: int; value: 1")
+
+    def testKeyword(self):
+        req = self._createRequest({}, ParamTestCall())
+        req.processInputs()
+        action = req.traverse(self.app)
+        self.failUnlessEqual(action(*req._args, **req.form),
+                             "kw1: [type: unicode; value: u'aaa']")
+
+    def test1_1spec_kw(self):
+        req = self._createRequest({}, Param1_1SpecTestCall1())
+        req.processInputs()
+        action = req.traverse(self.app)
+        self.failUnlessEqual(action(*req._args, **req.form),
+                             "kw1: [type: unicode; value: u'aaa']")
+
+    def test1_1spec2_p(self):
+        req = self._createRequest({}, Param1_1SpecTestCall2())
+        req.processInputs()
+        action = req.traverse(self.app)
+        self.failUnlessEqual(action(*req._args, **req.form),
+                             "Parameter[type: int; value: 1")
+
+    def testJSONRPCMode(self):
+        req = self._createRequest({}, jsonrpc_call)
+        req.processInputs()
+        self.failUnlessEqual(req['JSONRPC_MODE'],True)
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromTestCase(JSONRPCTests)
+
+
+if __name__=='__main__':
+    unittest.TextTestRunner().run(test_suite())


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+<include package="z3c.jsonrpc" />


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+<include package="z3c.jsonrpc" file="meta.zcml" />


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,210 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.configuration.fields
+import zope.interface
+import zope.schema
+import zope.security.zcml
+
+from zope.interface import Interface
+from zope.security.checker import CheckerPublic, Checker
+from zope.configuration.exceptions import ConfigurationError
+
+from zope.component.interface import provideInterface
+from zope.app.component.metaconfigure import handler
+
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.publisher import MethodPublisher
+
+
+class IJSONRPCDirective(zope.interface.Interface):
+    """Directive for JSONRPC methods."""
+
+    for_ = zope.configuration.fields.GlobalObject(
+        title=u"Published Object Type",
+        description=u"""The types of objects to be published via JSONRPC
+
+        This can be expressed with either a class or an interface
+        """,
+        required=True,
+        )
+
+    interface = zope.configuration.fields.Tokens(
+        title=u"Interface to be published.",
+        required=False,
+        value_type=zope.configuration.fields.GlobalInterface()
+        )
+
+    methods = zope.configuration.fields.Tokens(
+        title=u"Methods (or attributes) to be published",
+        required=False,
+        value_type=zope.configuration.fields.PythonIdentifier()
+        )
+
+    class_ = zope.configuration.fields.GlobalObject(
+        title=u"Class",
+        description=u"A class that provides attributes used by the view.",
+        required=False
+        )
+
+    permission = zope.security.zcml.Permission(
+        title=u"Permission",
+        description=u"""The permission needed to use the view.
+
+        If this option is used and a name is given for the view, then
+        the names defined by the given methods or interfaces will be
+        under the given permission.
+
+        If a name is not given for the view, then, this option is required and
+        the given permission is required to call the individual views defined
+        by the given interface and methods.
+
+        (See the name attribute.)
+
+        If no permission is given, then permissions should be declared
+        for the view using other means, such as the class directive.
+        """,
+        required=False)
+
+    name = zope.schema.TextLine(
+        title=u"The name of the view.",
+        description=u"""
+
+        If a name is given, then rpc methods are accessed by
+        traversing the name and then accessing the methods.  In this
+        case, the class should implement
+        zope.pubisher.interfaces.IPublishTraverse.
+
+        If no name is provided, then the names given by the attributes
+        and interfaces are published directly as callable views.
+
+        """,
+        required=False,
+        )
+
+    layer = zope.configuration.fields.GlobalInterface(
+        title=u"The layer the view is declared for",
+        description=u"The default layer for which the default view is "
+                    u"applicable. By default it is applied to all layers.",
+        default=interfaces.IJSONRPCRequest,
+        required=False
+        )
+
+
+def jsonrpc(_context, for_=None, interface=None, methods=None, class_=None,
+    permission=None, name=None, layer=None):
+
+    interface = interface or []
+    methods = methods or []
+
+    if layer is not None:
+        if not layer.extends(interfaces.IJSONRPCRequest):
+            raise ConfigurationError(
+                "The layer interface must extend `IJSONRPCRequest`.")
+    else:
+        layer = interfaces.IJSONRPCRequest
+
+    # If there were special permission settings provided, then use them
+    if permission == 'zope.Public':
+        permission = CheckerPublic
+
+    require = {}
+    for attr_name in methods:
+        require[attr_name] = permission
+
+    if interface:
+        for iface in interface:
+            for field_name in iface:
+                require[field_name] = permission
+            _context.action(
+                discriminator = None,
+                callable = provideInterface,
+                args = ('', for_)
+                )
+
+    # Make sure that the class inherits MethodPublisher, so that the views
+    # have a location
+    if class_ is None:
+        class_ = original_class = MethodPublisher
+    else:
+        original_class = class_
+        class_ = type(class_.__name__, (class_, MethodPublisher), {})
+
+    if name:
+        # Register a single jsonrpc view
+
+        if permission:
+            checker = Checker(require)
+
+            def proxyView(context, request, class_=class_, checker=checker):
+                view = class_(context, request)
+                # We need this in case the resource gets unwrapped and
+                # needs to be rewrapped
+                view.__Security_checker__ = checker
+                return view
+
+            class_ = proxyView
+            class_.factory = original_class
+        else:
+            # No permission was defined, so we defer to the checker
+            # of the original class
+            def proxyView(context, request, class_=class_):
+                view = class_(context, request)
+                view.__Security_checker__ = getCheckerForInstancesOf(
+                    original_class)
+                return view
+            class_ = proxyView
+            class_.factory = original_class
+
+        # Register the new view.
+        _context.action(
+            discriminator = ('jsonrpc', for_, name, layer),
+            callable = handler,
+            args = ('registerAdapter',
+                    class_, (for_, layer), Interface, name,
+                    _context.info)
+            )
+    else:
+        if permission:
+            checker = Checker({'__call__': permission})
+        else:
+            raise ConfigurationError(
+              "JSONRPC view has neither a name nor a permission. "
+              "You have to specify at least one of the two.")
+
+        for name in require:
+            # create a new callable class with a security checker;
+            cdict = {'__Security_checker__': checker,
+                     '__call__': getattr(class_, name)}
+            new_class = type(class_.__name__, (class_,), cdict)
+            _context.action(
+                discriminator = ('jsonrpc', for_, name, layer),
+                callable = handler,
+                args = ('registerAdapter',
+                        new_class, (for_, layer), Interface, name,
+                        _context.info)
+                )
+
+    # Register the used interfaces with the site manager
+    if for_ is not None:
+        _context.action(
+            discriminator = None,
+            callable = provideInterface,
+            args = ('', for_)
+            )


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt	                        (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt	2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,75 @@
+==========
+Directives
+==========
+
+JSONRPC directive
+-----------------
+
+Show how we can use the jsonrpc directive. Register the meta configuration for 
+the directive.
+
+  >>> from zope.configuration import xmlconfig
+  >>> import z3c.jsonrpc
+  >>> context = xmlconfig.file('meta.zcml', z3c.jsonrpc)
+
+Now register the view defined in the testing module within the ``z3c:jsonrpc``
+directive:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsonrpc
+  ...       for="z3c.jsonrpc.testing.IA"
+  ...       class="z3c.jsonrpc.testing.MethodsA"
+  ...       permission="zope.Public"
+  ...       methods="hello"
+  ...       />
+  ... </configure>
+  ... """, context)
+
+Let's check if the view is registered as adapter:
+
+  >>> import zope.component
+  >>> from z3c.jsonrpc.testing import A
+  >>> from z3c.jsonrpc.testing import TestRequest
+  >>> a = A()
+  >>> request = TestRequest()
+  >>> zope.component.queryMultiAdapter((a, request), name='hello')
+  <z3c.jsonrpc.zcml.MethodsA object at ...>
+
+We can also use a layer interface wich will restrict our view registration to
+a specific request type. Provide such a request type layer:
+
+  >>> from z3c.jsonrpc.testing import IJSONRPCTestLayer
+  >>> demoRequest = TestRequest()
+  >>> zope.interface.directlyProvides(demoRequest, IJSONRPCTestLayer)
+
+And register a new JSON-RPC view:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:jsonrpc
+  ...       for="z3c.jsonrpc.testing.IB"
+  ...       class="z3c.jsonrpc.testing.MethodsB"
+  ...       permission="zope.Public"
+  ...       methods="hello"
+  ...       layer="z3c.jsonrpc.testing.IJSONRPCTestLayer"
+  ...       />
+  ... </configure>
+  ... """, context)
+
+Setup a new content stub:
+
+  >>> from z3c.jsonrpc.testing import B
+  >>> b = B()
+
+And test the view within our new layer:
+
+  >>> zope.component.queryMultiAdapter((b, demoRequest), name='hello')
+  <z3c.jsonrpc.zcml.MethodsB object at ...>
+
+Note the object b does not know the view within the default request layer:
+
+  >>> zope.component.queryMultiAdapter((b, request), name='hello') is None
+  True


Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list