Browse code
rearranging for distutils
Showing 17 changed files
- jsonrpc/COPYING
- jsonrpc/README.markdown
- jsonrpc/__init__.py
- jsonrpc/__main__.py
- jsonrpc/doc/Makefile
- jsonrpc/doc/source/conf.py
- jsonrpc/doc/source/index.rst
- jsonrpc/doc/source/jsonutil.rst
- jsonrpc/doc/source/proxy.rst
- jsonrpc/doc/source/server.rst
- jsonrpc/example_server.py
- jsonrpc/jsonutil.py
- jsonrpc/proxy.py
- jsonrpc/server.py
- jsonrpc/tests/__init__.py
- jsonrpc/tests/test_jsonutil.py
- jsonrpc/tests/test_server.py
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 |
+ |