git.fiddlerwoaroof.com
Browse code

rearranging for distutils

Ed L authored on 01/06/2011 01:58:48
Showing 17 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,31 @@
1
+Copyright (c) 2011 Edward Langley
2
+All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions
6
+are met:
7
+
8
+Redistributions of source code must retain the above copyright notice,
9
+this list of conditions and the following disclaimer.
10
+
11
+Redistributions in binary form must reproduce the above copyright
12
+notice, this list of conditions and the following disclaimer in the
13
+documentation and/or other materials provided with the distribution.
14
+
15
+Neither the name of the project's author nor the names of its
16
+contributors may be used to endorse or promote products derived from
17
+this software without specific prior written permission.
18
+
19
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
31
+
0 32
new file mode 100644
... ...
@@ -0,0 +1,50 @@
1
+A JSON-RPC 2.0 implementation for Python (Python 3 not supported yet)
2
+
3
+
4
+## Getting Started
5
+
6
+### Start the Server:
7
+
8
+    % python -m jsonrpc.example_server
9
+    Listening on port 8007...
10
+
11
+### Start the Client:
12
+  
13
+  Python 2.7: `python -m jsonrpc <host name>`
14
+  
15
+  Python 2.6: `python -m jsonrpc.__main__ <host name>`
16
+
17
+
18
+    >>> server.add(1, 2)
19
+    3
20
+
21
+    >>> server.subtract(3,2)
22
+    1
23
+
24
+    # Exceptions
25
+    >>> server.add(1, '2')
26
+    Traceback (most recent call last):
27
+      File "<stdin>", line 1, in <module>
28
+      File "jsonrpc/proxy.py", line 182, in __call__
29
+        raise JSONRPCException(resp['error'])
30
+    jsonrpc.proxy.JSONRPCException: unsupported operand type(s) for +: 'int' and 'unicode'
31
+
32
+
33
+    >>> server.batch_call( dict(add=( (1,2), {} ), subtract=( (3,2), {} )) )
34
+    [(3, None), (1, None)] # the pattern is (result, error)
35
+
36
+    # batch calls can also be done with an iterable, if you want
37
+    # to have more than one call to the same method
38
+    >>> server.batch_call( [('add', ((1, 2), {})), ('subtract', ((3, 2), {}))] )
39
+    [(3, None), (1, None)]
40
+
41
+    # Exceptions in batch calls
42
+    >>> server.batch_call( dict(add=( (1,2), {} ), subtract=( (3,'2'), {} )) )
43
+    [(3, None), (None, {u'message': u"unsupported operand type(s) for -: 'int' 
44
+      and 'unicode'", u'code': 0, u'data': [u"unsupported operand type(s) for -: 
45
+      'int' and 'unicode'"]})]
46
+
47
+
48
+
49
+Made for:
50
+    http://ncmi.bcm.edu
0 51
\ No newline at end of file
1 52
new file mode 100644
... ...
@@ -0,0 +1,35 @@
1
+# $Id: __init__.py,v 1.2 2011/05/20 10:15:33 irees Exp $
2
+
3
+#  
4
+#  Copyright (c) 2011 Edward Langley
5
+#  All rights reserved.
6
+#  
7
+#  Redistribution and use in source and binary forms, with or without
8
+#  modification, are permitted provided that the following conditions
9
+#  are met:
10
+#  
11
+#  Redistributions of source code must retain the above copyright notice,
12
+#  this list of conditions and the following disclaimer.
13
+#  
14
+#  Redistributions in binary form must reproduce the above copyright
15
+#  notice, this list of conditions and the following disclaimer in the
16
+#  documentation and/or other materials provided with the distribution.
17
+#  
18
+#  Neither the name of the project's author nor the names of its
19
+#  contributors may be used to endorse or promote products derived from
20
+#  this software without specific prior written permission.
21
+#  
22
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+#  
34
+#
35
+__version__ = "$Revision: 1.2 $".split(":")[1][:-1].strip()
0 36
new file mode 100644
... ...
@@ -0,0 +1,12 @@
1
+from . import proxy
2
+import optparse
3
+
4
+def main(host, path=None):
5
+	proxy_ = proxy.JSONRPCProxy(host, path)
6
+	return proxy_
7
+
8
+if __name__ == '__main__':
9
+	optionparser = optparse.OptionParser()
10
+	optionparser.add_option('-p', '--path', dest='path', help='path to the JSON-RPC server', default='/jsonrpc')
11
+	(options, args) = optionparser.parse_args()
12
+	server = main(args[0], options.path)
0 13
new file mode 100644
... ...
@@ -0,0 +1,161 @@
1
+# Makefile for Sphinx documentation
2
+#  
3
+#  Copyright (c) 2011 Edward Langley
4
+#  All rights reserved.
5
+#  
6
+#  Redistribution and use in source and binary forms, with or without
7
+#  modification, are permitted provided that the following conditions
8
+#  are met:
9
+#  
10
+#  Redistributions of source code must retain the above copyright notice,
11
+#  this list of conditions and the following disclaimer.
12
+#  
13
+#  Redistributions in binary form must reproduce the above copyright
14
+#  notice, this list of conditions and the following disclaimer in the
15
+#  documentation and/or other materials provided with the distribution.
16
+#  
17
+#  Neither the name of the project's author nor the names of its
18
+#  contributors may be used to endorse or promote products derived from
19
+#  this software without specific prior written permission.
20
+#  
21
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+#  
33
+#
34
+
35
+# You can set these variables from the command line.
36
+SPHINXOPTS    =
37
+SPHINXBUILD   = sphinx-build
38
+PAPER         =
39
+BUILDDIR      = build
40
+
41
+# Internal variables.
42
+PAPEROPT_a4     = -D latex_paper_size=a4
43
+PAPEROPT_letter = -D latex_paper_size=letter
44
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
45
+
46
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
47
+
48
+help:
49
+	@echo "Please use \`make <target>' where <target> is one of"
50
+	@echo "  html       to make standalone HTML files"
51
+	@echo "  dirhtml    to make HTML files named index.html in directories"
52
+	@echo "  singlehtml to make a single large HTML file"
53
+	@echo "  pickle     to make pickle files"
54
+	@echo "  json       to make JSON files"
55
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
56
+	@echo "  qthelp     to make HTML files and a qthelp project"
57
+	@echo "  devhelp    to make HTML files and a Devhelp project"
58
+	@echo "  epub       to make an epub"
59
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
60
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
61
+	@echo "  text       to make text files"
62
+	@echo "  man        to make manual pages"
63
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
64
+	@echo "  linkcheck  to check all external links for integrity"
65
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
66
+
67
+clean:
68
+	-rm -rf $(BUILDDIR)/*
69
+
70
+html:
71
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
72
+	@echo
73
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
74
+
75
+dirhtml:
76
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
77
+	@echo
78
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
79
+
80
+singlehtml:
81
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
82
+	@echo
83
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
84
+
85
+pickle:
86
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
87
+	@echo
88
+	@echo "Build finished; now you can process the pickle files."
89
+
90
+json:
91
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
92
+	@echo
93
+	@echo "Build finished; now you can process the JSON files."
94
+
95
+htmlhelp:
96
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
97
+	@echo
98
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
99
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
100
+
101
+qthelp:
102
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
103
+	@echo
104
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
105
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
106
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JSONRPC.qhcp"
107
+	@echo "To view the help file:"
108
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JSONRPC.qhc"
109
+
110
+devhelp:
111
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
112
+	@echo
113
+	@echo "Build finished."
114
+	@echo "To view the help file:"
115
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/JSONRPC"
116
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JSONRPC"
117
+	@echo "# devhelp"
118
+
119
+epub:
120
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
121
+	@echo
122
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
123
+
124
+latex:
125
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
126
+	@echo
127
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
128
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
129
+	      "(use \`make latexpdf' here to do that automatically)."
130
+
131
+latexpdf:
132
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
133
+	@echo "Running LaTeX files through pdflatex..."
134
+	make -C $(BUILDDIR)/latex all-pdf
135
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
136
+
137
+text:
138
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
139
+	@echo
140
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
141
+
142
+man:
143
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
144
+	@echo
145
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
146
+
147
+changes:
148
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
149
+	@echo
150
+	@echo "The overview file is in $(BUILDDIR)/changes."
151
+
152
+linkcheck:
153
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
154
+	@echo
155
+	@echo "Link check complete; look for any errors in the above output " \
156
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
157
+
158
+doctest:
159
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
160
+	@echo "Testing of doctests in the sources finished, look at the " \
161
+	      "results in $(BUILDDIR)/doctest/output.txt."
0 162
new file mode 100644
... ...
@@ -0,0 +1,256 @@
1
+# -*- coding: utf-8 -*-
2
+#  
3
+#  Copyright (c) 2011 Edward Langley
4
+#  All rights reserved.
5
+#  
6
+#  Redistribution and use in source and binary forms, with or without
7
+#  modification, are permitted provided that the following conditions
8
+#  are met:
9
+#  
10
+#  Redistributions of source code must retain the above copyright notice,
11
+#  this list of conditions and the following disclaimer.
12
+#  
13
+#  Redistributions in binary form must reproduce the above copyright
14
+#  notice, this list of conditions and the following disclaimer in the
15
+#  documentation and/or other materials provided with the distribution.
16
+#  
17
+#  Neither the name of the project's author nor the names of its
18
+#  contributors may be used to endorse or promote products derived from
19
+#  this software without specific prior written permission.
20
+#  
21
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+#  
33
+#
34
+
35
+#
36
+# JSONRPC documentation build configuration file, created by
37
+# sphinx-quickstart on Mon May 23 20:36:05 2011.
38
+#
39
+# This file is execfile()d with the current directory set to its containing dir.
40
+#
41
+# Note that not all possible configuration values are present in this
42
+# autogenerated file.
43
+#
44
+# All configuration values have a default; values that are commented out
45
+# serve to show the default.
46
+
47
+import sys, os
48
+
49
+# If extensions (or modules to document with autodoc) are in another directory,
50
+# add these directories to sys.path here. If the directory is relative to the
51
+# documentation root, use os.path.abspath to make it absolute, like shown here.
52
+sys.path.insert(0, os.path.abspath('../../..'))
53
+
54
+# -- General configuration -----------------------------------------------------
55
+
56
+# If your documentation needs a minimal Sphinx version, state it here.
57
+#needs_sphinx = '1.0'
58
+
59
+# Add any Sphinx extension module names here, as strings. They can be extensions
60
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
61
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode']
62
+
63
+autoclass_content = 'both'
64
+autodoc_member_order = 'source'
65
+
66
+# Add any paths that contain templates here, relative to this directory.
67
+templates_path = ['_templates']
68
+
69
+# The suffix of source filenames.
70
+source_suffix = '.rst'
71
+
72
+# The encoding of source files.
73
+#source_encoding = 'utf-8-sig'
74
+
75
+# The master toctree document.
76
+master_doc = 'index'
77
+
78
+# General information about the project.
79
+project = u'JSONRPC'
80
+copyright = u'2011, Edward Langley'
81
+
82
+# The version info for the project you're documenting, acts as replacement for
83
+# |version| and |release|, also used in various other places throughout the
84
+# built documents.
85
+#
86
+# The short X.Y version.
87
+version = '1.0'
88
+# The full version, including alpha/beta/rc tags.
89
+release = '1.0'
90
+
91
+# The language for content autogenerated by Sphinx. Refer to documentation
92
+# for a list of supported languages.
93
+#language = None
94
+
95
+# There are two options for replacing |today|: either, you set today to some
96
+# non-false value, then it is used:
97
+#today = ''
98
+# Else, today_fmt is used as the format for a strftime call.
99
+#today_fmt = '%B %d, %Y'
100
+
101
+# List of patterns, relative to source directory, that match files and
102
+# directories to ignore when looking for source files.
103
+exclude_patterns = []
104
+
105
+# The reST default role (used for this markup: `text`) to use for all documents.
106
+#default_role = None
107
+
108
+# If true, '()' will be appended to :func: etc. cross-reference text.
109
+#add_function_parentheses = True
110
+
111
+# If true, the current module name will be prepended to all description
112
+# unit titles (such as .. function::).
113
+#add_module_names = True
114
+
115
+# If true, sectionauthor and moduleauthor directives will be shown in the
116
+# output. They are ignored by default.
117
+#show_authors = False
118
+
119
+# The name of the Pygments (syntax highlighting) style to use.
120
+pygments_style = 'sphinx'
121
+
122
+# A list of ignored prefixes for module index sorting.
123
+#modindex_common_prefix = []
124
+
125
+
126
+# -- Options for HTML output ---------------------------------------------------
127
+
128
+# The theme to use for HTML and HTML Help pages.  See the documentation for
129
+# a list of builtin themes.
130
+html_theme = 'default'
131
+
132
+# Theme options are theme-specific and customize the look and feel of a theme
133
+# further.  For a list of options available for each theme, see the
134
+# documentation.
135
+#html_theme_options = {}
136
+
137
+# Add any paths that contain custom themes here, relative to this directory.
138
+#html_theme_path = []
139
+
140
+# The name for this set of Sphinx documents.  If None, it defaults to
141
+# "<project> v<release> documentation".
142
+#html_title = None
143
+
144
+# A shorter title for the navigation bar.  Default is the same as html_title.
145
+#html_short_title = None
146
+
147
+# The name of an image file (relative to this directory) to place at the top
148
+# of the sidebar.
149
+#html_logo = None
150
+
151
+# The name of an image file (within the static path) to use as favicon of the
152
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
153
+# pixels large.
154
+#html_favicon = None
155
+
156
+# Add any paths that contain custom static files (such as style sheets) here,
157
+# relative to this directory. They are copied after the builtin static files,
158
+# so a file named "default.css" will overwrite the builtin "default.css".
159
+html_static_path = ['_static']
160
+
161
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
162
+# using the given strftime format.
163
+#html_last_updated_fmt = '%b %d, %Y'
164
+
165
+# If true, SmartyPants will be used to convert quotes and dashes to
166
+# typographically correct entities.
167
+#html_use_smartypants = True
168
+
169
+# Custom sidebar templates, maps document names to template names.
170
+#html_sidebars = {}
171
+
172
+# Additional templates that should be rendered to pages, maps page names to
173
+# template names.
174
+#html_additional_pages = {}
175
+
176
+# If false, no module index is generated.
177
+#html_domain_indices = True
178
+
179
+# If false, no index is generated.
180
+#html_use_index = True
181
+
182
+# If true, the index is split into individual pages for each letter.
183
+#html_split_index = False
184
+
185
+# If true, links to the reST sources are added to the pages.
186
+#html_show_sourcelink = True
187
+
188
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
189
+#html_show_sphinx = True
190
+
191
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
192
+#html_show_copyright = True
193
+
194
+# If true, an OpenSearch description file will be output, and all pages will
195
+# contain a <link> tag referring to it.  The value of this option must be the
196
+# base URL from which the finished HTML is served.
197
+#html_use_opensearch = ''
198
+
199
+# This is the file name suffix for HTML files (e.g. ".xhtml").
200
+#html_file_suffix = None
201
+
202
+# Output file base name for HTML help builder.
203
+htmlhelp_basename = 'JSONRPCdoc'
204
+
205
+
206
+# -- Options for LaTeX output --------------------------------------------------
207
+
208
+# The paper size ('letter' or 'a4').
209
+#latex_paper_size = 'letter'
210
+
211
+# The font size ('10pt', '11pt' or '12pt').
212
+#latex_font_size = '10pt'
213
+
214
+# Grouping the document tree into LaTeX files. List of tuples
215
+# (source start file, target name, title, author, documentclass [howto/manual]).
216
+latex_documents = [
217
+  ('index', 'JSONRPC.tex', u'JSONRPC Documentation',
218
+   u'Edward Langley', 'manual'),
219
+]
220
+
221
+# The name of an image file (relative to this directory) to place at the top of
222
+# the title page.
223
+#latex_logo = None
224
+
225
+# For "manual" documents, if this is true, then toplevel headings are parts,
226
+# not chapters.
227
+#latex_use_parts = False
228
+
229
+# If true, show page references after internal links.
230
+#latex_show_pagerefs = False
231
+
232
+# If true, show URL addresses after external links.
233
+#latex_show_urls = False
234
+
235
+# Additional stuff for the LaTeX preamble.
236
+#latex_preamble = ''
237
+
238
+# Documents to append as an appendix to all manuals.
239
+#latex_appendices = []
240
+
241
+# If false, no module index is generated.
242
+#latex_domain_indices = True
243
+
244
+
245
+# -- Options for manual page output --------------------------------------------
246
+
247
+# One entry per manual page. List of tuples
248
+# (source start file, name, description, authors, manual section).
249
+man_pages = [
250
+    ('index', 'jsonrpc', u'JSONRPC Documentation',
251
+     [u'Edward Langley'], 1)
252
+]
253
+
254
+
255
+# Example configuration for intersphinx: refer to the Python standard library.
256
+intersphinx_mapping = {'python':('http://docs.python.org/2.7', None)}
0 257
new file mode 100644
... ...
@@ -0,0 +1,57 @@
1
+.. JSONRPC documentation master file, created by
2
+   sphinx-quickstart on Mon May 23 20:36:05 2011.
3
+   You can adapt this file completely to your liking, but it should at least
4
+   contain the root `toctree` directive.
5
+
6
+ 
7
+.. Copyright (c) 2011 Edward Langley
8
+   All rights reserved.
9
+   
10
+   Redistribution and use in source and binary forms, with or without
11
+   modification, are permitted provided that the following conditions
12
+   are met:
13
+   
14
+   Redistributions of source code must retain the above copyright notice,
15
+   this list of conditions and the following disclaimer.
16
+   
17
+   Redistributions in binary form must reproduce the above copyright
18
+   notice, this list of conditions and the following disclaimer in the
19
+   documentation and/or other materials provided with the distribution.
20
+   
21
+   Neither the name of the project's author nor the names of its
22
+   contributors may be used to endorse or promote products derived from
23
+   this software without specific prior written permission.
24
+   
25
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
31
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ 
37
+
38
+
39
+Welcome to JSONRPC's documentation!
40
+===================================
41
+
42
+Contents:
43
+
44
+.. toctree::
45
+   :maxdepth: 2
46
+
47
+   server
48
+   proxy
49
+   jsonutil
50
+
51
+Indices and tables
52
+==================
53
+
54
+* :ref:`genindex`
55
+* :ref:`modindex`
56
+* :ref:`search`
57
+
0 58
new file mode 100644
... ...
@@ -0,0 +1,42 @@
1
+.. Copyright (c) 2011 Edward Langley
2
+   All rights reserved.
3
+   
4
+   Redistribution and use in source and binary forms, with or without
5
+   modification, are permitted provided that the following conditions
6
+   are met:
7
+   
8
+   Redistributions of source code must retain the above copyright notice,
9
+   this list of conditions and the following disclaimer.
10
+   
11
+   Redistributions in binary form must reproduce the above copyright
12
+   notice, this list of conditions and the following disclaimer in the
13
+   documentation and/or other materials provided with the distribution.
14
+   
15
+   Neither the name of the project's author nor the names of its
16
+   contributors may be used to endorse or promote products derived from
17
+   this software without specific prior written permission.
18
+   
19
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ 
31
+jsonrpc.jsonutil
32
+================
33
+.. automodule:: jsonrpc.jsonutil
34
+
35
+.. py:function:: encode(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding='utf-8', **kw)
36
+
37
+   Serialize obj to json, if it is not of a type which the encoder can handle,
38
+   make it the proper type. Args and kw are as in json.dumps
39
+
40
+.. py:function:: decode(str, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, **kw)
41
+
42
+   Return an object from a json string.  This is just :py:func:`json.loads` renamed
0 43
new file mode 100644
... ...
@@ -0,0 +1,35 @@
1
+.. Copyright (c) 2011 Edward Langley
2
+   All rights reserved.
3
+   
4
+   Redistribution and use in source and binary forms, with or without
5
+   modification, are permitted provided that the following conditions
6
+   are met:
7
+   
8
+   Redistributions of source code must retain the above copyright notice,
9
+   this list of conditions and the following disclaimer.
10
+   
11
+   Redistributions in binary form must reproduce the above copyright
12
+   notice, this list of conditions and the following disclaimer in the
13
+   documentation and/or other materials provided with the distribution.
14
+   
15
+   Neither the name of the project's author nor the names of its
16
+   contributors may be used to endorse or promote products derived from
17
+   this software without specific prior written permission.
18
+   
19
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ 
31
+JSON-RPC Proxy
32
+==============
33
+
34
+.. automodule:: jsonrpc.proxy
35
+   :members:
0 36
new file mode 100644
... ...
@@ -0,0 +1,36 @@
1
+.. Copyright (c) 2011 Edward Langley
2
+   All rights reserved.
3
+   
4
+   Redistribution and use in source and binary forms, with or without
5
+   modification, are permitted provided that the following conditions
6
+   are met:
7
+   
8
+   Redistributions of source code must retain the above copyright notice,
9
+   this list of conditions and the following disclaimer.
10
+   
11
+   Redistributions in binary form must reproduce the above copyright
12
+   notice, this list of conditions and the following disclaimer in the
13
+   documentation and/or other materials provided with the distribution.
14
+   
15
+   Neither the name of the project's author nor the names of its
16
+   contributors may be used to endorse or promote products derived from
17
+   this software without specific prior written permission.
18
+   
19
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ 
31
+JSON-RPC Server
32
+===============
33
+
34
+.. automodule:: jsonrpc.server
35
+   :members:
36
+
0 37
new file mode 100644
... ...
@@ -0,0 +1,58 @@
1
+#
2
+#  Copyright (c) 2011 Edward Langley
3
+#  All rights reserved.
4
+#
5
+#  Redistribution and use in source and binary forms, with or without
6
+#  modification, are permitted provided that the following conditions
7
+#  are met:
8
+#
9
+#  Redistributions of source code must retain the above copyright notice,
10
+#  this list of conditions and the following disclaimer.
11
+#
12
+#  Redistributions in binary form must reproduce the above copyright
13
+#  notice, this list of conditions and the following disclaimer in the
14
+#  documentation and/or other materials provided with the distribution.
15
+#
16
+#  Neither the name of the project's author nor the names of its
17
+#  contributors may be used to endorse or promote products derived from
18
+#  this software without specific prior written permission.
19
+#
20
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+#
32
+#
33
+from twisted.internet import reactor, ssl
34
+from twisted.web import server
35
+
36
+from .server import ServerEvents, JSON_RPC
37
+
38
+class JSONRPCTest(ServerEvents):
39
+	def callmethod(self, request, method, kwargs, args, **kw):
40
+		if method in set(['add', 'subtract']):
41
+			return getattr(self, method)(*args, **kwargs)
42
+
43
+	def subtract(self, a, b):
44
+		return a-b
45
+
46
+	def add(self, a, b):
47
+		return a+b
48
+
49
+root = JSON_RPC().customize(JSONRPCTest)
50
+site = server.Site(root)
51
+
52
+
53
+# 8007 is the port you want to run under. Choose something >1024
54
+reactor.listenTCP(8007, site)
55
+reactor.run()
56
+
57
+
58
+__version__ = "$Revision: 1.8 $".split(":")[1][:-1].strip()
0 59
new file mode 100644
... ...
@@ -0,0 +1,88 @@
1
+# $Id: jsonutil.py,v 1.2 2011/05/26 19:34:19 edwlan Exp $
2
+
3
+#  
4
+#  Copyright (c) 2011 Edward Langley
5
+#  All rights reserved.
6
+#  
7
+#  Redistribution and use in source and binary forms, with or without
8
+#  modification, are permitted provided that the following conditions
9
+#  are met:
10
+#  
11
+#  Redistributions of source code must retain the above copyright notice,
12
+#  this list of conditions and the following disclaimer.
13
+#  
14
+#  Redistributions in binary form must reproduce the above copyright
15
+#  notice, this list of conditions and the following disclaimer in the
16
+#  documentation and/or other materials provided with the distribution.
17
+#  
18
+#  Neither the name of the project's author nor the names of its
19
+#  contributors may be used to endorse or promote products derived from
20
+#  this software without specific prior written permission.
21
+#  
22
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+#  
34
+#  
35
+
36
+"""
37
+This module is primarily a wrapper around simplejson in order to make
38
+it behave like demjson
39
+
40
+- If an object being encoded has a 'json_equivalent' attribute, that will be called to get a (more)
41
+	serializable object
42
+
43
+- if it has an 'items' method, it will be called
44
+	- if it defines both items and iteritems, the second will be used)
45
+
46
+- if it is iterable, it will be made into a list
47
+
48
+- otherwise 'str' will be called on the object, and that result will be used
49
+"""
50
+__all__ = ['encode', 'decode']
51
+
52
+import functools
53
+
54
+try:
55
+	import json
56
+except ImportError:
57
+	import simplejson as json
58
+
59
+
60
+def dict_encode(obj):
61
+	items = getattr(obj, 'iteritems', obj.items)
62
+	return dict( (encode_(k),encode_(v)) for k,v in items() )
63
+
64
+def list_encode(obj):
65
+	return list(encode_(i) for i in obj)
66
+
67
+def safe_encode(obj):
68
+	'''Always return something, even if it is useless for serialization'''
69
+	try: json.dumps(obj)
70
+	except TypeError: obj = str(obj)
71
+	return obj
72
+
73
+def encode_(obj, **kw):
74
+	obj = getattr(obj, 'json_equivalent', lambda: obj)()
75
+	func = lambda x: x
76
+	if hasattr(obj, 'items'):
77
+		func = dict_encode
78
+	elif hasattr(obj, '__iter__'):
79
+		func = list_encode
80
+	else:
81
+		func = safe_encode
82
+	return func(obj)
83
+
84
+
85
+encode = functools.partial(json.dumps, default=encode_)
86
+decode = json.loads
87
+
88
+__version__ = "$Revision: 1.2 $".split(":")[1][:-1].strip()
0 89
new file mode 100644
... ...
@@ -0,0 +1,210 @@
1
+# $Id: proxy.py,v 1.20 2011/05/26 20:19:17 edwlan Exp $
2
+
3
+#  
4
+#  Copyright (c) 2011 Edward Langley
5
+#  All rights reserved.
6
+#  
7
+#  Redistribution and use in source and binary forms, with or without
8
+#  modification, are permitted provided that the following conditions
9
+#  are met:
10
+#  
11
+#  Redistributions of source code must retain the above copyright notice,
12
+#  this list of conditions and the following disclaimer.
13
+#  
14
+#  Redistributions in binary form must reproduce the above copyright
15
+#  notice, this list of conditions and the following disclaimer in the
16
+#  documentation and/or other materials provided with the distribution.
17
+#  
18
+#  Neither the name of the project's author nor the names of its
19
+#  contributors may be used to endorse or promote products derived from
20
+#  this software without specific prior written permission.
21
+#  
22
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+#  
34
+#
35
+import copy
36
+import urllib
37
+import urlparse
38
+import itertools
39
+import traceback
40
+import random
41
+import time
42
+import UserDict, collections
43
+collections.Mapping.register(UserDict.DictMixin)
44
+
45
+from hashlib import sha1
46
+import jsonrpc.jsonutil
47
+
48
+__all__ = ['JSONRPCProxy', 'ProxyEvents']
49
+
50
+class NewStyleBaseException(Exception):
51
+    def _get_message(self):
52
+        return self._message
53
+    def _set_message(self, message):
54
+        self._message = message
55
+
56
+    message = property(_get_message, _set_message)
57
+
58
+
59
+class JSONRPCException(NewStyleBaseException):
60
+	def __init__(self, rpcError):
61
+		Exception.__init__(self, rpcError.get('message'))
62
+		self.data = rpcError.get('data')
63
+		self.message = rpcError.get('message')
64
+		self.code = rpcError.get('code')
65
+
66
+class IDGen(object):
67
+	def __init__(self):
68
+		self._hasher = sha1()
69
+		self._id = 0
70
+	def __get__(self, *_, **__):
71
+		self._id += 1
72
+		self._hasher.update(str(self._id))
73
+		self._hasher.update(time.ctime())
74
+		self._hasher.update(str(random.random))
75
+		return self._hasher.hexdigest()
76
+
77
+
78
+
79
+class ProxyEvents(object):
80
+	'''An event handler for JSONRPCProxy'''
81
+
82
+	#: an instance of a class which defines a __get__ method, used to generate a request id
83
+	IDGen = IDGen()
84
+
85
+
86
+	def __init__(self, proxy):
87
+		'''Allow a subclass to do its own initialization, gets any arguments leftover from __init__'''
88
+		self.proxy = proxy
89
+
90
+	def get_postdata(self, args, kwargs):
91
+		'''allow a subclass to modify the method's arguments
92
+
93
+		e.g. if an authentication token is necessary, the subclass can automatically insert it into every call'''
94
+		return args, kwargs
95
+
96
+	def proc_response(self, data):
97
+		'''allow a subclass to access the response data before it is returned to the user'''
98
+		return data
99
+
100
+
101
+
102
+
103
+inst = lambda x:x()
104
+class JSONRPCProxy(object):
105
+	'''A class implementing a JSON-RPC Proxy.
106
+
107
+	:param str host: The HTTP server hosting the JSON-RPC server
108
+	:param str path: The path where the JSON-RPC server can be found
109
+
110
+	There are two ways of instantiating this class:
111
+	- JSONRPCProxy.from_url(url) -- give the absolute url to the JSON-RPC server
112
+	- JSONRPC(host, path) -- break up the url into smaller parts
113
+
114
+	'''
115
+
116
+	#: Override this attribute to customize proxy behavior
117
+	_eventhandler = ProxyEvents
118
+	def customize(self, eventhandler):
119
+		self._eventhandler = eventhandler(self)
120
+
121
+	def _transformURL(self, serviceURL, path):
122
+		if serviceURL[-1] == '/':
123
+			serviceURL = serviceURL[:-1]
124
+		if path[0] != '/':
125
+			path = '/%s'%path
126
+		if path[-1] != '/' and '?' not in path:
127
+			path = '%s/'%path
128
+		return serviceURL, path
129
+
130
+
131
+	def _get_postdata(self, args, kwargs):
132
+		args,kwargs = self._eventhandler.get_postdata(args, kwargs)
133
+
134
+		if kwargs.has_key('__args'):
135
+			raise ValueError, 'invalid argument name: __args'
136
+		kwargs['__args'] = args or ()
137
+		postdata = jsonrpc.jsonutil.encode({
138
+			"method": self._serviceName,
139
+			'params': kwargs,
140
+			'id': self._eventhandler.IDGen,
141
+			'jsonrpc': '2.0'
142
+		})
143
+		return postdata
144
+
145
+	## Public interface
146
+	@classmethod
147
+	def from_url(cls, url, ctxid=None, serviceName=None):
148
+		'''Create a JSONRPCProxy from a URL'''
149
+		urlsp = urlparse.urlsplit(url)
150
+		url = '%s://%s' % (urlsp.scheme, urlsp.netloc)
151
+		path = urlsp.path
152
+		if urlsp.query: path = '%s?%s' % (path, urlsp.query)
153
+		if urlsp.fragment: path = '%s#%s' % (path, urlsp.fragment)
154
+		return cls(url, path, serviceName, ctxid)
155
+
156
+
157
+	def __init__(self, host, path='/jsonrpc', serviceName=None, *args, **kwargs):
158
+		self.serviceURL = host
159
+		self._serviceName = serviceName
160
+		self._path = path
161
+		self.serviceURL, self._path = self._transformURL(host, path)
162
+		self.customize(self._eventhandler)
163
+
164
+
165
+
166
+	def __getattr__(self, name):
167
+		if self._serviceName != None:
168
+			name = "%s.%s" % (self._serviceName, name)
169
+		return self.__class__(self.serviceURL, path=self._path, serviceName=name)
170
+
171
+
172
+
173
+
174
+	def __call__(self, *args, **kwargs):
175
+
176
+		url = '%(host)s%(path)s' % dict(host = self.serviceURL, path = self._path)
177
+		postdata = self._get_postdata(args, kwargs)
178
+		respdata = urllib.urlopen(url, postdata).read()
179
+		resp = jsonrpc.jsonutil.decode(respdata)
180
+
181
+		if resp.get('error') != None:
182
+			raise JSONRPCException(resp['error'])
183
+		else:
184
+			resp = self._eventhandler.proc_response(resp)
185
+			result = resp['result']
186
+			return result
187
+
188
+
189
+	def call(self, method, *args, **kwargs):
190
+		'''call a JSON-RPC method
191
+
192
+		It's better to use instance.<methodname>(\\*args, \\*\\*kwargs),
193
+		but this version might be useful occasionally
194
+		'''
195
+		p = self.__class__(self.serviceURL, path=self._path, serviceName=method)
196
+		return p(*args, **kwargs)
197
+
198
+
199
+	def batch_call(self, methods):
200
+		'''call several methods at once, return a list of (result, error) pairs
201
+
202
+		:param names: a dictionary { method: (args, kwargs) }
203
+		:returns: a list of pairs (result, error) where only one is not None
204
+		'''
205
+		if hasattr(methods, 'items'): methods = methods.items()
206
+		data = [ getattr(self, k)._get_postdata(*v) for k, v in methods ]
207
+		postdata = '[%s]' % ','.join(data)
208
+		respdata = urllib.urlopen(self.serviceURL, postdata).read()
209
+		resp = jsonrpc.jsonutil.decode(respdata)
210
+		return [(res.get('result'), res.get('error')) for res in resp]
0 211
new file mode 100644
... ...
@@ -0,0 +1,235 @@
1
+# $Id: server.py,v 1.8 2011/05/26 19:34:19 edwlan Exp $
2
+
3
+#
4
+#  Copyright (c) 2011 Edward Langley
5
+#  All rights reserved.
6
+#
7
+#  Redistribution and use in source and binary forms, with or without
8
+#  modification, are permitted provided that the following conditions
9
+#  are met:
10
+#
11
+#  Redistributions of source code must retain the above copyright notice,
12
+#  this list of conditions and the following disclaimer.
13
+#
14
+#  Redistributions in binary form must reproduce the above copyright
15
+#  notice, this list of conditions and the following disclaimer in the
16
+#  documentation and/or other materials provided with the distribution.
17
+#
18
+#  Neither the name of the project's author nor the names of its
19
+#  contributors may be used to endorse or promote products derived from
20
+#  this software without specific prior written permission.
21
+#
22
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+#
34
+#
35
+import cgi
36
+import copy
37
+import time
38
+import itertools
39
+import jsonrpc.jsonutil
40
+import json
41
+
42
+# Twisted imports
43
+from twisted.web import server
44
+from twisted.internet import threads
45
+from twisted.web.resource import Resource
46
+
47
+
48
+import UserDict, collections
49
+collections.Mapping.register(UserDict.DictMixin)
50
+
51
+class ServerEvents(object):
52
+	'''Subclass this and pass to :py:meth:`jsonrpc.customize` to customize the jsonrpc server'''
53
+
54
+	def __init__(self, jsonrpc):
55
+		#: A link to the JSON-RPC server instance
56
+		self.server = jsonrpc
57
+
58
+	def callmethod(self, request, method, kwargs, args, **kw):
59
+		'''Finds the method and calls it with the specified args'''
60
+		method = self.findmethod(method)
61
+		if method is None: raise MethodNotFound
62
+
63
+		return method(*args, **kwargs)
64
+
65
+	def findmethod(self, method):
66
+		'''Override to allow server to define methods'''
67
+		return lambda *a, **kw: 'Test Data'
68
+
69
+	def processrequest(self, result, args):
70
+		'''Override to implement custom handling of the method result and request'''
71
+		return result
72
+
73
+	def log(self, result, request):
74
+		'''Override to implement custom error handling'''
75
+		pass
76
+
77
+	def processcontent(self, content, request):
78
+		'''Given the freshly decoded content of the request, return what content should be used'''
79
+		return content
80
+
81
+
82
+class ServerError(Exception):
83
+	code = 0
84
+	msg = ""
85
+	id = None
86
+
87
+	def json_equivalent(self):
88
+		return dict(jsonrpc="2.0", error=dict(code=self.code, message=self.msg), id=self.id)
89
+	def __str__(self):
90
+		return jsonrpc.jsonutil.encode(self)
91
+
92
+class InvalidRequest(ServerError):
93
+	code = -32600
94
+	msg = "Invalid Request."
95
+
96
+class MethodNotFound(ServerError):
97
+	code = -32601
98
+	msg = "Procedure not found."
99
+
100
+class ParseError(ServerError):
101
+	code = -32700
102
+	msg = "Parse error."
103
+
104
+## Base class providing a JSON-RPC 2.0 implementation with 2 customizable hooks
105
+class JSON_RPC(Resource):
106
+	'''This class implements a JSON-RPC 2.0 server as a Twisted Resource'''
107
+	isLeaf = True
108
+
109
+	#: set by :py:meth:`customize` used to change the behavior of the server
110
+	eventhandler = ServerEvents
111
+
112
+	def customize(self, eventhandler):
113
+		'''customize the behavior of the server'''
114
+		self.eventhandler = eventhandler(self)
115
+		return self
116
+
117
+
118
+	def __init__(self, *args, **kwargs):
119
+		self.customize(self.eventhandler)
120
+		Resource.__init__(self,*args, **kwargs)
121
+
122
+
123
+	def render(self, request):
124
+		#move to emen2 event handler
125
+		#ctxid = request.getCookie("ctxid") or request.args.get("ctxid", [None])[0]
126
+		#host = request.getClientIP()
127
+
128
+		request.content.seek(0, 0)
129
+		try:
130
+			try:
131
+				content = jsonrpc.jsonutil.decode(request.content.read())
132
+			except ValueError: raise ParseError
133
+			content = self.eventhandler.processcontent(content, request)
134
+			d = threads.deferToThread(self._action, request, content)
135
+			d.addCallback(self._cbRender, request)
136
+			d.addErrback(self._ebRender, request, content.get('id') if hasattr(content, 'get') else None)
137
+		except BaseException, e:
138
+			self._ebRender(e, request, None)
139
+
140
+		return server.NOT_DONE_YET
141
+
142
+
143
+	def _cbRender(self, result, request):
144
+		if result is not None:
145
+			request.setHeader("content-type", 'application/json')
146
+			request.setResponseCode(200)
147
+			result = jsonrpc.jsonutil.encode(result).encode('utf-8')
148
+			request.setHeader("content-length", len(result))
149
+			request.write(result)
150
+		request.finish()
151
+
152
+	def _ebRender(self, result, request, id, finish=True):
153
+		self.eventhandler.log(result, request)
154
+
155
+		err = None
156
+		if not isinstance(result, BaseException):
157
+			try: result.raiseException()
158
+			except BaseException, e:
159
+				err = e
160
+		else: err = result
161
+		err = self.render_error(err, id)
162
+
163
+		request.setHeader("content-type", 'application/json')
164
+		request.setResponseCode(200)
165
+		result = jsonrpc.jsonutil.encode(err).encode('utf-8')
166
+		request.setHeader("content-length", len(result))
167
+		request.write(result)
168
+		if finish: request.finish()
169
+
170
+	def _parse_data(self, content):
171
+		if content.get('jsonrpc') != '2.0': raise InvalidRequest
172
+
173
+		method = content.get('method')
174
+		if not isinstance(method, (str, unicode)): raise InvalidRequest
175
+
176
+		kwargs = content.get('params', {})
177
+		args = ()
178
+		if not isinstance(kwargs, dict):
179
+			args = tuple(kwargs)
180
+			kwargs = {}
181
+		else:
182
+			args = kwargs.pop('__args', args)
183
+		kwargs = dict( (str(k), v) for k,v in kwargs.items() )
184
+
185
+		return method, kwargs, args
186
+
187
+
188
+
189
+	def render_error(self, e, id):
190
+		if isinstance(e, ServerError):
191
+			e.id = id
192
+			err = e.json_equivalent()
193
+		else:
194
+			err = dict(
195
+				jsonrpc='2.0',
196
+				id = id,
197
+				error= dict(
198
+					code=0,
199
+					message=str(e),
200
+					data = e.args
201
+			))
202
+
203
+		return err
204
+
205
+
206
+
207
+	def _action(self, request, contents):
208
+		result = []
209
+		ol = (True if isinstance(contents, list) else False)
210
+		if not ol: contents = [contents]
211
+
212
+		if contents == []: raise InvalidRequest
213
+
214
+		for content in contents:
215
+			try:
216
+				res = dict(jsonrpc='2.0', id=content.get('id'), result=self.eventhandler.callmethod(request, *self._parse_data(content)))
217
+
218
+				res = self.eventhandler.processrequest(res, request.args)
219
+
220
+				if res['id'] is not None: result.append(res)
221
+			except Exception, e:
222
+				err = self.render_error(e, content.get('id'))
223
+				if err['id'] is not None: result.append(err)
224
+
225
+
226
+
227
+		self.eventhandler.log(result, request)
228
+
229
+		if result != []:
230
+			if not ol: result = result[0]
231
+		else: result = None
232
+
233
+		return result
234
+
235
+
1 237
new file mode 100644
... ...
@@ -0,0 +1,98 @@
1
+#  
2
+#  Copyright (c) 2011 Edward Langley
3
+#  All rights reserved.
4
+#  
5
+#  Redistribution and use in source and binary forms, with or without
6
+#  modification, are permitted provided that the following conditions
7
+#  are met:
8
+#  
9
+#  Redistributions of source code must retain the above copyright notice,
10
+#  this list of conditions and the following disclaimer.
11
+#  
12
+#  Redistributions in binary form must reproduce the above copyright
13
+#  notice, this list of conditions and the following disclaimer in the
14
+#  documentation and/or other materials provided with the distribution.
15
+#  
16
+#  Neither the name of the project's author nor the names of its
17
+#  contributors may be used to endorse or promote products derived from
18
+#  this software without specific prior written permission.
19
+#  
20
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+#  
32
+#
33
+import json
34
+import unittest
35
+import collections
36
+
37
+from jsonrpc import jsonutil
38
+
39
+class testobj(object):
40
+	value = 'This is the json value'
41
+	def json_equivalent(self): return self.value
42
+
43
+class TestSequenceFunctions(unittest.TestCase):
44
+
45
+	def setUp(self):
46
+		# simple tests
47
+		self.lis = [1,2,3]
48
+		self.jlis = json.dumps(self.lis)
49
+		self.int = 1
50
+		self.jint = json.dumps(self.int)
51
+		self.str = 'asdasd'
52
+		self.jstr = json.dumps(self.str)
53
+		self.none = None
54
+		self.jnone = json.dumps(self.none)
55
+
56
+		# more complicated tests
57
+		self.obj1 = dict(a=1,b=2,c=3)
58
+		self.jobj1 = json.dumps(self.obj1)
59
+		self.obj2 = dict(a=[1,2,3],b={2:2,3:3,4:4},c=(3,4,5))
60
+		self.jobj2 = json.dumps(self.obj2)
61
+
62
+		# extended functionality
63
+		self.obj3 = dict(a=set([1]),b=frozenset([2]),c=[1,2,3])
64
+		self.obj3_roundtrip = dict( (k,list(v)) for k,v in self.obj3.items())
65
+
66
+		self.obj4 = testobj()
67
+		self.jobj4 = json.dumps(testobj.value)
68
+
69
+	def test_encode(self):
70
+		self.assertEqual(jsonutil.encode(self.lis), self.jlis)
71
+		self.assertEqual(jsonutil.encode(self.int), self.jint)
72
+		self.assertEqual(jsonutil.encode(self.str), self.jstr)
73
+		self.assertEqual(jsonutil.encode(self.none), self.jnone)
74
+
75
+		self.assertEqual(jsonutil.encode(self.obj1), self.jobj1)
76
+		self.assertEqual(jsonutil.encode(self.obj2), self.jobj2)
77
+		self.assertEqual(jsonutil.encode(self.obj4), self.jobj4)
78
+
79
+	def test_decode(self):
80
+		self.assertEqual(jsonutil.decode(self.jlis), self.lis)
81
+		self.assertEqual(jsonutil.decode(self.jint), self.int)
82
+		self.assertEqual(jsonutil.decode(self.jstr), self.str)
83
+		self.assertEqual(jsonutil.decode(self.jnone), self.none)
84
+
85
+		self.assertEqual(jsonutil.decode(self.jobj1), self.obj1)
86
+		self.assertEqual(jsonutil.decode(self.jobj2), json.loads(self.jobj2))
87
+		self.assertEqual(jsonutil.decode(self.jobj4), testobj.value)
88
+
89
+	def test_roundtrip(self):
90
+		self.assertEqual(jsonutil.decode(jsonutil.encode(self.lis)), self.lis)
91
+		self.assertEqual(jsonutil.decode(jsonutil.encode(self.int)), self.int)
92
+		self.assertEqual(jsonutil.decode(jsonutil.encode(self.str)), self.str)
93
+		self.assertEqual(jsonutil.decode(jsonutil.encode(self.none)), self.none)
94
+
95
+		self.assertEqual(jsonutil.decode(jsonutil.encode(self.obj3)), self.obj3_roundtrip)
96
+
97
+if __name__ == '__main__':
98
+	unittest.main()
0 99
new file mode 100644
... ...
@@ -0,0 +1,293 @@
1
+#
2
+#  Copyright (c) 2011 Edward Langley
3
+#  All rights reserved.
4
+#
5
+#  Redistribution and use in source and binary forms, with or without
6
+#  modification, are permitted provided that the following conditions
7
+#  are met:
8
+#
9
+#  Redistributions of source code must retain the above copyright notice,
10
+#  this list of conditions and the following disclaimer.
11
+#
12
+#  Redistributions in binary form must reproduce the above copyright
13
+#  notice, this list of conditions and the following disclaimer in the
14
+#  documentation and/or other materials provided with the distribution.
15
+#
16
+#  Neither the name of the project's author nor the names of its
17
+#  contributors may be used to endorse or promote products derived from
18
+#  this software without specific prior written permission.
19
+#
20
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
+#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+#  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+#
32
+#
33
+from twisted.trial import unittest
34
+import StringIO
35
+
36
+import mock
37
+
38
+import jsonrpc.server
39
+import jsonrpc.jsonutil
40
+
41
+from twisted.web.test.test_web import DummyRequest
42
+from twisted.internet.defer import succeed
43
+from twisted.web.static import server
44
+
45
+def _render(resource, request):
46
+    result = resource.render(request)
47
+    if isinstance(result, str):
48
+        request.write(result)
49
+        request.finish()
50
+        return succeed(None)
51
+    elif result is server.NOT_DONE_YET:
52
+        if request.finished:
53
+            return succeed(None)
54
+        else:
55
+            return request.notifyFinish()
56
+    else:
57
+        raise ValueError("Unexpected return value: %r" % (result,))
58
+
59
+class SimpleEventHandler(jsonrpc.server.ServerEvents):
60
+	def log(self, result, request): pass
61
+
62
+	def findmethod(self, method):
63
+		if method in set(['echo', 'add']):
64
+			return getattr(self, method)
65
+
66
+	def add(self, a,b):
67
+		return a+b
68
+
69
+	def echo(self, v): return v
70
+
71
+
72
+class TestJSONRPCServer(unittest.TestCase):
73
+
74
+	def setUp(self):
75
+		self.id_ = 'an_id'
76
+		self.param = "some data"
77
+
78
+	def test_invalid_data(self):
79
+		resource = jsonrpc.server.JSON_RPC()
80
+		resource.customize(SimpleEventHandler)
81
+
82
+		request = DummyRequest([''])
83
+		request.getCookie = mock.Mock()
84
+		request.content = StringIO.StringIO(' {"v": %s}, "method": "echo"}' % (jsonrpc.jsonutil.encode(self.param)))
85
+
86
+		d = _render(resource, request)
87
+
88
+		@d.addCallback
89
+		def rendered(ignored):
90
+			assert len(request.written) == 1
91
+			data = jsonrpc.jsonutil.decode(request.written[0])
92
+
93
+			assert data == {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": None}
94
+
95
+		return d
96
+
97
+	def test_wrongversion(self):
98
+		resource = jsonrpc.server.JSON_RPC()
99
+		resource.customize(SimpleEventHandler)
100
+
101
+		request = DummyRequest([''])
102
+		request.getCookie = mock.Mock()
103
+		request.content = StringIO.StringIO('{"jsonrpc": "2.1", "params": %s, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_))
104
+
105
+		d = _render(resource, request)
106
+
107
+		@d.addCallback
108
+		def rendered(ignored):
109
+			assert len(request.written) == 1
110
+			data = jsonrpc.jsonutil.decode(request.written[0])
111
+			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_}
112
+		return d
113
+
114
+
115
+	def test_invalidmethodname(self):
116
+		resource = jsonrpc.server.JSON_RPC()
117
+		resource.customize(SimpleEventHandler)
118
+
119
+		request = DummyRequest([''])
120
+		request.getCookie = mock.Mock()
121
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": 0, "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_))
122
+
123
+		d = _render(resource, request)
124
+
125
+		@d.addCallback
126
+		def rendered(ignored):
127
+			assert len(request.written) == 1
128
+			data = jsonrpc.jsonutil.decode(request.written[0])
129
+			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_}
130
+		return d
131
+
132
+	def test_missingmethod(self):
133
+		resource = jsonrpc.server.JSON_RPC()
134
+		resource.customize(SimpleEventHandler)
135
+
136
+		request = DummyRequest([''])
137
+		request.getCookie = mock.Mock()
138
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": "non_existent", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_))
139
+
140
+		d = _render(resource, request)
141
+
142
+		@d.addCallback
143
+		def rendered(ignored):
144
+			assert len(request.written) == 1
145
+			data = jsonrpc.jsonutil.decode(request.written[0])
146
+			assert data == {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": self.id_}
147
+		return d
148
+
149
+
150
+
151
+	def test_simplecall(self):
152
+		resource = jsonrpc.server.JSON_RPC()
153
+		resource.customize(SimpleEventHandler)
154
+
155
+		request = DummyRequest([''])
156
+		request.getCookie = mock.Mock()
157
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_))
158
+
159
+		d = _render(resource, request)
160
+
161
+		@d.addCallback
162
+		def rendered(ignored):
163
+			assert len(request.written) == 1
164
+			data = jsonrpc.jsonutil.decode(request.written[0])
165
+
166
+			assert data['id'] == self.id_
167
+			assert data['result'] == self.param
168
+		return d
169
+
170
+	def test_notify(self):
171
+		resource = jsonrpc.server.JSON_RPC()
172
+		resource.customize(SimpleEventHandler)
173
+
174
+		request = DummyRequest([''])
175
+		request.getCookie = mock.Mock()
176
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": {"v": %s}, "method": "echo"}' % (jsonrpc.jsonutil.encode(self.param)))
177
+
178
+		d = _render(resource, request)
179
+
180
+		@d.addCallback
181
+		def rendered(ignored):
182
+			assert len(request.written) == 0
183
+
184
+		return d
185
+
186
+
187
+	def test_kwcall(self):
188
+		resource = jsonrpc.server.JSON_RPC()
189
+		resource.customize(SimpleEventHandler)
190
+
191
+		request = DummyRequest([''])
192
+		request.getCookie = mock.Mock()
193
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": {"v": %s}, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode(self.param), self.id_))
194
+
195
+		d = _render(resource, request)
196
+
197
+		@d.addCallback
198
+		def rendered(ignored):
199
+			assert len(request.written) == 1
200
+			data = jsonrpc.jsonutil.decode(request.written[0])
201
+
202
+			assert data['id'] == self.id_
203
+			assert data['result'] == self.param
204
+
205
+		return d
206
+
207
+
208
+	def test_err(self):
209
+		resource = jsonrpc.server.JSON_RPC()
210
+		resource.customize(SimpleEventHandler)
211
+
212
+		request = DummyRequest([''])
213
+		request.getCookie = mock.Mock()
214
+		request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": [1, "sss"], "method": "add", "id": "%s"}' % self.id_)
215
+
216
+		d = _render(resource, request)
217
+
218
+		@d.addCallback
219
+		def rendered(ignored, *a):
220
+			assert len(request.written) == 1
221
+			data = jsonrpc.jsonutil.decode(request.written[0])
222
+
223
+			assert data['id'] == self.id_
224
+			assert data.get('error', False)
225
+		return rendered
226
+
227
+	def test_batchcall(self):
228
+		resource = jsonrpc.server.JSON_RPC()
229
+		resource.customize(SimpleEventHandler)
230
+
231
+		request = DummyRequest([''])
232
+		request.content = StringIO.StringIO(
233
+			'[{"jsonrpc": "2.0", "params": [1, 2], "method": "add", "id": "1"},'
234
+				'{"jsonrpc": "2.0", "params": {"a": 3, "b": 2}, "method": "add", "id": "2"}]'
235
+		)
236
+		request.getCookie = mock.Mock()
237
+
238
+		d = _render(resource, request)
239
+
240
+		@d.addCallback
241
+		def rendered(ignored, *a):
242
+			assert len(request.written) == 1
243
+			data = jsonrpc.jsonutil.decode(request.written[0])
244
+			assert len(data) == 2
245
+			assert set(x['id'] for x in data) == set("12")
246
+			assert set(x['result'] for x in data) == set([3,5])
247
+
248
+			assert not any(x.get('error', False) for x in data)
249
+		return rendered
250
+
251
+	def test_batchcall_1err(self):
252
+		resource = jsonrpc.server.JSON_RPC()
253
+		resource.customize(SimpleEventHandler)
254
+
255
+		request = DummyRequest([''])
256
+		request.content = StringIO.StringIO(
257
+			'[{"jsonrpc": "2.0", "params": [1, 2], "method": "add", "id": "1"},'
258
+				'{"jsonrpc": "2.0", "params": {"a": "3", "b": 2}, "method": "add", "id": "2"}]'
259
+		)
260
+		request.getCookie = mock.Mock()
261
+
262
+		d = _render(resource, request)
263
+
264
+		@d.addCallback
265
+		def rendered(ignored, *a):
266
+			assert len(request.written) == 1
267
+			data = jsonrpc.jsonutil.decode(request.written[0])
268
+			assert len(data) == 2
269
+			assert set(x['id'] for x in data) == set("12")
270
+			assert set(x.get('result', False) for x in data) == set([3,False])
271
+
272
+			assert len(filter(None, [x.get('error') for x in data])) == 1
273
+		return rendered
274
+
275
+
276
+	def test_batchcall_emptylist(self):
277
+		resource = jsonrpc.server.JSON_RPC()
278
+		resource.customize(SimpleEventHandler)
279
+
280
+		request = DummyRequest([''])
281
+
282
+		request.content = StringIO.StringIO('[]')
283
+		request.getCookie = mock.Mock()
284
+
285
+		d = _render(resource, request)
286
+
287
+		@d.addCallback
288
+		def rendered(ignored, *a):
289
+			data = jsonrpc.jsonutil.decode(request.written[0])
290
+			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": None}
291
+		return rendered
292
+
293
+