[z3-checkins] r24101 - in z3/jsonserver/trunk: . etc jsolait jsolait/doc jsolait/lib jsolait/libnew jsolait/libws jsolait/src tests
jwashin at codespeak.net
jwashin at codespeak.net
Wed Mar 8 13:37:15 CET 2006
Author: jwashin
Date: Wed Mar 8 13:37:02 2006
New Revision: 24101
Added:
z3/jsonserver/trunk/CHANGES.txt
z3/jsonserver/trunk/DEPENDENCIES.cfg
z3/jsonserver/trunk/README.txt
z3/jsonserver/trunk/SETUP.cfg
z3/jsonserver/trunk/VERSION.txt
z3/jsonserver/trunk/ZopePublicLicense.txt
z3/jsonserver/trunk/__init__.py
z3/jsonserver/trunk/configure.zcml
z3/jsonserver/trunk/etc/
z3/jsonserver/trunk/etc/jsonserver-configure.zcml
z3/jsonserver/trunk/etc/jsonserver-meta.zcml
z3/jsonserver/trunk/ftesting.py
z3/jsonserver/trunk/interfaces.py
z3/jsonserver/trunk/jsolait/
z3/jsonserver/trunk/jsolait/README.txt
z3/jsonserver/trunk/jsolait/doc/
z3/jsonserver/trunk/jsolait/install_jsolait.py
z3/jsonserver/trunk/jsolait/lib/
z3/jsonserver/trunk/jsolait/lib/pythonkw.js
z3/jsonserver/trunk/jsolait/libnew/
z3/jsonserver/trunk/jsolait/libws/
z3/jsonserver/trunk/jsolait/src/
z3/jsonserver/trunk/jsoncomponent.py
z3/jsonserver/trunk/jsonrpc.py
z3/jsonserver/trunk/jsonserver-configure.zcml
z3/jsonserver/trunk/jsonserver-meta.zcml
z3/jsonserver/trunk/jsonserver.e3p
z3/jsonserver/trunk/jsonserver.e3t
z3/jsonserver/trunk/jsontest.py (contents, props changed)
z3/jsonserver/trunk/meta.zcml
z3/jsonserver/trunk/metaconfigure.py
z3/jsonserver/trunk/minjson.py
z3/jsonserver/trunk/requestpublicationfactory.py
z3/jsonserver/trunk/tests/
z3/jsonserver/trunk/tests/__init__.py
z3/jsonserver/trunk/tests/jsonrpc.zcml
z3/jsonserver/trunk/tests/jsonrpc_error.zcml
z3/jsonserver/trunk/tests/jsonrpcviews.py
z3/jsonserver/trunk/tests/test.pt
z3/jsonserver/trunk/tests/test_directives.py
z3/jsonserver/trunk/tests/test_httpfactory.py
z3/jsonserver/trunk/tests/test_json.py
z3/jsonserver/trunk/tests/test_jsonrpcpublication.py
z3/jsonserver/trunk/tests/test_jsonrpcrequest.py
Log:
now adding the files
Added: z3/jsonserver/trunk/CHANGES.txt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/CHANGES.txt Wed Mar 8 13:37:02 2006
@@ -0,0 +1,17 @@
+#CHANGES.txt
+
+20051202 a bit of backward-compatibility with five:
+ ++resource++ notation for the javascript. updated to the available
+ jsolait (2005-11-15.small)
+
+20051125 some work toward making json requests better zope citizens.
+Some work toward keyword parameter support.
+Some work toward making more information available in request: now
+provides IBrowserApplicationRequest. This is experimental. Let me
+know what you think.
+
+20050921 Using the next beta of jsolait (20050914). The init file is now
+ jsolait.js instead of init.js. jsolait installer adds init.js as a copy of jsolait.js
+ for backwards compatibility. We are now using the 3.1 ResourceDirectory,
+ so the jsolait file structure does not need to be flattened. Upgrade to this
+ version using the installer is optional.
Added: z3/jsonserver/trunk/DEPENDENCIES.cfg
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/DEPENDENCIES.cfg Wed Mar 8 13:37:02 2006
@@ -0,0 +1,21 @@
+jsonserver
+jsonserver.tests
+zope.app.component
+zope.app.component.tests
+zope.app.location
+zope.app.publication
+zope.app.publication.tests
+zope.app.publisher.xmlrpc
+zope.app.server
+zope.app.testing
+zope.app.zapi
+zope.component
+zope.component.tests
+zope.configuration
+zope.interface
+zope.proxy
+zope.publisher
+zope.publisher.interfaces
+zope.security
+zope.server.http
+zope.testing
Added: z3/jsonserver/trunk/README.txt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/README.txt Wed Mar 8 13:37:02 2006
@@ -0,0 +1,254 @@
+==========
+jsonserver
+==========
+
+JSON is javascript object notation. JSON-RPC performs the same service
+as XML-RPC, except the transport is javascript objects instead of
+XML.
+
+jwashin 4 Jan 2006
+
+===================
+jsonserver Project:
+===================
+
+This project provides the additional functionality of listening and responding
+properly to requests of type "application/json-rpc".
+
+Dependencies:
+-------------
+
+This package will work with (unreleased) zope3 version 3.2 or greater, currently
+the development version available at svn://svn.zope.org/repos/main/Zope3/trunk
+
+jsolait from http://jsolait.net is the recommended client-side javascript
+library. Installation of jsolait is covered in the README.txt file in this
+package's jsolait folder.
+
+Installation:
+-------------
+Install this package in a location accessible to your zope3 instance. The
+lib/python folder of the instance is a good choice.
+
+The files in the etc folder should go into etc/package-includes.
+
+A README.txt file in the jsolait folder has instructions for installing
+a client javascript library.
+
+Usage:
+------
+Similar to xmlrpc usage.
+
+jsonserver looks for content-type "application/json-rpc", and handles those
+requests as JSON-RPC. Other http requests are not affected and will
+presumably work as expected.
+
+jsonrpc provides another namespace,
+'http://namespaces.zope.org/jsonrpc' for zcml configuration.
+Statements like
+
+::
+
+<jsonrpc:view for="" permission="" methods="" class="" />
+
+are used in zcml to make jsonrpc methods viewable.
+
+You may create views that appear only if a jsonrpc listener is installed:
+
+::
+
+ <configure zcml:condition="have jsonrpc">
+ <jsonrpc:view
+ for="someInterface"
+ permission="zope.View"
+ methods="blah blecht"
+ class=".views.JsonViewClass"
+ />
+ </configure>
+
+To make a view class, subclass
+jsonserver.jsonrpc.MethodPublisher like this:
+
+::
+
+ from jsonserver.jsonrpc import MethodPublisher
+ class MyClass(MethodPublisher):
+ def myOutput(self):
+ blah = 'something cool'
+ return blah
+ def myOutput1(self,param1):
+ blecht = self.context.something(param1)
+ return blecht
+
+where the return value can be a python simple object
+(int, long, float, string, etc.) or list or dictionary composed of
+simple objects, lists, and/or dictionaries. Composite built-ins
+like complex numbers, dates, or classes are not currently
+supported. Decompose those, and send a list or dictionary instead.
+Multiple returned values will be marshalled into a list.
+
+For web pages, you will need to include a javascript library for the client side
+in your page template:
+
+::
+
+ <script type="text/javascript" src="/++resource++jsolait/jsolait.js"></script>
+
+will bring in the recommended jsolait library, if it is installed here. The following javascript examples
+are for jsolait, but any similar javascript library may be used, or you can write your own. The
+xmlHTTPRequest POST must set a content-type of "application/json-rpc" for this package to invoke
+json-rpc requests on the server.
+
+From your client javascript code, import the jsonrpc module:
+
+::
+
+ var jsonrpc = imprt('jsonrpc');
+
+Then, make a jsolait connection proxy ("." often works fine
+for addr):
+
+::
+
+ addr="address to server object providing jsonrpc view class";
+ //for better error handling, see http://jsolait.net/wiki/documentation
+ try{var aServer = new jsonrpc.ServiceProxy(addr, ["myOutput"]);
+ }catch(e){alert(e);}
+
+then, for async communication, provide a callback function:
+
+::
+
+ function doThis(resp,err){
+ if (!err) {do something with resp} else {do something with err}
+ }
+
+and call the method:
+
+::
+
+ aServer.myOutput(aparam,doThis);
+
+If you want sync communication, call the method without
+the name of a function as the last parameter.
+
+For communication other than in a web browser (javascript), minjson.py
+or other json implementations have functions for reading and writing
+JSON objects.
+
+The text of a JSON-RPC request looks like:
+
+::
+
+ {"id":jsonid,"method":remotemethod,"params":methodparams}
+
+where
+
+- jsonid is a string or number that may identify this specific request
+- remotemethod is the method to call on the server
+- methodparams is a list (javascript Array) of parameters to the method
+
+The text of a JSON-RPC response looks like:
+
+::
+
+ {"id":jsonid,"result":returnedresult,"error":returnederr}
+
+where
+
+- jsonid is the same jsonid as sent in the request
+- returnedresult is a javascript representation of the result or null
+- returnederr is a javascript representation of an error state
+
+Either returnedresult or returnederr will be the javascript null value.
+
+Actual implementation using e.g., urllib is left as an exercise for the
+reader. Hint: Use the minjson.write(object) and minjson.read(string)
+methods for conversion before and after transport.
+
+Page Templates, Form Variables, and Named Parameters:
+_______________________________________________________
+
+jsonserver will work with page templates and similar
+snippets of HTML. Most registered views (browser:page or similar)
+are also accessible to json-rpc clients. The simplest way to use a
+page template is to call it in javascript just as you would call a
+jsonrpc:view. jsonserver sets a request variable, JSONRPC_MODE,
+which will be True if a template is requested through json-rpc.
+This may be useful if you need json-rpc-specific behavior.
+
+If you need form data, jsonserver has a special facility for this. The
+contents of any client object (dict) passed as a parameter to json-rpc
+that is (cleverly) named "pythonKwMaRkEr" will be available in the request
+as items in request.form. If you call methods with named parameters,
+those items also will replace the named parameters as appropriate.
+
+A pythonkw module is provided here for use with jsolait on the client side.
+Code like
+
+::
+
+ var pythonkw = imprt("pythonkw");
+ var kwparams = new pythonkw.PythonKw({'parm1': 'aaa', 'parm2': text_value})
+ var result = aServer.my_portlet(kwparams);
+
+will do the marshalling so you do not have to type "pythonKwMaRkEr".
+
+Here is an example of using a page template through a
+jsonrpc:view method ( ViewPageTemplateFile is in zope.app.pagetemplate)
+
+::
+
+ def my_portlet(self,parm1='bbb',parm2=None):
+ date = datetime.now()
+ rand = random.randint(0,2000)
+ portlet = ViewPageTemplateFile("my_portlet.pt")
+ return portlet(self,date=date,random=rand,parm1=parm1)
+
+In the above example, parm1 is available to the template as options/parm1
+and as request/form/parm1. parm2 may be available to the template as
+request/form/parm2 if provided in the request.
+
+Debugging:
+__________
+
+To get verbose output of requests, responses, and errors,
+set level DEBUG for your event log in etc/zope.conf for your
+instance. e.g.,
+
+::
+
+ <eventlog>
+ level DEBUG
+ <logfile>
+ path $LOGDIR/z3.log
+ </logfile>
+ <logfile>
+ path STDOUT
+ </logfile>
+ </eventlog>
+
+You can get pretty much the same results with tcpwatch, except you get the
+entire request and response with tcpwatch.
+
+Compatibility:
+______________
+
+Most compatibility issues should be about client implementations.
+
+jsonserver will accept any valid JSON-RPC request that is a POST with
+content-type "application/json-rpc". Output responses will be of
+content-type "application/x-javascript" so that browser clients can know
+that the response will be interpreted in javascript.
+
+jsolait should work on any current browser with enabled javascript and
+a functioning xmlHTTPRequest POST implementation. This includes most
+gecko browsers (Firefox, Mozilla, and Netscape 6.1+), khtml browsers (Safari and
+konqueror), recent IEs, and Opera 8.1+. If it will do Google maps, it probably
+will do jsolait.
+
+Unicode Support:
+________________
+
+jsonserver supports unicode properly now, I think, (maybe?). If you have a
+project that depends on unicode, let me know if this does anything unexpected.
Added: z3/jsonserver/trunk/SETUP.cfg
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/SETUP.cfg Wed Mar 8 13:37:02 2006
@@ -0,0 +1,5 @@
+# Tell zpkg how to install the ZCML slugs.
+
+<data-files zopeskel/etc/package-includes>
+ jsonserver-*.zcml
+</data-files>
Added: z3/jsonserver/trunk/VERSION.txt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/VERSION.txt Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+jsonserver 3.2.2 svn
Added: z3/jsonserver/trunk/ZopePublicLicense.txt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/ZopePublicLicense.txt Wed Mar 8 13:37:02 2006
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+ accompanying copyright notice, this list of conditions,
+ and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from the copyright
+ holders.
+
+4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use
+ Servicemarks (sm) or Trademarks (tm) of the copyright
+ holders. Use of them is covered by separate agreement
+ with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
Added: z3/jsonserver/trunk/__init__.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/__init__.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+#python package
Added: z3/jsonserver/trunk/configure.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/configure.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,86 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:jsonrpc="http://namespaces.zope.org/jsonrpc"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:help="http://namespaces.zope.org/help"
+ i18n_domain="zope"
+ >
+
+ <publisher
+ name="JSONRPC"
+ factory=".requestpublicationfactory.JSONRPCFactory"
+ methods="POST"
+ mimetypes="application/json-rpc"
+ priority="25"
+ />
+
+ <utility
+ factory=".jsoncomponent.JSONReader"
+ provides=".interfaces.IJSONReader"
+ />
+
+ <utility
+ factory=".jsoncomponent.JSONWriter"
+ provides=".interfaces.IJSONWriter"
+ />
+
+ <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=".jsonrpc.MethodTraverser"
+ permission="zope.Public"
+ />
+
+ <!--register a default view for errors, but we do not create the named
+ view, so errors from IException are handled by the publisher.-->
+ <defaultView
+ for="zope.interface.common.interfaces.IException"
+ type=".interfaces.IJSONRPCRequest"
+ name="jsonrpcerror.html"
+ />
+
+ <browser:resourceDirectory
+ name="jsolait"
+ directory="./jsolait"
+ />
+ <browser:resourceDirectory
+ name="jsolaitlib"
+ directory="./jsolait/lib"
+ />
+ <browser:resourceDirectory
+ name="jsolaitlibws"
+ directory="./jsolait/libws"
+ />
+
+ <help:register
+ id="json_rpc"
+ title="JSON RPC"
+ doc_path="README.txt"
+ class="zope.app.onlinehelp.onlinehelptopic.RESTOnlineHelpTopic"
+ />
+
+</configure>
Added: z3/jsonserver/trunk/etc/jsonserver-configure.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/etc/jsonserver-configure.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+<include package="jsonserver"/>
Added: z3/jsonserver/trunk/etc/jsonserver-meta.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/etc/jsonserver-meta.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+<include package="jsonserver" file="meta.zcml" />
Added: z3/jsonserver/trunk/ftesting.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/ftesting.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,48 @@
+import zope.app.testing.functional as functional
+from zope.testing import doctest
+from jsonserver.jsonrpc import JSONRPCRequest, JSONRPCPublication
+
+class HTTPCaller(functional.HTTPCaller):
+ 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-rpc')
+
+ if method in ('GET', 'POST', 'HEAD'):
+ if (method == 'POST' and is_json):
+ request_cls = JSONRPCRequest
+ publication_cls = JSONRPCPublication
+
+ return request_cls, publication_cls
+
+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)
+
+ return doctest.DocFileSuite(*paths, **kw)
Added: z3/jsonserver/trunk/interfaces.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/interfaces.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+# adapted from various xmlrpc interface files jwashin 2005-06-26
+
+#2005-08-16 A few changes needed after a zope3 trunk change
+#2005-11-07 Allowed IDefaultBrowserLayer in JSONRPCRequest. This permits skin
+# lookups
+
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces.http import IHTTPApplicationRequest,\
+ IHTTPCredentials
+from zope.interface import Interface
+from zope.component.interfaces import IView, IPresentation
+from zope.interface import Attribute
+from zope.app.publisher.xmlrpc import IMethodPublisher
+from zope.publisher.interfaces.xmlrpc import IXMLRPCPublication
+from zope.app.publication.interfaces import IRequestFactory
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+class IJSONRPCRequestFactory(IRequestFactory):
+ """Browser request factory"""
+
+class IJSONRPCPublisher(IPublishTraverse):
+ """JSON-RPC Publisher
+ like zope.publisher.interfaces.xmlrpc.IXMLRPCPublisher
+ """
+class IJSONRPCPublication(IXMLRPCPublication):
+ """Object publication framework.
+ like zope.publisher.interfaces.xmlrpc.IXMLRPCPublication
+ """
+class IJSONRPCRequest(IHTTPApplicationRequest, IHTTPCredentials, IDefaultBrowserLayer):
+ """JSON-RPC Request
+ like zope.publisher.interfaces.xmlrpc.IXMLRPCRequest
+ """
+ jsonID=Attribute("""JSON-RPC ID for the request""")
+
+class IJSONReader(Interface):
+ def read(aString):
+ """read and interpret a string in JSON as python"""
+
+class IJSONWriter(Interface):
+ def write(anObject, encoding=None):
+ """return a JSON unicode string representation of a python object
+ Encode if encoding is provided.
+ """
+
+class IJSON(IJSONReader,IJSONWriter):
+ """read and write JSON"""
+
+#class IMethodPublisher(Interface):
+#
+# """Marker interface for an object that wants to publish methods
+# see zope.app.publisher.xmlrpc.IMethodPublisher
+#
+# it's commented here for completeness; actually, this uses
+# the one in zope.app.publisher.xmlrpc
+# """
+
+class IJSONRPCPresentation(IPresentation):
+ """JSONRPC Presentation
+ like zope.app.publisher.interfaces.xmlrpc.IXMLRPCPresentation
+ """
+
+class IJSONRPCView(IJSONRPCPresentation,IView):
+ """JSONRPC View
+ like zope.app.publisher.interfaces.xmlrpc.IXMLRPCView
+ """
Added: z3/jsonserver/trunk/jsolait/README.txt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsolait/README.txt Wed Mar 8 13:37:02 2006
@@ -0,0 +1,28 @@
+This is a place for the jsolait javascript library from
+
+ http://jsolait.net/
+
+It will be accessible as a resource , '/@@/jsolait/...'
+
+Manual installation is something like:
+
+get the library
+extract it here
+fix the paths in jsolait.js so that all .js files are accessible from the
+/@@/jsolait resourceDirectory, i.e., this folder
+fix the submodule GET so that things still work after page refresh on konqueror and safari
+ (just add a ?s="[timestamp]" so the file gets refetched instead of mouldering in cache)
+fix content-types in jsonrpc.js from text/plain or text/xml to text/x-json
+
+There is a script here that does the above for you. It's small, and
+you probably want to read it so that it cannot suprise you. After
+viewing it, use it from this directory:
+
+python install_jsolait.py
+
+The installer uses urllib and writes files in this directory, so expect
+it to fail if you do not have write privileges or an accessible internet
+connection.
+
+Jim Washington
+21 Sep 2005
Added: z3/jsonserver/trunk/jsolait/install_jsolait.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsolait/install_jsolait.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,154 @@
+##############################################################################
+#
+# Copyright (c) 2005 Jim Washington 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.
+#
+##############################################################################
+# jsolait installer
+#
+#
+# jsolait is available on the net, licensed LGPL. When you run this
+# script, you will download, unzip, and make appropriate changes to
+# the jsolait library to make it usable with jsonserver.
+# jwashin 2005-05-16
+# 2005-09-21 Updated for 20050914 beta of jsolait
+
+import os, md5, urllib
+
+#get the current, working version of jsolait
+#this one (a beta of 1.1) works as of now (3 Jun 05)
+loc = 'http://jsolait.net/download/'
+
+filename = 'jsolait.2005-11-15.small.zip'
+#md5sum = '69014dddf37fb7cb7b88b5eac84d1569'
+#md5sum = '475b7a505d6ab911b25818831244bd43'
+md5sum = 'c21c32a7a8756a35a0e48a30b710b3e1'
+fileurl = loc + filename
+
+#Assure the folders are there.
+for directory in ['src','doc','lib','libws']:
+ if not os.path.exists(directory):
+ os.mkdir(directory)
+
+zippedfile = os.path.join('src',filename)
+
+#get file unless it is already there
+if not os.path.exists(zippedfile):
+ print "retrieving %s" % fileurl
+ urllib.urlretrieve(fileurl,zippedfile)
+else:
+ print "%s exists; reprocessing. " % zippedfile
+ print "to retrieve again, delete or rename %s." % zippedfile
+
+#check file signature
+print "checking md5"
+filedata = file(zippedfile,'r').read()
+check = md5.new(filedata).hexdigest()
+if not check == md5sum:
+ raise ValueError('md5 sums do not match')
+
+#got the file; now, unzip it.
+
+import zipfile
+
+print "unzipping %s" % zippedfile
+
+zip = zipfile.ZipFile(zippedfile,'r')
+
+filesList = zip.namelist()
+
+
+#put the files in the folders
+for k in filesList:
+ if not k.endswith('/'):
+ f = os.path.split(k)
+ #print f
+ if 'doc' in f[0]:
+ file(os.path.join('doc',f[1]),'wb').write(zip.read(k))
+ elif 'libws' in f[0]:
+ file(os.path.join('libws',f[1]),'wb').write(zip.read(k))
+ elif 'lib' in f[0]:
+ file(os.path.join('lib',f[1]),'wb').write(zip.read(k))
+ else:
+ file(f[-1],'wb').write(zip.read(k))
+
+# patch to modify paths in init.js
+# init.js is no more, replaced by jsolait.js, so fix those paths
+
+linesep = os.linesep
+mfile = 'jsolait.js'
+print "patching %s" % mfile
+t = file(mfile,'U')
+lines = t.readlines()
+t.close()
+t = file(mfile,'w')
+moda = False
+modb = False
+lineadded = False
+for k in lines:
+ d = k.rstrip()
+ #set the installPath to the resource directory name
+ if d.find('mod.baseURI="./jsolait"') >= 0:
+ s = 'mod.baseURI = "/++resource++jsolait";'
+ #this one works around some javascript clients not using js if it's already in cache
+ #OK, we load the js each time it's used, but it still works if you refresh the page
+ #in konqueror or safari.
+ #elif d.find('xmlhttp.open("GET", url, false);') >= 0:
+ # s = 'var d = new Date();\nxmlhttp.open("GET", url +"?m="+d.getTime(), false);'
+ #elif d.find('xmlhttp.open("GET", uri, false);') >= 0:
+ # s = 'var d = new Date();\nxmlhttp.open("GET", uri +"?m="+d.getTime(), false);'
+ #elif d.find('xmlhttp.open("GET",uri,false);') >= 0:
+ # s = 'var d=new Date();%sxmlhttp.open("GET",uri +"?m="+d.getTime(),false);' % linesep
+ elif d.find('baseURI)s') > 0:
+ s = d.replace('/','',1)
+ if lineadded == False:
+ #add a line for pythonkw
+ s = s + linesep + 'pythonkw:"%(baseURI)slib/pythonkw.js",'
+ lineadded = True
+ elif d.find('if(xmlhttp.status==200') == 0:
+ s = 'if(xmlhttp.status==200||xmlhttp.status==0||xmlhttp.status==null){'
+ else:
+ s = d
+ t.write('%s%s' % (s,linesep))
+t.write('importModule=imprt'+linesep)
+t.close()
+
+#for compat with prev version, copy jsolait.js to init.js
+t = file(mfile,'r')
+z = t.readlines()
+t.close()
+f = file('init.js','w')
+for k in z:
+ f.write(k)
+f.close()
+
+#patch text/plain and text/xml to text/x-json
+mfile = 'jsonrpc.js'
+os.chdir('lib')
+print "patching %s" % mfile
+t = file(mfile,'U')
+lines = t.readlines()
+t.close()
+t = file(mfile,'w')
+for k in lines:
+ d = k.rstrip()
+ if d.find('text/plain') >= 0:
+ s = d.replace('text/plain','application/json-rpc')
+ elif d.find('text/xml') >= 0:
+ s = d.replace('text/xml','application/json-rpc')
+ elif d.find('ImportFailed(') > 0 and not d.endswith(';'):
+ s = d + ';'
+ else:
+ s = d
+ t.write('%s%s' % (s,linesep))
+t.close()
+os.chdir('..')
+print "done."
+print "original zip file is %s" % zippedfile
Added: z3/jsonserver/trunk/jsolait/lib/pythonkw.js
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsolait/lib/pythonkw.js Wed Mar 8 13:37:02 2006
@@ -0,0 +1,20 @@
+
+Module("pythonkw","1.0.0", function(mod){
+
+ var jsonrpc = imprt("jsonrpc");
+
+ mod.PythonKw = Class(function(publ){
+ publ.__init__ = function(kw){
+ this.kw = kw;
+ }
+
+ publ.toJSON = function(){
+ var pack = {};
+ pack["pythonKwMaRkEr"] = this.kw;
+ return jsonrpc.marshall(pack);
+ }
+
+ publ.kw;
+ })
+
+})
Added: z3/jsonserver/trunk/jsoncomponent.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsoncomponent.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2005 Jim Washington 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.
+#
+##############################################################################
+
+# jsoncomponent.py
+# json implementation component
+# jwashin 2005-07-27
+# 2005-10-09 updated to properly? handle unicode on the boundary
+# 2005-09-08 updated for modularity and IResult
+
+import minjson as json
+from interfaces import IJSONReader, IJSONWriter
+from zope.interface import implements
+
+
+class JSONReader(object):
+ """component implementing JSON reading"""
+ implements(IJSONReader)
+
+ def read(self,aString, encoding='utf-8'):
+ return json.read(aString,encoding)
+
+
+class JSONWriter(object):
+ """component implementing JSON writing"""
+ implements(IJSONWriter)
+
+ def write(self,anObject):
+ return json.write(anObject)
Added: z3/jsonserver/trunk/jsonrpc.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsonrpc.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,331 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+#adapted from various trunk xmlrpc files jwashin 2005-06-27
+
+#2005-06-26 removed JSONRPCNotifyResponse and got JSONRPCResponse to handle
+# 'notify' events
+#2005-06-26 let the publisher handle encoding
+#2005-06-27 set outgoing content-type the same as incoming content-type.
+#2005-09-08 updated to work with the new IResult idea (wsgi)
+#2005-10-09 unicode handling update
+
+__docformat__ = 'restructuredtext'
+
+from zope.app.publication.http import BaseHTTPPublication
+from interfaces import IMethodPublisher, IJSONRPCView, IJSONRPCPublisher,\
+ IJSONRPCRequest, IJSONReader, IJSONWriter
+from zope.interface import implements
+#from zope.publisher.http import IResult
+from zope.app.location.location import Location
+from zope.publisher.http import HTTPRequest, HTTPResponse, \
+ getCharsetUsingRequest, DirectResult
+from zope.publisher.browser import BrowserRequest
+from zope.security.proxy import isinstance
+from StringIO import StringIO
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IBrowserApplicationRequest
+from zope.component import getUtility
+
+import traceback
+import logging
+
+DEBUG = logging.DEBUG
+logger = logging.getLogger()
+
+keyword_key = "pythonKwMaRkEr"
+
+#writeProfileData transcribes reads and writes files in the zope3
+# instance directory for profiling use.
+# profiledata.py has response dicts that can be read as python
+# profiledata.js has request javascript objects in javascript
+writeProfileData = False
+
+JSONRPCPublication = BaseHTTPPublication
+
+class JSONRPCPublicationFactory(object):
+ """a JSON-RPC Publication handler
+ modeled after zope.app.publication.xmlrpc.XMLRPCPublicationFactory
+ """
+ def __init__(self,db):
+ self.__pub = JSONRPCPublication(db)
+ def __call__(self):
+ return self.__pub
+
+class JSONRPCRequest(BrowserRequest):
+ """a JSON-RPC Request
+ modeled after zope.publisher.xmlrpc.XMLRPCRequest
+ REQUEST from JSON-RPC client
+ should have
+ method
+ params
+ id
+ """
+ jsonID = 'dummy'
+ form = {}
+ #IBrowserRequest is necessary because sometimes complete views may be
+ #transported that may need to look-up e.g., icons.
+ implements(IJSONRPCRequest,IBrowserRequest, IBrowserApplicationRequest)
+ _args = ()
+ def _createResponse(self):
+ """return a response"""
+ return JSONRPCResponse()
+
+ def processInputs(self):
+ """take the converted request and make useful args of it"""
+
+ json = getUtility(IJSONReader)
+ stream = self._body_instream
+ input = []
+ incoming = stream.read(1000)
+ while incoming:
+ input.append(incoming)
+ incoming = stream.read(1000)
+ input = ''.join(input)
+ #make it unicode; we are at the boundary
+ input = self._decode(input)
+ #encoding = getCharsetUsingRequest(self)
+ #if not isinstance(input,unicode):
+ # input = input.decode(encoding)
+ data = json.read(input)
+ if writeProfileData:
+ infile = 'profiledata.js'
+ t = open(infile,'a')
+ t.write('%s\n' % input)
+ t.close()
+ logger.log(DEBUG, "processing inputs (%s)" % data)
+ #print "processing inputs (%s)" % data
+ functionstr = data['method']
+ # failure unless we split on '.' Why?
+ function = functionstr.split('.')
+ self.jsonID = data['id']
+ args = list(data['params'])
+ # now, look for keyword parameters
+ kwargs = None
+ notPositional = []
+ for k in args:
+ if isinstance(k,dict):
+ if k.has_key(keyword_key):
+ if isinstance(k[keyword_key],dict):
+ j = k[keyword_key]
+ 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]
+ self._args = tuple(args)
+ #make environment,cookies, etc., available to request.get()
+ super(JSONRPCRequest,self).processInputs()
+ self._environ['JSONRPC_MODE'] = True
+ if function:
+ self.setPathSuffix(function)
+
+ def traverse(self,object):
+ return super(BrowserRequest,self).traverse(object)
+
+ def __getitem__(self,key):
+ return self.get(key)
+
+# IResult has gone PRIVATE; for we now will send a bytestring and set
+# charset.
+
+##class JSONResult(object):
+## implements(IResult)
+## def __init__(self,aString, encoding='utf-8'):
+## #statements to help figure out Opera
+## #encoding='utf-8' #yields oriental charset, unparseable
+## #encoding='utf-16' #yields something that looks like the right code,
+## # no trouble in gecko, but opera refuses to parse it
+## #encoding = None is troublesome for gecko; only get '{'
+## #encoding = 'iso-8859-1'
+## try:
+## s= aString.encode(encoding)
+## except UnicodeEncodeError:
+## s = aString
+## self.body = [s]
+## if encoding == 'utf-8':
+## # don't need charset for utf-8; charset handling seems buggy on
+## # some browsers, so best not to use unnecessarily
+## self.headers = [('content-type',
+## 'application/x-javascript'),
+## ('content-length',str(len(s)))]
+## else:
+## self.headers = [('content-type',
+## 'application/x-javascript;charset=%s' % encoding),
+## ('content-length',str(len(s)))]
+#### else:
+#### self.body = [aString]
+#### self.headers = [('content-type','application/x-javascript'),
+#### ('content-length',str(len(aString)))]
+##
+
+class JSONRPCResponse(HTTPResponse):
+ """JSON-RPC Response
+ modeled after zope.publisher.xmlrpc.XMLRPCResponse
+ """
+ #def getBase(self):
+ # return True
+
+ def setResult(self,result):
+ """return
+ {
+ 'id' : matches id in request
+ 'result' : the result or null if error
+ 'error' : the error or null if result
+ }
+ """
+ id = self._request.jsonID
+ if id is not None:
+ result = premarshal(result)
+ wrapper = {'id':id}
+ wrapper['result'] = result
+ wrapper['error'] = None
+ if writeProfileData:
+ outfile = 'profiledata.py'
+ t = open(outfile,'a')
+ t.write('%s\n' % wrapper)
+ t.close()
+ json = getUtility(IJSONWriter)
+ encoding = getCharsetUsingRequest(self._request)
+ result = json.write(wrapper)
+ #body = JSONResult(result, encoding)
+ 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'
+ if isinstance(result,unicode):
+ body = result.encode(encoding)
+ charset = encoding
+ #elif isinstance(result,string):
+ # body = result
+ # charset = sys.getdefaultencoding()
+ else:
+ #something's wrong. json did not return unicode.
+ raise AttributeError
+ 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 = getUtility(IJSONWriter)
+ result = json.write(wrapper)
+ body = self._prepareResult(result)
+ super(JSONRPCResponse,self).setResult(body)
+ logger.log(DEBUG,"Exception: %s" % result)
+ self.setStatus(200)
+
+def premarshal_dict(data):
+ """return a non-proxied dict"""
+ return dict([(premarshal(k), premarshal(v))
+ for (k, v) in data.items()])
+
+def premarshal_list(data):
+ """return a non-proxied list"""
+ return map(premarshal, data)
+
+#note: no dates or datetimes in json, though supported by xmlrpc
+premarshal_dispatch_table = {
+ dict: premarshal_dict,
+ list: premarshal_list,
+ tuple: premarshal_list,
+ }
+
+premarshal_dispatch = premarshal_dispatch_table.get
+
+def premarshal(data):
+ premarshaller = premarshal_dispatch(data.__class__)
+ if premarshaller is not None:
+ return premarshaller(data)
+ return data
+
+
+class JSONRPCView(object):
+ """A base JSON-RPC view that can be used as mix-in for JSON-RPC views.
+ like zope.app.publisher.xmlrpc.XMLRPCView
+ """
+ implements(IJSONRPCView)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+
+class MethodPublisher(JSONRPCView, Location):
+ """Base class for JSON-RPC views that publish methods
+ like zope.app.publisher.xmlrpc.MethodPublisher
+ """
+ implements(IMethodPublisher)
+
+ 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):
+ implements(IJSONRPCPublisher)
+
+ __used_for__ = IMethodPublisher
+
+ def __init__(self, context, request):
+ self.context = context
+
+ def publishTraverse(self, request, name):
+ return getattr(self.context, name)
+
+
+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('')
+
+ super(TestRequest, self).__init__(
+ body_instream, _testEnv, response)
+
Added: z3/jsonserver/trunk/jsonserver-configure.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsonserver-configure.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,5 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <include package="jsonserver" />
+
+</configure>
Added: z3/jsonserver/trunk/jsonserver-meta.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsonserver-meta.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,5 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <include package="jsonserver" file="meta.zcml" />
+
+</configure>
Added: z3/jsonserver/trunk/jsonserver.e3p
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsonserver.e3p Wed Mar 8 13:37:02 2006
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
+<!-- Project file for project jsonserver -->
+<!-- Saved: 2006-01-01, 12:26:01 -->
+<!-- Copyright (C) 2006 , -->
+<Project version="3.8">
+ <ProgLanguage mixed="0">Python</ProgLanguage>
+ <UIType>Console</UIType>
+ <Description></Description>
+ <Version></Version>
+ <Author></Author>
+ <Email></Email>
+ <Sources>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>__init__.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>test_directives.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>test_httpfactory.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>test_jsonrpcrequest.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>test_jsonrpcpublication.py</Name>
+ </Source>
+ <Source>
+ <Dir>jsolait</Dir>
+ <Name>install_jsolait.py</Name>
+ </Source>
+ <Source>
+ <Name>metaconfigure.py</Name>
+ </Source>
+ <Source>
+ <Name>jsonrpc.py</Name>
+ </Source>
+ <Source>
+ <Name>__init__.py</Name>
+ </Source>
+ <Source>
+ <Name>interfaces.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>jsonrpcviews.py</Name>
+ </Source>
+ <Source>
+ <Name>minjson.py</Name>
+ </Source>
+ <Source>
+ <Dir>tests</Dir>
+ <Name>test_json.py</Name>
+ </Source>
+ <Source>
+ <Name>jsoncomponent.py</Name>
+ </Source>
+ <Source>
+ <Name>requestpublicationfactory.py</Name>
+ </Source>
+ <Source>
+ <Name>ftesting.py</Name>
+ </Source>
+ </Sources>
+ <Forms>
+ </Forms>
+ <Translations>
+ </Translations>
+ <Interfaces>
+ </Interfaces>
+ <Others>
+ <Other>
+ <Name>configure.zcml</Name>
+ </Other>
+ <Other>
+ <Name>meta.zcml</Name>
+ </Other>
+ <Other>
+ <Name>README.txt</Name>
+ </Other>
+ <Other>
+ <Name>ZopePublicLicense.txt</Name>
+ </Other>
+ <Other>
+ <Dir>jsolait</Dir>
+ <Name>README.txt</Name>
+ </Other>
+ <Other>
+ <Dir>etc</Dir>
+ <Name>jsonserver-configure.zcml</Name>
+ </Other>
+ <Other>
+ <Dir>etc</Dir>
+ <Name>jsonserver-meta.zcml</Name>
+ </Other>
+ <Other>
+ <Dir>tests</Dir>
+ <Name>jsonrpc.zcml</Name>
+ </Other>
+ <Other>
+ <Dir>tests</Dir>
+ <Name>jsonrpc_error.zcml</Name>
+ </Other>
+ <Other>
+ <Dir>tests</Dir>
+ <Name>test.pt</Name>
+ </Other>
+ <Other>
+ <Name>VERSION.txt</Name>
+ </Other>
+ <Other>
+ <Name>jsonserver-configure.zcml</Name>
+ </Other>
+ <Other>
+ <Name>jsonserver-meta.zcml</Name>
+ </Other>
+ <Other>
+ <Name>DEPENDENCIES.cfg</Name>
+ </Other>
+ <Other>
+ <Name>SETUP.cfg</Name>
+ </Other>
+ <Other>
+ <Dir>jsolait</Dir>
+ <Dir>lib</Dir>
+ <Name>nothing.txt</Name>
+ </Other>
+ <Other>
+ <Dir>jsolait</Dir>
+ <Dir>libws</Dir>
+ <Name>nothing.txt</Name>
+ </Other>
+ <Other>
+ <Name>CHANGES.txt</Name>
+ </Other>
+ <Other>
+ <Dir>jsolait</Dir>
+ <Dir>lib</Dir>
+ <Name>pythonkw.js</Name>
+ </Other>
+ </Others>
+ <Vcs>
+ <VcsType>Subversion</VcsType>
+ <VcsOptions>{'status': [''], 'log': [''], 'global': [''], 'update': [''], 'remove': [''], 'add': [''], 'tag': [''], 'export': [''], 'diff': [''], 'commit': [''], 'checkout': [''], 'history': ['']}</VcsOptions>
+ <VcsOtherData>{'standardLayout': True}</VcsOtherData>
+ </Vcs>
+ <FiletypeAssociations>
+ <FiletypeAssociation pattern="*.ui.h" type="FORMS" />
+ <FiletypeAssociation pattern="*.ui" type="FORMS" />
+ <FiletypeAssociation pattern="*.idl" type="INTERFACES" />
+ <FiletypeAssociation pattern="*.py" type="SOURCES" />
+ <FiletypeAssociation pattern="*.ptl" type="SOURCES" />
+ </FiletypeAssociations>
+</Project>
Added: z3/jsonserver/trunk/jsonserver.e3t
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsonserver.e3t Wed Mar 8 13:37:02 2006
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Tasks SYSTEM "Tasks-3.8.dtd">
+<!-- Tasks file for project jsonserver -->
+<!-- Saved: 2006-02-03, 09:04:27 -->
+<Tasks version="3.8">
+</Tasks>
Added: z3/jsonserver/trunk/jsontest.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/jsontest.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,329 @@
+from unittest import TestCase, TestSuite, main, makeSuite
+import minjson as json
+#import jsonnew as json
+
+## jsontest.py implements tests for the json.py JSON
+## (http://json.org) reader and writer.
+## Copyright (C) 2005 Patrick D. Logan
+## Contact mailto:patrickdlogan at stardecisions.com
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.=
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# The object tests should be order-independent. They're not.
+# i.e. they should test for existence of keys and values
+# with read/write invariance.
+
+
+
+class JsonTest(TestCase):
+
+ def testReadEmptyObject(self):
+ obj = json.read("{}")
+ self.assertEqual({}, obj)
+
+ def testWriteEmptyObject(self):
+ s = json.write({})
+ self.assertEqual("{}", s)
+
+ def testReadStringValue(self):
+ obj = json.read('{ "name" : "Patrick" }')
+ self.assertEqual({ "name" : "Patrick" }, obj)
+
+ def testReadEscapedQuotationMark(self):
+ obj = json.read(r'"\""')
+ #self.assertEqual(r'"', obj)
+
+ def testReadEscapedSolidus(self):
+ obj = json.read(r'"\/"')
+ #self.assertEqual(r'/', obj)
+
+ def testReadEscapedReverseSolidus(self):
+ obj = json.read(r'"\\"')
+ self.assertEqual("\\", obj)
+
+ def testReadEscapedBackspace(self):
+ obj = json.read(r'"\b"')
+ self.assertEqual("\b", obj)
+
+ def testReadEscapedFormfeed(self):
+ obj = json.read(r'"\f"')
+ self.assertEqual("\f", obj)
+
+ def testReadEscapedNewline(self):
+ obj = json.read(r'"\n"')
+ self.assertEqual("\n", obj)
+
+ def testReadEscapedCarriageReturn(self):
+ obj = json.read(r'"\r"')
+ self.assertEqual("\r", obj)
+
+ def testReadEscapedHorizontalTab(self):
+ obj = json.read(r'"\t"')
+ self.assertEqual("\t", obj)
+
+ def testReadEscapedHexCharacter(self):
+ obj = json.read(r'"\u000A"')
+ #self.assertEqual("\n", obj)
+ obj = json.read(r'"\u1001"')
+ #self.assertEqual(u'\u1001', obj)
+
+ #def testReadBadEscapedHexCharacter(self):
+ # self.assertRaises(UnicodeDecodeError, self.doReadBadEscapedHexCharacter)
+ #self.doReadBadEscapedHexCharacter()
+ #do we really care about this?
+
+ #def doReadBadEscapedHexCharacter(self):
+ # json.read('"\u10K5"')
+
+
+ def testReadBadObjectKey(self):
+ self.assertRaises(SyntaxError, self.doReadBadObjectKey)
+
+ def doReadBadObjectKey(self):
+ json.read('{ "44 : "age" }')
+
+ def testReadDoubleSolidusComment(self):
+ obj = json.read("[1, 2, // This is a comment.\n 3]")
+ self.assertEqual([1, 2, 3], obj)
+ obj = json.read('[1, 2, // This is a comment.\n{"last":3}]')
+ self.assertEqual([1, 2, {"last":3}], obj)
+
+ def testReadBadDoubleSolidusComment(self):
+ self.assertRaises(SyntaxError, self.doReadBadDoubleSolidusComment)
+
+ def doReadBadDoubleSolidusComment(self):
+ json.read("[1, 2, / This is not a comment.\n 3]")
+
+ def testReadCStyleComment(self):
+ obj = json.read("[1, 2, /* This is a comment. \n */ 3]")
+ self.assertEqual([1, 2, 3], obj)
+ obj = json.read('[1, 2, /* This is a comment. */{"last":3}]')
+ self.assertEqual([1, 2, {"last":3}], obj)
+
+ def testReadCStyleCommentWithoutEnd(self):
+ self.assertRaises(SyntaxError, self.doReadCStyleCommentWithoutEnd)
+
+ def testReadCStyleCommentWithSlashStar(self):
+ obj = self.doReadCStyleCommentWithSlashStar()
+ self.assertEqual([1, 2, 3], obj)
+
+ def doReadCStyleCommentWithoutEnd(self):
+ json.read("[1, 2, /* This is not a comment./ 3]")
+
+ def doReadCStyleCommentWithSlashStar(self):
+ return json.read("[1, 2, /* This is not a comment./* */ 3]")
+
+ def testReadBadObjectSyntax(self):
+ self.assertRaises(SyntaxError, self.doReadBadObjectSyntax)
+
+ def doReadBadObjectSyntax(self):
+ json.read('{"age", 44}')
+
+ def testWriteStringValue(self):
+ s = json.write({ 'name' : 'Patrick' })
+ self.assertEqual("{'name': 'Patrick'}", s)
+
+ def testReadIntegerValue(self):
+ obj = json.read('{ "age" : 44 }')
+ self.assertEqual({ "age" : 44 }, obj)
+
+ def testReadNegativeIntegerValue(self):
+ obj = json.read('{ "key" : -44 }')
+ self.assertEqual({ "key" : -44 }, obj)
+
+ def testReadFloatValue(self):
+ obj = json.read('{ "age" : 44.5 }')
+ self.assertEqual({ "age" : 44.5 }, obj)
+
+ def testReadNegativeFloatValue(self):
+ obj = json.read(' { "key" : -44.5 } ')
+ self.assertEqual({ "key" : -44.5 }, obj)
+
+ def testReadBadNumber(self):
+ self.assertRaises(SyntaxError, self.doReadBadNumber)
+
+ def doReadBadNumber(self):
+ json.read('-44.4.4')
+
+ def testReadSmallObject(self):
+ obj = json.read('{ "name" : \'Patrick\', "age":44} ')
+ self.assertEqual({ "age" : 44, "name" : "Patrick" }, obj)
+
+ def testReadEmptyArray(self):
+ obj = json.read('[]')
+ self.assertEqual([], obj)
+
+ def testWriteEmptyArray(self):
+ self.assertEqual("[]", json.write([]))
+
+ def testReadSmallArray(self):
+ obj = json.read(' [ "a" , "b", "c" ] ')
+ self.assertEqual(["a", "b", "c"], obj)
+
+ def testWriteSmallArray(self):
+ self.assertEqual('[1, 2, 3, 4]', json.write([1, 2, 3, 4]))
+
+ def testWriteSmallObject(self):
+ s = json.write({ "name" : "Patrick", "age": 44 })
+ self.assertEqual("{'age': 44, 'name': 'Patrick'}", s)
+
+ def testWriteFloat(self):
+ self.assertEqual("3.445567", json.write(3.445567))
+
+ def testWriteLong(self):
+ self.assertEqual("12345678901234567890",json.write(12345678901234567890))
+
+ def testReadNegativeLongValue(self):
+ obj = json.read('{ "key" : -44556677889900112233 }')
+ self.assertEqual({ "key" : -44556677889900112233 }, obj)
+
+ def testReadTrue(self):
+ self.assertEqual(json.read("true"),True)
+
+ def testReadFalse(self):
+ self.assertEqual(json.read("false"),False)
+
+ def testReadNull(self):
+ self.assertEqual(None, json.read("null"))
+
+ def testWriteTrue(self):
+ self.assertEqual("true", json.write(True))
+
+ def testWriteFalse(self):
+ self.assertEqual("false", json.write(False))
+
+ def testReadEmptyArrayAtEndOfObject(self):
+ self.assertEqual({"a":"a","b":"b","c":[]}, json.read('{"a":"a","b":"b","c":[]}'))
+
+ def testReadObjectAtEndOfArray(self):
+ self.assertEqual(["a","b","c",{"a":"a","b":"b"}], json.read('["a","b","c",{"a":"a","b":"b"} ]'))
+
+ def testReadEmptyObjectAtEndOfArray(self):
+ self.assertEqual(["a","b","c",{}], json.read('["a","b","c",{}]'))
+
+ def testReadEmptyArrayAtEndOfArray(self):
+ self.assertEqual(["a","b",[]], json.read('["a","b",[]]'))
+
+ def testReadEmptyObjectAtEndOfObject(self):
+ self.assertEqual({"a":"a","b":"b","c":{}}, json.read('{"a":"a","b":"b","c":{}}'))
+
+
+ #The following 3 yield a syntax error in minjson
+ #def testWriteAndReadComplexString(self):
+ # obj = 'she said, "isn\'t this great?"'
+ # print
+ # print obj
+ # obj = str(obj)
+ # w = json.write(obj)
+ # print w
+ # r = json.read(w)
+ # self.assertEqual(obj,str(r))
+
+ #def testWriteAndReadTripleQuotedString(self):
+ # obj = """ Mary said, "Hi, show me a 'good time!'"
+ # To which Bob replied, "Sure!"
+ # """
+ # w = json.write(obj)
+ # print str(w)
+ # r = json.read(obj)
+ # self.assertEqual(r , obj)
+
+ #def testWriteAndReadUnicode(self):
+ # obj = u'La Pe\xf1a'
+ # w = json.write(obj)
+ # print
+ # print w
+ # print '_________________________'
+ # r = json.read(w)
+ # print r
+ # self.assertEqual(r,obj)
+
+ def testWriteNull(self):
+ self.assertEqual("null", json.write(None))
+
+ def testWriteUnicode(self):
+ self.assertEqual(u'test', json.write(u'test'))
+
+ def testReadArrayOfSymbols(self):
+ self.assertEqual([True, False, None], json.read(" [ true, false,null] "))
+
+ def testWriteArrayOfSymbols(self):
+ self.assertEqual("[true, false, null]", json.write([True, False, None]))
+
+ def testReadArray2(self):
+ self.assertEqual(json.read('{"a":[1,2,3]}'),{'a':[1,2,3]})
+
+ def testReadComplexObject(self):
+ src = '''
+ { "name": "Patrick", "age" : 44, "Employed?" : true, "Female?" : false, "grandchildren":null }
+'''
+ obj = json.read(src)
+ self.assertEqual({"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None}, obj)
+
+ def testReadSingleQuotedDictKeys(self):
+ src = "{'id':'HttpReq','params':[1],'method':'action'}"
+ obj = json.read(src)
+
+ def testReadSingleQuotedStrings(self):
+ src = "'hello'"
+ obj = json.read(src)
+
+ def testReadLongArray(self):
+ src = '''[ "used",
+ "abused",
+ "confused",
+ true, false, null,
+ 1,
+ 2,
+ [3, 4, 5] ]
+'''
+ obj = json.read(src)
+ self.assertEqual(["used","abused","confused", True, False, None,
+ 1,2,[3,4,5]], obj)
+
+
+ def testReadComplexArray(self):
+ src = '''
+[
+ { "name": "Patrick", "age" : 44,
+ "Employed?" : true, "Female?" : false,
+ "grandchildren":null },
+ "used",
+ "abused",
+ "confused",
+ 1,
+ 2,
+ [3, 4, 5]
+]
+'''
+ obj = json.read(src)
+ self.assertEqual([{"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None},
+ "used","abused","confused",
+ 1,2,[3,4,5]], obj)
+
+ def testWriteComplexArray(self):
+ obj = [{"name":"Patrick","age":44,"Employed?":True,"Female?":False,"grandchildren":None},
+ "used","abused","confused",
+ 1,2,[3,4,5]]
+ self.assertEqual("[{'Female?': false, 'age': 44, 'name': 'Patrick', 'grandchildren': null, 'Employed?': true}, 'used', 'abused', 'confused', 1, 2, [3, 4, 5]]",
+ json.write(obj))
+
+def test_suite():
+ return TestSuite((
+ makeSuite(JsonTest),
+ ))
+
+if __name__ == '__main__':
+ main(defaultTest = 'test_suite')
Added: z3/jsonserver/trunk/meta.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/meta.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,20 @@
+<configure
+ xmlns:meta="http://namespaces.zope.org/meta"
+ xmlns="http://namespaces.zope.org/zope">
+
+ <!--
+
+ using xmlrpc's code for schema;
+ If we needed something slightly different, we would have
+ copied and altered that metadirectives.py file
+
+ -->
+
+ <meta:directive
+ namespace="http://namespaces.zope.org/jsonrpc"
+ name="view"
+ schema="zope.app.publisher.xmlrpc.metadirectives.IViewDirective"
+ handler=".metaconfigure.view" />
+
+ <meta:provides feature="jsonrpc" />
+</configure>
Added: z3/jsonserver/trunk/metaconfigure.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/metaconfigure.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,115 @@
+############################################################################
+##
+#
+# Copyright (c) 2001 - 2005 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.
+#
+############################################################################
+##
+"""JSON-RPC configuration code
+
+like zope.app.publisher.xmlrpc.metaconfigure
+
+updated 2005-12-03 Roger Ineichen
+jwashin 2005-06-06
+"""
+import zope.interface
+from zope.interface import Interface
+from zope.security.checker import CheckerPublic, Checker
+from zope.configuration.exceptions import ConfigurationError
+from interfaces import IJSONRPCRequest
+
+from zope.app.component.interface import provideInterface
+from zope.app.component.metaconfigure import handler
+from jsonrpc import MethodPublisher
+
+def view(_context, for_=None, interface=None, methods=None,
+ class_=None, permission=None, name=None):
+
+ interface = interface or []
+ methods = methods or []
+
+ # 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_ = MethodPublisher
+ original_class = class_
+ else:
+ original_class = class_
+ class_ = type(class_.__name__, (class_, MethodPublisher), {})
+
+ if name:
+ # Register a single 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
+
+ # Register the new view.
+ _context.action(
+ discriminator = ('view', for_, name, IJSONRPCRequest),
+ callable = handler,
+ args = ('provideAdapter',
+ (for_, IJSONRPCRequest), Interface, name, class_,
+ _context.info)
+ )
+ else:
+ if permission:
+ checker = Checker({'__call__': permission})
+ else:
+ checker = None
+
+ 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 = ('view', for_, name, IJSONRPCRequest),
+ callable = handler,
+ args = ('provideAdapter',
+ (for_, IJSONRPCRequest), Interface, name, new_class,
+ _context.info)
+ )
+
+ # Register the used interfaces with the site manager
+ if for_ is not None:
+ _context.action(
+ discriminator = None,
+ callable = provideInterface,
+ args = ('', for_)
+ )
Added: z3/jsonserver/trunk/minjson.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/minjson.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,436 @@
+##############################################################################
+#
+# Copyright (c) 2005 Jim Washington 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.
+#
+##############################################################################
+
+# minjson.py
+# reads minimal javascript objects.
+# str's objects and fixes the text to write javascript.
+
+#UNICODE USAGE: Minjson tries hard to accommodate naive usage in a
+#"Do what I mean" manner. Real applications should handle unicode separately.
+# The "right" way to use minjson in an application is to provide minjson a
+# python unicode string for reading and accept a unicode output from minjson's
+# writing. That way, the assumptions for unicode are yours and not minjson's.
+
+# That said, the minjson code has some (optional) unicode handling that you
+# may look at as a model for the unicode handling your application may need.
+
+# Thanks to Patrick Logan for starting the json-py project and making so many
+# good test cases.
+
+# Additional thanks to Balazs Ree for replacing the writing module.
+
+# Jim Washington 30 Dec 2005.
+
+# 2005-12-30 writing now traverses the object tree instead of relying on
+# str() or unicode()
+# 2005-10-10 on reading, looks for \\uxxxx and replaces with u'\uxxxx'
+# 2005-10-09 now tries hard to make all strings unicode when reading.
+# 2005-10-07 got rid of eval() completely, makes object as found by the
+# tokenizer.
+# 2005-09-06 imported parsing constants from tokenize; they changed a bit from
+# python2.3 to 2.4
+# 2005-08-22 replaced the read sanity code
+# 2005-08-21 Search for exploits on eval() yielded more default bad operators.
+# 2005-08-18 Added optional code from Koen van de Sande to escape
+# outgoing unicode chars above 128
+
+
+from re import compile, sub, search, DOTALL
+from token import ENDMARKER, NAME, NUMBER, STRING, OP, ERRORTOKEN
+from tokenize import tokenize, TokenError, NL
+
+#Usually, utf-8 will work, set this to utf-16 if you dare.
+emergencyEncoding = 'utf-8'
+
+class ReadException(Exception):
+ pass
+
+class WriteException(Exception):
+ pass
+
+#################################
+# read JSON object #
+#################################
+
+slashstarcomment = compile(r'/\*.*?\*/',DOTALL)
+doubleslashcomment = compile(r'//.*\n')
+
+unichrRE = compile(r"\\u[0-9a-fA-F]{4,4}")
+
+def unichrReplace(match):
+ return unichr(int(match.group()[2:],16))
+
+escapeStrs = (('\\','\\\\'),('\n',r'\n'),('\b',r'\b'),
+ ('\f',r'\f'),('\t',r'\t'),('\r',r'\r'), ('"',r'\"')
+ )
+
+class DictToken:
+ __slots__=[]
+ pass
+class ListToken:
+ __slots__=[]
+ pass
+class ColonToken:
+ __slots__=[]
+ pass
+class CommaToken:
+ __slots__=[]
+ pass
+
+class JSONReader(object):
+ """raise SyntaxError if it is not JSON, and make the object available"""
+ def __init__(self,data):
+ self.stop = False
+ #make an iterator of data so that next() works in tokenize.
+ self._data = iter([data])
+ self.lastOp = None
+ self.objects = []
+ self.tokenize()
+
+ def tokenize(self):
+ try:
+ tokenize(self._data.next,self.readTokens)
+ except TokenError:
+ raise SyntaxError
+
+ def resolveList(self):
+ #check for empty list
+ if isinstance(self.objects[-1],ListToken):
+ self.objects[-1] = []
+ return
+ theList = []
+ commaCount = 0
+ try:
+ item = self.objects.pop()
+ except IndexError:
+ raise SyntaxError
+ while not isinstance(item,ListToken):
+ if isinstance(item,CommaToken):
+ commaCount += 1
+ else:
+ theList.append(item)
+ try:
+ item = self.objects.pop()
+ except IndexError:
+ raise SyntaxError
+ if not commaCount == (len(theList) -1):
+ raise SyntaxError
+ theList.reverse()
+ item = theList
+ self.objects.append(item)
+
+ def resolveDict(self):
+ theList = []
+ #check for empty dict
+ if isinstance(self.objects[-1], DictToken):
+ self.objects[-1] = {}
+ return
+ #not empty; must have at least three values
+ try:
+ #value (we're going backwards!)
+ value = self.objects.pop()
+ except IndexError:
+ raise SyntaxError
+ try:
+ #colon
+ colon = self.objects.pop()
+ if not isinstance(colon, ColonToken):
+ raise SyntaxError
+ except IndexError:
+ raise SyntaxError
+ try:
+ #key
+ key = self.objects.pop()
+ if not isinstance(key,basestring):
+ raise SyntaxError
+ except IndexError:
+
+ raise SyntaxError
+ #salt the while
+ comma = value
+ while not isinstance(comma,DictToken):
+ # store the value
+ theList.append((key,value))
+ #do it again...
+ try:
+ #might be a comma
+ comma = self.objects.pop()
+ except IndexError:
+ raise SyntaxError
+ if isinstance(comma,CommaToken):
+ #if it's a comma, get the values
+ try:
+ value = self.objects.pop()
+ except IndexError:
+ #print self.objects
+ raise SyntaxError
+ try:
+ colon = self.objects.pop()
+ if not isinstance(colon, ColonToken):
+ raise SyntaxError
+ except IndexError:
+ raise SyntaxError
+ try:
+ key = self.objects.pop()
+ if not isinstance(key,basestring):
+ raise SyntaxError
+ except IndexError:
+ raise SyntaxError
+ theDict = {}
+ for k in theList:
+ theDict[k[0]] = k[1]
+ self.objects.append(theDict)
+
+ def readTokens(self,type, token, (srow, scol), (erow, ecol), line):
+ # UPPERCASE consts from tokens.py or tokenize.py
+ if type == OP:
+ if token not in "[{}],:-":
+ raise SyntaxError
+ else:
+ self.lastOp = token
+ if token == '[':
+ self.objects.append(ListToken())
+ elif token == '{':
+ self.objects.append(DictToken())
+ elif token == ']':
+ self.resolveList()
+ elif token == '}':
+ self.resolveDict()
+ elif token == ':':
+ self.objects.append(ColonToken())
+ elif token == ',':
+ self.objects.append(CommaToken())
+ elif type == STRING:
+ tok = token[1:-1]
+ for k in escapeStrs:
+ if k[1] in tok:
+ tok = tok.replace(k[1],k[0])
+ self.objects.append(tok)
+ elif type == NUMBER:
+ if self.lastOp == '-':
+ factor = -1
+ else:
+ factor = 1
+ try:
+ self.objects.append(factor * int(token))
+ except ValueError:
+ self.objects.append(factor * float(token))
+ elif type == NAME:
+ try:
+ self.objects.append({'true':True,
+ 'false':False,'null':None}[token])
+ except KeyError:
+ raise SyntaxError
+ elif type == ENDMARKER:
+ pass
+ elif type == NL:
+ pass
+ elif type == ERRORTOKEN:
+ if ecol == len(line):
+ #it's a char at the end of the line. (mostly) harmless.
+ pass
+ else:
+ raise SyntaxError
+ else:
+ raise SyntaxError
+ def output(self):
+ try:
+ assert len(self.objects) == 1
+ except AssertionError:
+ raise SyntaxError
+ return self.objects[0]
+
+def safeRead(aString, encoding=None):
+ """read the js, first sanitizing a bit and removing any c-style comments
+ If the input is a unicode string, great. That's preferred. If the input
+ is a byte string, strings in the object will be produced as unicode anyway.
+ """
+ # get rid of trailing null. Konqueror appends this.
+ CHR0 = chr(0)
+ while aString.endswith(CHR0):
+ aString = aString[:-1]
+ # strip leading and trailing whitespace
+ aString = aString.strip()
+ # zap /* ... */ comments
+ aString = slashstarcomment.sub('',aString)
+ # zap // comments
+ aString = doubleslashcomment.sub('',aString)
+ # detect and handle \\u unicode characters. Note: This has the side effect
+ # of converting the entire string to unicode. This is probably OK.
+ unicodechars = unichrRE.search(aString)
+ if unicodechars:
+ aString = unichrRE.sub(unichrReplace, aString)
+ #if it's already unicode, we won't try to decode it
+ if isinstance(aString, unicode):
+ s = aString
+ else:
+ if encoding:
+ # note: no "try" here. the encoding provided must work for the
+ # incoming byte string. UnicodeDecode error will be raised
+ # in that case. Often, it will be best not to provide the encoding
+ # and allow the default
+ s = unicode(aString, encoding)
+ #print "decoded %s from %s" % (s,encoding)
+ else:
+ # let's try to decode to unicode in system default encoding
+ try:
+ s = unicode(aString)
+ #import sys
+ #print "decoded %s from %s" % (s,sys.getdefaultencoding())
+ except UnicodeDecodeError:
+ # last choice: handle as emergencyEncoding
+ enc = emergencyEncoding
+ s = unicode(aString, enc)
+ #print "%s decoded from %s" % (s, enc)
+ # parse and get the object.
+ try:
+ data = JSONReader(s).output()
+ except SyntaxError:
+ raise ReadException, 'Unacceptable JSON expression: %s' % aString
+ return data
+
+read = safeRead
+
+#################################
+# write object as JSON #
+#################################
+
+import re, codecs
+from cStringIO import StringIO
+
+### Codec error handler
+
+def jsonreplace_handler(exc):
+ '''Error handler for json
+
+ If encoding fails, \\uxxxx must be emitted. This
+ is similar to the "backshashreplace" handler, only
+ that we never emit \\xnn since this is not legal
+ according to the JSON syntax specs.
+ '''
+ if isinstance(exc, UnicodeEncodeError):
+ part = exc.object[exc.start]
+ # repr(part) will convert u'\unnnn' to u'u\\nnnn'
+ return u'\\u%04x' % ord(part), exc.start+1
+ else:
+ raise exc
+
+# register the error handler
+codecs.register_error('jsonreplace', jsonreplace_handler)
+
+### Writer
+
+def write(input, encoding='utf-8', outputEncoding=None):
+ writer = JsonWriter(input_encoding=encoding, output_encoding=outputEncoding)
+ writer.write(input)
+ return writer.getvalue()
+
+re_strmangle = re.compile('"|\b|\f|\n|\r|\t|\\\\')
+
+def func_strmangle(match):
+ return {
+ '"': '\\"',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ '\\': '\\\\',
+ }[match.group(0)]
+
+def strmangle(text):
+ return re_strmangle.sub(func_strmangle, text)
+
+class JsonStream(object):
+
+ def __init__(self):
+ self.buf = []
+
+ def write(self, text):
+ self.buf.append(text)
+
+ def getvalue(self):
+ return ''.join(self.buf)
+
+class JsonWriter(object):
+
+ def __init__(self, stream=None, input_encoding='utf-8', output_encoding=None):
+ '''
+ - stream is optional, if specified must also give output_encoding
+ - The input strings can be unicode or in input_encoding
+ - output_encoding is optional, if omitted, result will be unicode
+ '''
+ if stream is not None:
+ if output_encoding is None:
+ raise WriteException, 'If a stream is given, output encoding must also be provided'
+ else:
+ stream = JsonStream()
+ self.stream = stream
+ self.input_encoding = input_encoding
+ self.output_encoding = output_encoding
+
+ def write(self, obj):
+ if isinstance(obj, (list, tuple)):
+ self.stream.write('[')
+ first = True
+ for elem in obj:
+ if first:
+ first = False
+ else:
+ self.stream.write(',')
+ self.write(elem)
+ self.stream.write(']'),
+ elif isinstance(obj, dict):
+ self.stream.write('{')
+ first = True
+ for key, value in obj.iteritems():
+ if first:
+ first = False
+ else:
+ self.stream.write(',')
+ self.write(key)
+ self.stream.write(':')
+ self.write(value)
+ self.stream.write('}')
+ elif obj is True:
+ self.stream.write('true')
+ elif obj is False:
+ self.stream.write('false')
+ elif obj is None:
+ self.stream.write('null')
+ elif not isinstance(obj, basestring):
+ # if we are not baseobj, convert to it
+ try:
+ obj = str(obj)
+ except Exception, exc:
+ raise WriteException, 'Cannot write object (%s: %s)' % (exc.__class__, exc)
+ self.stream.write(obj)
+ else:
+ # convert to unicode first
+ if not isinstance(obj, unicode):
+ try:
+ obj = unicode(obj, self.input_encoding)
+ except (UnicodeDecodeError, UnicodeTranslateError):
+ obj = unicode(obj, 'utf-8', 'replace')
+ # do the mangling
+ obj = strmangle(obj)
+ # make the encoding
+ if self.output_encoding is not None:
+ obj = obj.encode(self.output_encoding, 'jsonreplace')
+ self.stream.write('"')
+ self.stream.write(obj)
+ self.stream.write('"')
+
+ def getvalue(self):
+ return self.stream.getvalue()
Added: z3/jsonserver/trunk/requestpublicationfactory.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/requestpublicationfactory.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,21 @@
+# class for json-rpc publication factory.
+# like zope.app.publication.requestpublicationfactories, which
+# registers SOAPFactory, XMLRPCFactory, HTTPFactory, and BrowserFactory.
+# JSONRPCFactory is similar to those.
+
+from zope.app.publication.interfaces import IRequestPublicationFactory
+from interfaces import IJSONRPCRequestFactory
+from zope import component
+from jsonrpc import JSONRPCRequest, JSONRPCPublication
+from zope.interface import implements
+
+class JSONRPCFactory(object):
+ implements(IRequestPublicationFactory)
+
+ def canHandle(self,environment):
+ return True
+
+ def __call__(self):
+ request_class = component.queryUtility(
+ IJSONRPCRequestFactory, default=JSONRPCRequest)
+ return request_class, JSONRPCPublication
Added: z3/jsonserver/trunk/tests/__init__.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/__init__.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+#tests for json-rpc; now with WorryFree® :)
\ No newline at end of file
Added: z3/jsonserver/trunk/tests/jsonrpc.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/jsonrpc.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,55 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:jsonrpc="http://namespaces.zope.org/jsonrpc"
+ i18n_domain="zope">
+
+ <include package="jsonserver" file="meta.zcml"/>
+ <include package="zope.app.security" file="meta.zcml"/>
+
+ <jsonrpc:view
+ name="test"
+ class="zope.app.component.tests.views.V1"
+ permission="zope.Public"
+ for="zope.app.component.tests.views.IC"
+ />
+
+ <jsonrpc:view
+ name="test2"
+ class="zope.app.component.tests.views.V1"
+ for="zope.app.component.tests.views.IC"
+ permission="zope.Public"
+ interface="zope.app.component.tests.views.IV"
+ />
+
+ <jsonrpc:view
+ name="test3"
+ class=".jsonrpcviews.V1"
+ for="zope.app.component.tests.views.IC"
+ permission="zope.Public"
+ methods="action" />
+
+ <jsonrpc:view
+ name="test4"
+ class=".jsonrpcviews.V1"
+ for="zope.app.component.tests.views.IC"
+ permission="zope.Public"
+ methods="action"
+ interface="zope.app.component.tests.views.IV" />
+
+ <jsonrpc:view
+ name="test5"
+ class=".jsonrpcviews.V1"
+ for="zope.app.component.tests.views.IC"
+ permission="zope.Public"
+ methods="action index"
+ interface="zope.app.component.tests.views.IV" />
+
+
+ <jsonrpc:view
+ class=".jsonrpcviews.V1"
+ for="zope.app.component.tests.views.IC"
+ interface="zope.app.component.tests.views.IV"
+ permission="zope.Public"
+ methods="action"
+ />
+
+</configure>
Added: z3/jsonserver/trunk/tests/jsonrpc_error.zcml
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/jsonrpc_error.zcml Wed Mar 8 13:37:02 2006
@@ -0,0 +1,13 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:jsonrpc="http://namespaces.zope.org/jsonrpc"
+ i18n_domain="zope">
+
+ <include package="zope.app.publisher.jsonrpc" file="meta.zcml"/>
+
+ <jsonrpc:view
+ name="test"
+ factory="zope.component.tests.views.V1"
+ for="zope.component.tests.views.IC"
+ methods="action index" />
+
+</configure>
Added: z3/jsonserver/trunk/tests/jsonrpcviews.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/jsonrpcviews.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2001 - 2005 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.
+#
+##############################################################################
+"""JSON-RPC Views test objects
+
+adapted from zope.publisher.tests.xmlrpcviews
+jwashin 2005-06-06
+
+"""
+from zope.interface import Interface, implements
+from jsonserver.interfaces import IJSONRPCPublisher
+
+class IC(Interface): pass
+
+class V1(object):
+ implements(IJSONRPCPublisher)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+ def action(self):
+ return 'done'
+ def index(self):
+ return 'V1 here'
+
+class VZMI(V1):
+ pass
+
+class R1(object):
+ def __init__(self, request):
+ self.request = request
+
+ implements(IJSONRPCPublisher)
+
+class RZMI(R1):
+ pass
+
Added: z3/jsonserver/trunk/tests/test.pt
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test.pt Wed Mar 8 13:37:02 2006
@@ -0,0 +1 @@
+<html><body><p>test</p></body></html>
Added: z3/jsonserver/trunk/tests/test_directives.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test_directives.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,99 @@
+##############################################################################
+#
+# Copyright (c) 2001 - 2005 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.
+#
+##############################################################################
+"""Test 'jsonrpc' ZCML Namespace directives.
+
+mod from zope.app.publisher.xmlrpc.tests.test_directives.py jwashin 2005-06-06
+"""
+import unittest
+
+from zope.configuration import xmlconfig
+from zope.configuration.exceptions import ConfigurationError
+from zope.app.component.tests.views import IC, V1
+from zope.app.testing.placelesssetup import PlacelessSetup
+from zope.security.proxy import ProxyFactory
+
+from zope.component.tests.request import Request
+
+from jsonserver.interfaces import IJSONRPCRequest
+
+import jsonserver
+from jsonserver import jsonrpc
+from zope.interface import implements
+from zope.component import queryMultiAdapter, getMultiAdapter
+
+#Michael Dudzik wanted this for his test setup
+import jsonserver.tests
+
+request = Request(IJSONRPCRequest)
+
+class Ob(object):
+ implements(IC)
+
+ob = Ob()
+
+class DirectivesTest(PlacelessSetup, unittest.TestCase):
+
+ def testView(self):
+ self.assertEqual(
+ queryMultiAdapter((ob, request), name='test'), None)
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ view = queryMultiAdapter((ob, request), name='test')
+ self.assert_(V1 in view.__class__.__bases__)
+ self.assert_(jsonrpc.MethodPublisher in view.__class__.__bases__)
+
+ def testInterfaceProtectedView(self):
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ v = getMultiAdapter((ob, request), name='test2')
+ v = ProxyFactory(v)
+ self.assertEqual(v.index(), 'V1 here')
+ self.assertRaises(Exception, getattr, v, 'action')
+
+ def testAttributeProtectedView(self):
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ v = getMultiAdapter((ob, request), name='test3')
+ v = ProxyFactory(v)
+ self.assertEqual(v.action(), 'done')
+ self.assertRaises(Exception, getattr, v, 'index')
+
+ def testInterfaceAndAttributeProtectedView(self):
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ v = getMultiAdapter((ob, request), name='test4')
+ self.assertEqual(v.index(), 'V1 here')
+ self.assertEqual(v.action(), 'done')
+
+ def testDuplicatedInterfaceAndAttributeProtectedView(self):
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ v = getMultiAdapter((ob, request), name='test5')
+ self.assertEqual(v.index(), 'V1 here')
+ self.assertEqual(v.action(), 'done')
+
+ def testIncompleteProtectedViewNoPermission(self):
+ self.assertRaises(ConfigurationError, xmlconfig.file,
+ "jsonrpc_error.zcml", jsonserver.tests)
+
+ def test_no_name(self):
+ xmlconfig.file("jsonrpc.zcml", jsonserver.tests)
+ v = getMultiAdapter((ob, request), name='index')
+ self.assertEqual(v(), 'V1 here')
+ v = getMultiAdapter((ob, request), name='action')
+ self.assertEqual(v(), 'done')
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(DirectivesTest),
+ ))
+
+if __name__ == '__main__':
+ unittest.main()
Added: z3/jsonserver/trunk/tests/test_httpfactory.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test_httpfactory.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2003 - 2005 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.
+#
+##############################################################################
+"""Tests for the JSONRPC Publication Request Factory.
+
+modified from zope.app.publication.tests.test_requestpublicationfactories.py jwashin 2005-11-06
+
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+
+from StringIO import StringIO
+from zope import component,interface
+from zope.publisher.browser import BrowserRequest
+from zope.publisher.http import HTTPRequest
+from jsonserver.jsonrpc import JSONRPCRequest
+from zope.component.tests.placelesssetup import PlacelessSetup
+from jsonserver.interfaces import IJSONRPCRequestFactory
+from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
+from zope.app.publication.browser import BrowserPublication
+from zope.app.publication.http import HTTPPublication
+from jsonserver.requestpublicationfactory import JSONRPCFactory
+from jsonserver.jsonrpc import JSONRPCPublication
+
+from zope.app.testing import ztapi
+
+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, 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()
+ interface.directlyProvides(
+ jsonrpcrequestfactory, IJSONRPCRequestFactory)
+ 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 TestSuite((
+ makeSuite(Test),
+ ))
+
+if __name__=='__main__':
+ main(defaultTest='test_suite')
Added: z3/jsonserver/trunk/tests/test_json.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test_json.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,463 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2005 Jim Washington 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.
+#
+##############################################################################
+"""JSON Tests
+jwashin 2005-08-18
+"""
+import unittest
+
+from jsonserver import minjson as json
+from jsonserver.minjson import ReadException, WriteException
+
+def spaceless(aString):
+ return aString.replace(' ','')
+
+class JSONTests(unittest.TestCase):
+
+ def testReadString(self):
+ s = u"'hello'"
+ self.assertEqual(json.read(s) ,'hello')
+
+ def testWriteString(self):
+ s = 'hello'
+ self.assertEqual(json.write(s), '"hello"')
+
+ def testReadInt(self):
+ s = u"1"
+ self.assertEqual(json.read(s), 1)
+
+ def testWriteInt(self):
+ s = 1
+ self.assertEqual(json.write(s), "1")
+
+ def testReadLong(self):
+ s = u"999999999999999999999"
+ self.assertEqual(json.read(s), 999999999999999999999)
+
+ def testWriteShortLong(self):
+ s = 1L
+ self.assertEqual(json.write(s), "1")
+
+ def testWriteLongLong(self):
+ s = 999999999999999999999L
+ self.assertEqual(json.write(s), "999999999999999999999")
+
+ def testReadNegInt(self):
+ s = u"-1"
+ assert json.read(s) == -1
+
+ def testWriteNegInt(self):
+ s = -1
+ assert json.write(s) == '-1'
+
+ def testReadFloat(self):
+ s = u"1.334"
+ assert json.read(s) == 1.334
+
+ def testReadEFloat1(self):
+ s = u"1.334E2"
+ assert json.read(s) == 133.4
+
+ def testReadEFloat2(self):
+ s = u"1.334E-02"
+ assert json.read(s) == 0.01334
+
+ def testReadeFloat1(self):
+ s = u"1.334e2"
+ assert json.read(s) == 133.4
+
+ def testReadeFloat2(self):
+ s = u"1.334e-02"
+ assert json.read(s) == 0.01334
+
+ def testWriteFloat(self):
+ s = 1.334
+ assert json.write(s) == "1.334"
+
+ def testWriteDecimal(self):
+ try:
+ from decimal import Decimal
+ s = Decimal('1.33')
+ assert json.write(s) == "1.33"
+ except ImportError:
+ pass
+
+ def testReadNegFloat(self):
+ s = u"-1.334"
+ assert json.read(s) == -1.334
+
+ def testWriteNegFloat(self):
+ s = -1.334
+ assert json.write(s) == "-1.334"
+
+ def testReadEmptyDict(self):
+ s = u"{}"
+ assert json.read(s) == {}
+
+ def testWriteEmptyList(self):
+ s = []
+ assert json.write(s) == "[]"
+
+ def testWriteEmptyTuple(self):
+ s = ()
+ assert json.write(s) == "[]"
+
+ def testReadEmptyList(self):
+ s = u"[]"
+ assert json.read(s) == []
+
+ def testWriteEmptyDict(self):
+ s = {}
+ assert json.write(s) == '{}'
+
+ def testReadTrue(self):
+ s = u"true"
+ assert json.read(s) == True
+
+ def testWriteTrue(self):
+ s = True
+ assert json.write(s) == "true"
+
+ def testReadStringTrue(self):
+ s = u'"true"'
+ assert json.read(s) == 'true'
+
+ def testWriteStringTrue(self):
+ s = "True"
+ assert json.write(s) == '"True"'
+
+ def testReadStringNull(self):
+ s = u'"null"'
+ assert json.read(s) == 'null'
+
+ def testWriteStringNone(self):
+ s = "None"
+ assert json.write(s) == '"None"'
+
+ def testReadFalse(self):
+ s = u"false"
+ assert json.read(s) == False
+
+ def testWriteFalse(self):
+ s = False
+ assert json.write(s) == 'false'
+
+ def testReadNull(self):
+ s = u"null"
+ assert json.read(s) == None
+
+ def testWriteNone(self):
+ s = None
+ assert json.write(s) == "null"
+
+ def testReadDictOfLists(self):
+ s = u"{'a':[],'b':[]}"
+ assert json.read(s) == {'a':[],'b':[]}
+
+ def testReadDictOfListsWithSpaces(self):
+ s = u"{ 'a' : [], 'b' : [] } "
+ assert json.read(s) == {'a':[],'b':[]}
+
+ def testWriteDictOfLists(self):
+ s = {'a':[],'b':[]}
+ assert spaceless(json.write(s)) == '{"a":[],"b":[]}'
+
+ def testWriteDictOfTuples(self):
+ s = {'a':(),'b':()}
+ assert spaceless(json.write(s)) == '{"a":[],"b":[]}'
+
+ def testWriteDictWithNonemptyTuples(self):
+ s = {'a':('fred',7),'b':('mary',1.234)}
+ w = json.write(s)
+ assert spaceless(w) == '{"a":["fred",7],"b":["mary",1.234]}'
+
+ def testWriteVirtualTuple(self):
+ s = 4,4,5,6
+ w = json.write(s)
+ assert spaceless(w) == '[4,4,5,6]'
+
+ def testReadListOfDicts(self):
+ s = u"[{},{}]"
+ assert json.read(s) == [{},{}]
+
+ def testReadListOfDictsWithSpaces(self):
+ s = u" [ { } ,{ \n} ] "
+ assert json.read(s) == [{},{}]
+
+ def testWriteListOfDicts(self):
+ s = [{},{}]
+ assert spaceless(json.write(s)) == "[{},{}]"
+
+ def testWriteTupleOfDicts(self):
+ s = ({},{})
+ assert spaceless(json.write(s)) == "[{},{}]"
+
+ def testReadListOfStrings(self):
+ s = u"['a','b','c']"
+ assert json.read(s) == ['a','b','c']
+
+ def testReadListOfStringsWithSpaces(self):
+ s = u" ['a' ,'b' ,\n 'c'] "
+ assert json.read(s) == ['a','b','c']
+
+ def testReadStringWithWhiteSpace(self):
+ s = ur"'hello \tworld'"
+ assert json.read(s) == 'hello \tworld'
+
+ def testWriteMixedList(self):
+ o = ['OIL',34,199L,38.5]
+ assert spaceless(json.write(o)) == '["OIL",34,199,38.5]'
+
+ def testWriteMixedTuple(self):
+ o = ('OIL',34,199L,38.5)
+ assert spaceless(json.write(o)) == '["OIL",34,199,38.5]'
+
+ def testWriteStringWithWhiteSpace(self):
+ s = 'hello \tworld'
+ assert json.write(s) == r'"hello \tworld"'
+
+ def testWriteListofStringsWithApostrophes(self):
+ s = ["hasn't","don't","isn't",True,"won't"]
+ w = json.write(s)
+ assert spaceless(w) == '["hasn\'t","don\'t","isn\'t",true,"won\'t"]'
+
+ def testWriteTupleofStringsWithApostrophes(self):
+ s = ("hasn't","don't","isn't",True,"won't")
+ w = json.write(s)
+ assert spaceless(w) == '["hasn\'t","don\'t","isn\'t",true,"won\'t"]'
+
+ def testWriteListofStringsWithRandomQuoting(self):
+ s = ["hasn't","do\"n't","isn't",True,"wo\"n't"]
+ w = json.write(s)
+ assert "true" in w
+
+ def testWriteStringWithDoubleQuote(self):
+ s = "do\"nt"
+ w = json.write(s)
+ assert w == '"do\\\"nt"'
+
+ def testReadDictWithSlashStarComments(self):
+ s = """
+ {'a':false, /*don't want b
+ b:true, */
+ 'c':true
+ }
+ """
+ assert json.read(s) == {'a':False,'c':True}
+
+ def testReadDictWithTwoSlashStarComments(self):
+ s = """
+ {'a':false, /*don't want b
+ b:true, */
+ 'c':true,
+ 'd':false, /*don;t want e
+ e:true, */
+ 'f':true
+ }
+ """
+ assert json.read(s) == {'a':False,'c':True, 'd':False,'f':True}
+
+ def testReadDictWithDoubleSlashComments(self):
+ s = """
+ {'a':false,
+ // b:true, don't want b
+ 'c':true
+ }
+ """
+ assert json.read(s) == {'a':False,'c':True}
+
+ def testReadStringWithEscapedSingleQuote(self):
+ s = '"don\'t tread on me."'
+ assert json.read(s) == "don't tread on me."
+
+ def testWriteStringWithEscapedDoubleQuote(self):
+ s = 'he said, \"hi.'
+ t = json.write(s)
+ assert json.write(s) == '"he said, \\\"hi."'
+
+ def testReadStringWithEscapedDoubleQuote(self):
+ s = r'"She said, \"Hi.\""'
+ assert json.read(s) == 'She said, "Hi."'
+
+ def testReadStringWithNewLine(self):
+ s = r'"She said, \"Hi,\"\n to which he did not reply."'
+ assert json.read(s) == 'She said, "Hi,"\n to which he did not reply.'
+
+ def testReadNewLine(self):
+ s = r'"\n"'
+ assert json.read(s) == '\n'
+
+ def testWriteNewLine(self):
+ s = u'\n'
+ assert json.write(s) == r'"\n"'
+
+ def testWriteSimpleUnicode(self):
+ s = u'hello'
+ assert json.write(s) == '"hello"'
+
+ def testReadBackSlashuUnicode(self):
+ s = u'"\u0066"'
+ assert json.read(s) == 'f'
+
+ def testReadBackSlashuUnicodeInDictKey(self):
+ s = u'{"\u0066ather":34}'
+ assert json.read(s) == {'father':34}
+
+ def testReadDictKeyWithBackSlash(self):
+ s = u'{"mo\\use":22}'
+ self.assertEqual(json.read(s) , {r'mo\use':22})
+
+ def testWriteDictKeyWithBackSlash(self):
+ s = {"mo\\use":22}
+ self.assertEqual(json.write(s) , r'{"mo\\use":22}')
+
+ def testWriteListOfBackSlashuUnicodeStrings(self):
+ s = [u'\u20ac',u'\u20ac',u'\u20ac']
+ self.assertEqual(spaceless(json.write(s)) ,u'["\u20ac","\u20ac","\u20ac"]')
+
+ def testWriteUnicodeCharacter(self):
+ s = json.write(u'\u1001', 'ascii')
+ self.assertEqual(u'"\u1001"', s)
+
+ def testWriteUnicodeCharacter1(self):
+ s = json.write(u'\u1001', 'ascii',outputEncoding='ascii')
+ self.assertEqual(r'"\u1001"', s)
+
+ def testWriteHexUnicode(self):
+ s = unicode('\xff\xfe\xbf\x00Q\x00u\x00\xe9\x00 \x00p\x00a\x00s\x00a\x00?\x00','utf-16')
+ p = json.write(s, 'latin-1', outputEncoding="latin-1")
+ self.assertEqual(unicode(p,'latin-1'), u'"¿Qué pasa?"')
+
+ def testWriteHexUnicode1(self):
+ s = unicode('\xff\xfe\xbf\x00Q\x00u\x00\xe9\x00 \x00p\x00a\x00s\x00a\x00?\x00','utf-16')
+ p = json.write(s, 'latin-1')
+ self.assertEqual(p, u'"¿Qué pasa?"')
+
+ def testWriteDosPath(self):
+ s = 'c:\\windows\\system'
+ assert json.write(s) == r'"c:\\windows\\system"'
+
+ def testWriteDosPathInList(self):
+ s = ['c:\windows\system','c:\\windows\\system',r'c:\windows\system']
+ self.assertEqual(json.write(s) , r'["c:\\windows\\system","c:\\windows\\system","c:\\windows\\system"]')
+
+
+ def readImportExploit(self):
+ s = ur"\u000aimport('os').listdir('.')"
+ json.read(s)
+
+ def testImportExploit(self):
+ self.assertRaises(ReadException, self.readImportExploit)
+
+ def readClassExploit(self):
+ s = ur'''"__main__".__class__.__subclasses__()'''
+ json.read(s)
+
+ def testReadClassExploit(self):
+ self.assertRaises(ReadException, self.readClassExploit)
+
+ def readBadJson(self):
+ s = "'DOS'*30"
+ json.read(s)
+
+ def testReadBadJson(self):
+ self.assertRaises(ReadException, self.readBadJson)
+
+ def readUBadJson(self):
+ s = ur"\u0027DOS\u0027*30"
+ json.read(s)
+
+ def testReadUBadJson(self):
+ self.assertRaises(ReadException, self.readUBadJson)
+
+ def testReadEncodedUnicode(self):
+ obj = "'La Peña'"
+ r = json.read(obj, 'utf-8')
+ self.assertEqual(r, unicode('La Peña','utf-8'))
+
+ def testUnicodeFromNonUnicode(self):
+ obj = "'\u20ac'"
+ r = json.read(obj)
+ self.assertEqual(r, u'\u20ac')
+
+ def testUnicodeInObjectFromNonUnicode(self):
+ obj = "['\u20ac', '\u20acCESS', 'my\u20ACCESS']"
+ r = json.read(obj)
+ self.assertEqual(r, [u'\u20AC', u'\u20acCESS', u'my\u20acCESS'])
+
+ def testWriteWithEncoding(self):
+ obj = u'La Peña'
+ r = json.write(obj,'latin-1',outputEncoding='latin-1')
+ self.assertEqual(unicode(r,'latin-1'), u'"La Peña"')
+
+ def testWriteWithEncodingBaseCases(self):
+ #input_uni = u"'Ă�rvĂztĹąrĹ� tĂźkĂśrfĂşrĂłgĂŠp'"
+ input_uni = u'\xc1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p'
+ #print "input_uni is %s" % input_uni.encode('latin2')
+ # the result supposes doUxxxx = False
+ good_result = u'"\xc1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p"'
+ #
+ # from utf8
+ obj = input_uni.encode('utf-8')
+ r = json.write(obj, 'utf-8',outputEncoding='utf-8')
+ self.assertEqual(unicode(r,'utf-8'), good_result)
+ #
+ # from unicode
+ obj = input_uni
+ r = json.write(obj, outputEncoding='utf-8')
+ self.assertEqual(unicode(r,'utf-8'), good_result)
+ #
+ # from latin2
+ obj = input_uni.encode('latin2')
+ r = json.write(obj, 'latin2', outputEncoding='latin2')
+ self.assertEqual(unicode(r,'latin2'), good_result)
+ #
+ # from unicode, encoding is ignored
+ obj = input_uni
+ r = json.write(obj, 'latin2', outputEncoding='latin2')
+ self.assertEqual(unicode(r,'latin2'), good_result)
+ #
+ # same with composite types, uni
+ good_composite_result = \
+ u'["\xc1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p","\xc1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p"]'
+ #print "Good composite result = %s" % good_composite_result.encode('latin2')
+ obj = [input_uni, input_uni]
+ r = json.write(obj, outputEncoding='utf-8')
+ #print "r is %s, length is %s." % (r, len(r))
+ self.assertEqual(unicode(r,'utf-8'), good_composite_result)
+ #
+ # same with composite types, utf-8
+ obj = [input_uni.encode('utf-8'), input_uni.encode('utf-8')]
+ r = json.write(obj, 'utf-8')
+ # print unicode(r,'utf-8'), good_composite_result
+ #self.assertEqual(unicode(r,'utf-8'), good_composite_result)
+## #
+ #
+## # same with composite types, latin2
+ obj = [input_uni.encode('latin2'), input_uni.encode('latin2')]
+ r = json.write(obj, 'latin2')
+ #cannot assertEqual here, but the printed representation should be readable
+ #self.assertEqual(unicode(r,'latin2'), good_composite_result)
+ #
+## # same with composite types, mixed utf-8 with unicode
+ obj = [input_uni, input_uni.encode('utf-8')]
+ r = json.write(obj, 'utf-8')
+ #cannot assertEqual here, but the printed representation should be readable
+ #self.assertEqual(unicode(r,'utf-8'), good_composite_result)
+def test_suite():
+ loader = unittest.TestLoader()
+ return loader.loadTestsFromTestCase(JSONTests)
+
+if __name__=='__main__':
+ unittest.TextTestRunner().run(test_suite())
Added: z3/jsonserver/trunk/tests/test_jsonrpcpublication.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test_jsonrpcpublication.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,135 @@
+##############################################################################
+#
+# Copyright (c) 2001 - 2005 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.
+#
+##############################################################################
+"""JSON-RPC Publication Tests
+
+modified from zope.app.publication.tests.test_xmlrpcpublication.py jwashin 2005-06-06
+
+"""
+import unittest
+
+from zope.app.publication.tests.test_zopepublication import \
+ BasePublicationTests
+from zope.app.publication.traversers import TestTraverser
+from jsonserver.jsonrpc import JSONRPCPublication
+from zope.interface import Interface, implements
+from zope.proxy import removeAllProxies
+from zope.publisher.interfaces import NotFound
+from jsonserver.interfaces import IJSONRPCPresentation
+from jsonserver.interfaces import IJSONRPCRequest
+from jsonserver.interfaces import IJSONRPCPublisher
+from jsonserver.jsonrpc import TestRequest
+from zope.app.testing import ztapi
+
+class SimpleObject(object):
+ def __init__(self, v):
+ self.v = v
+
+
+
+class JSONRPCPublicationTests(BasePublicationTests):
+
+ klass = JSONRPCPublication
+
+ def _createRequest(self, path, publication, **kw):
+ request = TestRequest(PATH_INFO=path, **kw)
+ request.setPublication(publication)
+ return request
+
+ def testTraverseName(self):
+ pub = self.klass(self.db)
+ class C(object):
+ x = SimpleObject(1)
+ ob = C()
+ r = self._createRequest('/x', pub)
+ ztapi.provideView(None, IJSONRPCRequest, IJSONRPCPublisher,
+ '', TestTraverser)
+ ob2 = pub.traverseName(r, ob, 'x')
+ self.assertEqual(removeAllProxies(ob2).v, 1)
+
+ def testDenyDirectMethodAccess(self):
+ pub = self.klass(self.db)
+ class I(Interface):
+ pass
+
+ class C(object):
+ implements(I)
+
+ def foo(self):
+ return 'bar'
+
+ class V(object):
+ def __init__(self, context, request):
+ pass
+ implements(IJSONRPCPresentation)
+
+ ob = C()
+ r = self._createRequest('/foo', pub)
+
+ ztapi.provideView(I, IJSONRPCPresentation, Interface, 'view', V)
+ ztapi.setDefaultViewName(I, 'view', type=IJSONRPCPresentation)
+ self.assertRaises(NotFound, pub.traverseName, r, ob, 'foo')
+
+
+ def testTraverseNameView(self):
+ pub = self.klass(self.db)
+
+ class I(Interface):
+ pass
+
+ class C(object):
+ implements(I)
+
+ ob = C()
+
+ class V(object):
+ def __init__(self, context, request):
+ pass
+ implements(IJSONRPCPresentation)
+
+
+ # Register the simple traverser so we can traverse without @@
+ from jsonserver.jsonrpc import IJSONRPCPublisher
+ from jsonserver.interfaces import IJSONRPCRequest
+ from zope.app.publication.traversers import SimpleComponentTraverser
+ ztapi.provideView(Interface, IJSONRPCRequest, IJSONRPCPublisher, '',
+ SimpleComponentTraverser)
+
+ r = self._createRequest('/@@spam', pub)
+ ztapi.provideView(I, IJSONRPCRequest, Interface, 'spam', V)
+ ob2 = pub.traverseName(r, ob, '@@spam')
+ self.assertEqual(removeAllProxies(ob2).__class__, V)
+
+ ob2 = pub.traverseName(r, ob, 'spam')
+ self.assertEqual(removeAllProxies(ob2).__class__, V)
+
+
+ def testTraverseNameSiteManager(self):
+ pub = self.klass(self.db)
+ class C(object):
+ def getSiteManager(self):
+ return SimpleObject(1)
+ ob = C()
+ r = self._createRequest('/++etc++site',pub)
+ ob2 = pub.traverseName(r, ob, '++etc++site')
+ self.assertEqual(removeAllProxies(ob2).v, 1)
+
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(JSONRPCPublicationTests),
+ ))
+
+if __name__ == '__main__':
+ unittest.main()
Added: z3/jsonserver/trunk/tests/test_jsonrpcrequest.py
==============================================================================
--- (empty file)
+++ z3/jsonserver/trunk/tests/test_jsonrpcrequest.py Wed Mar 8 13:37:02 2006
@@ -0,0 +1,156 @@
+##############################################################################
+#
+# Copyright (c) 2001 - 2005 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.
+#
+##############################################################################
+"""JSON-RPC Request Tests
+modified from zope/publisher/tests/test_xmlrpcrequest.py jwashin 2005-06-06
+"""
+import unittest
+from StringIO import StringIO
+
+from zope.publisher.base import DefaultPublication
+from zope.publisher.http import HTTPCharsets
+from jsonserver.jsonrpc import JSONRPCRequest
+from zope.app.testing import ztapi
+from jsonserver.jsoncomponent import JSONReader, JSONWriter
+from jsonserver.interfaces import IJSONReader, IJSONWriter
+
+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 = JSONWriter().output({'id':'httpReq','method':'action','params':[1]})
+jsonrpc_call = TestCall()
+
+class ParamTestCall:
+ def __init__(self):
+ self.body = '{"id":"httpReq","method":"keyworded","params":[1,{"pythonKwMaRkEr":{"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):
+ 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`)
+
+ class Item2(object):
+ view = View()
+
+ self.app = AppRoot()
+ self.app.folder = Folder()
+ self.app.folder.item = Item()
+ self.app.folder.item2 = Item2()
+ ztapi.provideUtility(IJSONReader,JSONReader())
+
+ 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")
+
+