[Checkins] SVN: z3c.json/trunk/ Added initial implementation
Roger Ineichen
roger at projekt01.ch
Fri Nov 30 10:50:19 EST 2007
Log message for revision 82059:
Added initial implementation
Changed:
A z3c.json/trunk/CHANGES.txt
A z3c.json/trunk/README.txt
A z3c.json/trunk/TODO.txt
A z3c.json/trunk/bootstrap.py
A z3c.json/trunk/buildout.cfg
A z3c.json/trunk/setup.py
A z3c.json/trunk/src/z3c/__init__.py
A z3c.json/trunk/src/z3c/json/SETUP.cfg
A z3c.json/trunk/src/z3c/json/__init__.py
A z3c.json/trunk/src/z3c/json/configure.zcml
A z3c.json/trunk/src/z3c/json/converter.py
A z3c.json/trunk/src/z3c/json/exceptions.py
A z3c.json/trunk/src/z3c/json/interfaces.py
A z3c.json/trunk/src/z3c/json/minjson.py
A z3c.json/trunk/src/z3c/json/proxy.py
A z3c.json/trunk/src/z3c/json/tests.py
A z3c.json/trunk/src/z3c/json/transport.py
A z3c.json/trunk/src/z3c/json/z3c.json-configure.zcml
-=-
Added: z3c.json/trunk/CHANGES.txt
===================================================================
--- z3c.json/trunk/CHANGES.txt (rev 0)
+++ z3c.json/trunk/CHANGES.txt 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+Version 0.5.0 (unreleased)
+-------------------------
+
+- Initial Release
Property changes on: z3c.json/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/README.txt
===================================================================
--- z3c.json/trunk/README.txt (rev 0)
+++ z3c.json/trunk/README.txt 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,4 @@
+This package provides basic JSON components like JSON reader and writer
+utilities and a JSON-RPC client proxy including the transport implementation
+for Zope3.
+
\ No newline at end of file
Property changes on: z3c.json/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/TODO.txt
===================================================================
--- z3c.json/trunk/TODO.txt (rev 0)
+++ z3c.json/trunk/TODO.txt 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,8 @@
+====
+TODO
+====
+
+- add tests and use testing stub for fake the JSONRPC server response
+
+- improve coverage, write tests for all untested code, most of this is related
+ to error handling.
Property changes on: z3c.json/trunk/TODO.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/bootstrap.py
===================================================================
--- z3c.json/trunk/bootstrap.py (rev 0)
+++ z3c.json/trunk/bootstrap.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75940 2007-05-24 14:45:00Z srichter $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3c.json/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/buildout.cfg
===================================================================
--- z3c.json/trunk/buildout.cfg (rev 0)
+++ z3c.json/trunk/buildout.cfg 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,15 @@
+[buildout]
+develop = .
+parts = test checker coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.json [test]
+
+[checker]
+recipe = lovely.recipe:importchecker
+path = src/z3c/json
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
Added: z3c.json/trunk/setup.py
===================================================================
--- z3c.json/trunk/setup.py (rev 0)
+++ z3c.json/trunk/setup.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id:$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+ name='z3c.json',
+ version='0.5.0dev',
+ author = "Roger Ineichen and the Zope Community",
+ author_email = "zope3-dev at zope.org",
+ description = "Zope3 JSON base libraries used for z3c.jsonpage and z3c.jsonrpc",
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ license = "ZPL 2.1",
+ keywords = "zope3 z3c json base library",
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url = 'http://cheeseshop.python.org/pypi/z3c.json',
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['z3c'],
+ extras_require = dict(
+ test = [
+ 'z3c.coverage',
+ 'zope.app.testing',],
+ ),
+ install_requires = [
+ 'setuptools',
+ 'zope.component',
+ ],
+ zip_safe = False,
+)
Property changes on: z3c.json/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/__init__.py
===================================================================
--- z3c.json/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.json/trunk/src/z3c/__init__.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+ import pkg_resources
+ pkg_resources.declare_namespace(__name__)
+except ImportError:
+ import pkgutil
+ __path__ = pkgutil.extend_path(__path__, __name__)
Property changes on: z3c.json/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/SETUP.cfg
===================================================================
--- z3c.json/trunk/src/z3c/json/SETUP.cfg (rev 0)
+++ z3c.json/trunk/src/z3c/json/SETUP.cfg 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.json-*.zcml
+</data-files>
Added: z3c.json/trunk/src/z3c/json/__init__.py
===================================================================
--- z3c.json/trunk/src/z3c/json/__init__.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/__init__.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.json/trunk/src/z3c/json/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/configure.zcml
===================================================================
--- z3c.json/trunk/src/z3c/json/configure.zcml (rev 0)
+++ z3c.json/trunk/src/z3c/json/configure.zcml 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,35 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:i18n="http://namespaces.zope.org/i18n"
+ i18n_domain="z3c">
+
+ <!-- JSON reader and writer utility -->
+ <utility
+ factory=".converter.JSONReader"
+ provides=".interfaces.IJSONReader"
+ />
+
+ <utility
+ factory=".converter.JSONWriter"
+ provides=".interfaces.IJSONWriter"
+ />
+
+
+ <!-- marshaller -->
+ <adapter
+ for="list"
+ factory=".converter.ListPreMarshaller"
+ />
+
+ <adapter
+ for="tuple"
+ factory=".converter.ListPreMarshaller"
+ />
+
+ <adapter
+ factory=".converter.DictPreMarshaller"
+ for="dict"
+ />
+
+</configure>
Property changes on: z3c.json/trunk/src/z3c/json/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/converter.py
===================================================================
--- z3c.json/trunk/src/z3c/json/converter.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/converter.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,112 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+
+from z3c.json import interfaces
+from z3c.json import minjson
+from z3c.json import exceptions
+
+
+try:
+ import cjson
+ hasCJson = True
+except ImportError:
+ import logging
+ logger = logging.getLogger()
+ logger.log(logging.INFO,
+ "Using minjson only. cjson is much faster and available at the cheese "
+ "shop. easy_install python-cjson")
+ hasCJson = False
+
+
+class JSONReader(object):
+ """JSON reader utility."""
+ zope.interface.implements(interfaces.IJSONReader)
+
+ def read(self, aString, encoding=None):
+ if hasCJson:
+ try:
+ # the True parameter here tells cjson to make all strings
+ # unicode. This is a good idea here.
+ return cjson.decode(aString, True)
+ except cjson.DecodeError:
+ # fall back to minjson
+ pass
+ # This is a fall-back position for less-well-constructed JSON
+ try:
+ return minjson.read(aString, encoding)
+ except minjson.ReadException, e:
+ raise exceptions.ResponseError(e)
+
+
+class JSONWriter(object):
+ """JSON writer utility."""
+ zope.interface.implements(interfaces.IJSONWriter)
+
+ def write(self, anObject):
+ if hasCJson:
+ try:
+ return unicode(cjson.encode(anObject))
+ except cjson.EncodeError:
+ # fall back to minjson
+ pass
+ try:
+ return minjson.write(anObject)
+ except minjson.WriteException, e:
+ raise TypeError, e
+
+
+def premarshal(data):
+ """Premarshal data before handing it to JSON writer for marshalling
+
+ The initial purpose of this function is to remove security proxies
+ without resorting to removeSecurityProxy. This way, we can avoid
+ inadvertently providing access to data that should be protected.
+ """
+ premarshaller = interfaces.IJSONRPCPremarshaller(data, alternate=None)
+ if premarshaller is not None:
+ return premarshaller()
+ return data
+
+
+class PreMarshallerBase(object):
+ """Abstract base class for pre-marshallers."""
+ zope.interface.implements(interfaces.IJSONRPCPremarshaller)
+
+ def __init__(self, data):
+ self.data = data
+
+ def __call__(self):
+ raise Exception, "Not implemented"
+
+
+class DictPreMarshaller(PreMarshallerBase):
+ """Pre-marshaller for dicts"""
+
+ def __call__(self):
+ return dict([(premarshal(k), premarshal(v))
+ for (k, v) in self.data.items()])
+
+
+class ListPreMarshaller(PreMarshallerBase):
+ """Pre-marshaller for list"""
+
+ def __call__(self):
+ return map(premarshal, self.data)
+
Property changes on: z3c.json/trunk/src/z3c/json/converter.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/exceptions.py
===================================================================
--- z3c.json/trunk/src/z3c/json/exceptions.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/exceptions.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+
+class Error(Exception):
+ """Base class for client errors."""
+ def __str__(self):
+ return repr(self)
+
+
+class ProtocolError(Error):
+ """Indicates an HTTP protocol error.
+
+ Indicates an HTTP-level protocol error. This is raised by the HTTP
+ transport layer, if the server returns an error code other than 200
+ (OK).
+
+ @param url The target URL.
+ @param errcode The HTTP error code.
+ @param errmsg The HTTP error message.
+ @param headers The HTTP header dictionary.
+
+ """
+ def __init__(self, url, errcode, errmsg, headers):
+ Error.__init__(self)
+ self.url = url
+ self.errcode = errcode
+ self.errmsg = errmsg
+ self.headers = headers
+ def __repr__(self):
+ return (
+ "<ProtocolError for %s: %s %s>" %
+ (self.url, self.errcode, self.errmsg)
+ )
+
+
+class ResponseError(ValueError):
+ """Indicates a broken response package.
+
+ Indicates a broken JSON-RPC response package. This exception is
+ raised by the unmarshalling layer, if the JSON-RPC response is
+ malformed.
+
+ """
+ pass
+
+
+# TODO: implement this option if we implement __jsonclass__, ri
+#class Fault(Error):
+# """Indicates an JSON-RPC fault package.
+#
+# Indicates an JSON-RPC fault response package. This exception is
+# raised by the unmarshalling layer, if the JSON-RPC response contains
+# a fault string. This exception can also used as a class, to
+# generate a fault JSON-RPC message.
+#
+# @param faultCode The JSON-RPC fault code.
+# @param faultString The JSON-RPC fault string.
+#
+# """
+# def __init__(self, faultCode, faultString, **extra):
+# Error.__init__(self)
+# self.faultCode = faultCode
+# self.faultString = faultString
+# def __repr__(self):
+# return (
+# "<Fault %s: %s>" %
+# (self.faultCode, repr(self.faultString))
+# )
Property changes on: z3c.json/trunk/src/z3c/json/exceptions.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/interfaces.py
===================================================================
--- z3c.json/trunk/src/z3c/json/interfaces.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/interfaces.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+
+
+class IJSONReader(zope.interface.Interface):
+ """JSON reader."""
+
+ def read(aString):
+ """read and interpret a string in JSON as python"""
+
+
+class IJSONWriter(zope.interface.Interface):
+ """JSON writer."""
+
+ def write(anObject, encoding=None):
+ """return a JSON unicode string representation of a python object
+ Encode if encoding is provided.
+ """
+
+
+class IJSONRPCPremarshaller(zope.interface.Interface):
+ """Premarshaller to remove security proxies"""
+
+ def __call__():
+ """return the object without proxies"""
Property changes on: z3c.json/trunk/src/z3c/json/interfaces.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/minjson.py
===================================================================
--- z3c.json/trunk/src/z3c/json/minjson.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/minjson.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,441 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+
+# 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 6 Dec 2006.
+
+# 2006-12-06 Thanks to Koen van de Sande, now handles the case where someone
+# might want e.g., a literal "\n" in text not a new-line.
+# 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]
+ parts = tok.split("\\\\")
+ for k in escapeStrs:
+ if k[1] in tok:
+ parts = [part.replace(k[1],k[0]) for part in parts]
+ self.objects.append("\\".join(parts))
+ 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()
+
+
Property changes on: z3c.json/trunk/src/z3c/json/minjson.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/proxy.py
===================================================================
--- z3c.json/trunk/src/z3c/json/proxy.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/proxy.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,136 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import string
+import urllib
+import httplib
+import copy
+import base64
+import types
+import logging
+import socket
+
+import zope.component
+from z3c.json import interfaces
+from z3c.json.exceptions import ProtocolError
+from z3c.json.exceptions import ResponseError
+
+logger = logging.getLogger(__name__)
+
+
+class _Method(object):
+
+ def __init__(self, call, name, jsonId):
+ self.call = call
+ self.name = name
+ self.jsonId = jsonId
+
+ def __call__(self, *args, **kwargs):
+ request = {}
+ request['version'] = '1.1'
+ request['method'] = self.name
+ if len(kwargs) is not 0:
+ params = copy.copy(kwargs)
+ index = 0
+ for arg in args:
+ params[str(index)] = arg
+ index = index + 1
+ elif len(args) is not 0:
+ params = copy.copy(args)
+ else:
+ params = {}
+ request['params'] = params
+ # add our json id
+ request['id'] = self.jsonId
+ json = zope.component.getUtility(interfaces.IJSONWriter)
+ data = json.write(request)
+ try:
+ return self.call(data)
+ except socket.error, msg:
+ raise ResponseError("JSONRPC server connection error.")
+
+ def __getattr__(self, name):
+ return _Method(self.call, "%s.%s" % (self.name, name), self.jsonId)
+
+
+class JSONRPCProxy(object):
+ """JSON-RPC server proxy."""
+
+ def __init__(self, uri, transport=None, encoding=None,
+ verbose=None, jsonId=None):
+ utype, uri = urllib.splittype(uri)
+ if utype not in ("http", "https"):
+ raise IOError, "Unsupported JSONRPC protocol"
+ self.__host, self.__handler = urllib.splithost(uri)
+ if not self.__handler:
+ self.__handler = ""
+
+ if transport is None:
+ if utype == "https":
+ transport = SafeTransport()
+ else:
+ transport = Transport()
+ self.__transport = transport
+
+ self.__encoding = encoding
+ self.__verbose = verbose
+ self.jsonId = jsonId or u'jsonrpc'
+ self.error = None
+
+ def __request(self, request):
+ """call a method on the remote server.
+
+ This will raise a ResponseError or return the JSON result dict
+ """
+ # start the call
+ try:
+ response = self.__transport.request(self.__host, self.__handler,
+ request, verbose=self.__verbose)
+ except ResponseError, e:
+ # catch error message
+ self.error = unicode(e)
+ raise
+
+ if isinstance(response, int):
+ # that's just a status code response with no result
+ logger.error('Recieved status code %s' % response)
+
+ if len(response) == 3:
+ # that's a valid response format
+ if self.jsonId is not None and \
+ (self.jsonId != response.get('id')):
+ # different request id returned
+ raise ResponseError("Invalid request id returned")
+ if response.get('error'):
+ # error mesage in response
+ self.error = response['error']
+ raise ResponseError("Check proxy.error for error message")
+ else:
+ # only return the result if everything is fine
+ return response['result']
+
+ return response
+
+ def __getattr__(self, name):
+ """This let us call methods on remote server."""
+ return _Method(self.__request, name, self.jsonId)
+
+ def __repr__(self):
+ return ("<JSONProxy for %s%s>" % (self.__host, self.__handler))
+
+ __str__ = __repr__
Property changes on: z3c.json/trunk/src/z3c/json/proxy.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/tests.py
===================================================================
--- z3c.json/trunk/src/z3c/json/tests.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/tests.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,493 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+
+from z3c.json import minjson as json
+from z3c.json.minjson import ReadException
+from z3c.json.minjson import 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)
+
+ #these tests from Koen van der Sande; just in case we really want literal
+ # '\n' sent across the wire.
+
+ def testReadSpecialEscapedChars1(self):
+ test = r'"\\f"'
+ self.assertEqual([ord(x) for x in json.read(test)],[92,102])
+
+ def testReadSpecialEscapedChars2(self):
+ test = r'"\\a"'
+ self.assertEqual([ord(x) for x in json.read(test)],[92,97])
+
+ def testReadSpecialEscapedChars3(self):
+ test = r'"\\\\a"'
+ self.assertEqual([ord(x) for x in json.read(test)],[92,92,97])
+
+ def testReadSpecialEscapedChars4(self):
+ test = r'"\\\\b"'
+ self.assertEqual([ord(x) for x in json.read(test)],[92,92,98])
+
+ def testReadSpecialEscapedChars5(self):
+ test = r'"\\\n"'
+ self.assertEqual([ord(x) for x in json.read(test)],[92,10])
+
+
+def test_suite():
+ loader = unittest.TestLoader()
+ return loader.loadTestsFromTestCase(JSONTests)
+
+
+if __name__=='__main__':
+ unittest.TextTestRunner().run(test_suite())
Property changes on: z3c.json/trunk/src/z3c/json/tests.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/transport.py
===================================================================
--- z3c.json/trunk/src/z3c/json/transport.py (rev 0)
+++ z3c.json/trunk/src/z3c/json/transport.py 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1,257 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import string
+import urllib
+import httplib
+import base64
+import types
+
+import zope.component
+from z3c.json import interfaces
+from z3c.json.exceptions import ProtocolError
+from z3c.json.exceptions import ResponseError
+
+
+def getparser():
+ un = Unmarshaller()
+ par = Parser(un)
+ return par,un
+
+
+class Unmarshaller(object):
+ def __init__(self):
+ self.data = None
+
+ def feed(self, data):
+ if self.data is None:
+ self.data = data
+ else:
+ self.data = self.data + data
+
+ def close(self):
+ # convert to json, reader raises ResponseError on read error
+ json = zope.component.getUtility(interfaces.IJSONReader)
+ return json.read(self.data)
+
+
+class Parser(object):
+ def __init__(self, unmarshaller):
+ self._target = unmarshaller
+ self.data = None
+
+ def feed(self, data):
+ if self.data is None:
+ self.data = data
+ else:
+ self.data = self.data + data
+
+ def close(self):
+ self._target.feed(self.data)
+
+
+class Transport:
+ """Handles an HTTP transaction to an JSON-RPC server.
+
+ Standard transport class for JSON-RPC over HTTP.
+ You can create custom transports by subclassing this method, and
+ overriding selected methods.
+
+ Send a complete request, and parse the response.
+
+ @param host Target host.
+ @param handler Target PRC handler.
+ @param request_body JSON-RPC request body.
+ @param verbose Debugging flag.
+ @return Parsed response.
+
+ """
+
+ user_agent = "z3c.jsonrpc/0.5.0"
+
+ def request(self, host, handler, request_body, verbose=0):
+ # issue JSON-RPC request
+
+ h = self.make_connection(host)
+ if verbose:
+ h.set_debuglevel(1)
+
+ self.send_request(h, handler, request_body)
+ self.send_host(h, host)
+ self.send_user_agent(h)
+ self.send_content(h, request_body)
+
+ errcode, errmsg, headers = h.getreply()
+
+ if errcode != 200:
+ raise ProtocolError(host + handler, errcode, errmsg, headers)
+
+ self.verbose = verbose
+
+ try:
+ sock = h._conn.sock
+ except AttributeError:
+ sock = None
+
+ return self._parse_response(h.getfile(), sock)
+
+ def getparser(self):
+ """Get parser and unmarshaller."""
+ return getparser()
+
+ def get_host_info(self, host):
+ """Get authorization info from host parameter
+
+ Host may be a string, or a (host, x509-dict) tuple; if a string,
+ it is checked for a "user:pw at host" format, and a "Basic
+ Authentication" header is added if appropriate.
+
+ @param host Host descriptor (URL or (URL, x509 info) tuple).
+ @return A 3-tuple containing (actual host, extra headers,
+ x509 info). The header and x509 fields may be None.
+
+ """
+ x509 = {}
+ if isinstance(host, types.TupleType):
+ host, x509 = host
+
+ auth, host = urllib.splituser(host)
+
+ if auth:
+ auth = base64.encodestring(urllib.unquote(auth))
+ auth = string.join(string.split(auth), "") # get rid of whitespace
+ extra_headers = [
+ ("Authorization", "Basic " + auth)
+ ]
+ else:
+ extra_headers = None
+
+ return host, extra_headers, x509
+
+ def make_connection(self, host):
+ # create a HTTP connection object from a host descriptor
+ host, extra_headers, x509 = self.get_host_info(host)
+ return httplib.HTTP(host)
+
+ def send_request(self, connection, handler, request_body):
+ connection.putrequest("POST", handler)
+
+ def send_host(self, connection, host):
+ host, extra_headers, x509 = self.get_host_info(host)
+ connection.putheader("Host", host)
+ if extra_headers:
+ if isinstance(extra_headers, DictType):
+ extra_headers = extra_headers.items()
+ for key, value in extra_headers:
+ connection.putheader(key, value)
+
+ def send_user_agent(self, connection):
+ connection.putheader("User-Agent", self.user_agent)
+
+ def send_content(self, connection, request_body):
+ connection.putheader("Content-Type", "application/json")
+ connection.putheader("Content-Length", str(len(request_body)))
+ connection.endheaders()
+ if request_body:
+ connection.send(request_body)
+
+ def parse_response(self, file):
+ # compatibility interface
+ return self._parse_response(file, None)
+
+ def _parse_response(self, file, sock):
+ # read response from input file/socket, and parse it
+
+ p, u = self.getparser()
+
+ while 1:
+ if sock:
+ response = sock.recv(1024)
+ else:
+ response = file.read(1024)
+ if not response:
+ break
+ if self.verbose:
+ print "body:", repr(response)
+ p.feed(response)
+
+ file.close()
+ p.close()
+
+ return u.close()
+
+
+class SafeTransport(Transport):
+ """Handles an HTTPS transaction to an JSON-RPC server."""
+
+ def make_connection(self, host):
+ """Create a HTTPS connection object from a host descriptor
+
+ host may be a string, or a (host, x509-dict) tuple
+
+ """
+ host, extra_headers, x509 = self.get_host_info(host)
+ try:
+ HTTPS = httplib.HTTPS
+ except AttributeError:
+ raise NotImplementedError(
+ "your version of httplib doesn't support HTTPS"
+ )
+ else:
+ return HTTPS(host, None, **(x509 or {}))
+
+
+class BasicAuthTransport(Transport):
+
+ def __init__(self, username=None, password=None, verbose=0):
+ self.username=username
+ self.password=password
+ self.verbose=verbose
+
+ def request(self, host, handler, request_body, verbose=0):
+ # issue JSON-RPC request
+
+ self.verbose = verbose
+
+ h = httplib.HTTP(host)
+ h.putrequest("POST", handler)
+
+ # required by HTTP/1.1
+ h.putheader("Host", host)
+
+ # required by JSON-RPC
+ h.putheader("User-Agent", self.user_agent)
+ h.putheader("Content-Type", "application/json")
+ h.putheader("Content-Length", str(len(request_body)))
+
+ # basic auth
+ if self.username is not None and self.password is not None:
+ h.putheader("AUTHORIZATION", "Basic %s" %
+ base64.encodestring("%s:%s" % (self.username, self.password)
+ ).replace("\012", ""))
+ h.endheaders()
+
+ if request_body:
+ h.send(request_body)
+
+ errcode, errmsg, headers = h.getreply()
+
+ if errcode != 200:
+ raise ProtocolError(host + handler, errcode, errmsg, headers)
+
+ return self.parse_response(h.getfile())
Property changes on: z3c.json/trunk/src/z3c/json/transport.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.json/trunk/src/z3c/json/z3c.json-configure.zcml
===================================================================
--- z3c.json/trunk/src/z3c/json/z3c.json-configure.zcml (rev 0)
+++ z3c.json/trunk/src/z3c/json/z3c.json-configure.zcml 2007-11-30 15:50:19 UTC (rev 82059)
@@ -0,0 +1 @@
+<include package="z3c.json" />
Property changes on: z3c.json/trunk/src/z3c/json/z3c.json-configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list