Browse code
Writing documentation, tweaking example server and proxy
Ed L authored on 02/06/2011 21:41:39
Showing 5 changed files
Showing 5 changed files
- doc/source/conf.py
- doc/source/getting_started.rst
- doc/source/index.rst
- jsonrpc/example_server.py
- jsonrpc/proxy.py
77 | 78 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,63 @@ |
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 |
+ |
|
32 |
+Getting Started |
|
33 |
+=============== |
|
34 |
+This code will create a server that logs all requests, and provides two methods to clients: |
|
35 |
+add and subtract: |
|
36 |
+ |
|
37 |
+.. literalinclude:: ../../jsonrpc/example_server.py |
|
38 |
+ :language: python |
|
39 |
+ :linenos: |
|
40 |
+ :tab-width: 2 |
|
41 |
+ :lines: 2,3,34- |
|
42 |
+ |
|
43 |
+To use the server, start the client this way: |
|
44 |
+ |
|
45 |
+.. code-block:: bash |
|
46 |
+ |
|
47 |
+ # Python 2.6 |
|
48 |
+ % python2.6 -i -m jsonrpc.__main__ http://localhost:8007 |
|
49 |
+ |
|
50 |
+ # Python 2.7 |
|
51 |
+ % python2.7 -i -m jsonrpc http://localhost:8007 |
|
52 |
+ |
|
53 |
+.. code-block:: python |
|
54 |
+ |
|
55 |
+ >>> server.add(1,2) |
|
56 |
+ 3 |
|
57 |
+ >>> server.subtract(3,2) |
|
58 |
+ 1 |
|
59 |
+ >>> server.batch_call(dict( |
|
60 |
+ ... add = ((3, 2), {} ), |
|
61 |
+ ... subtract = ((), {'a': 3, 'b': 2}) |
|
62 |
+ ... )) |
|
63 |
+ [(5, None), (1, None)] |
... | ... |
@@ -30,26 +30,35 @@ |
30 | 30 |
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 |
# |
32 | 32 |
# |
33 |
+ |
|
33 | 34 |
from twisted.internet import reactor, ssl |
34 | 35 |
from twisted.web import server |
35 | 36 |
import traceback |
36 | 37 |
|
37 | 38 |
from jsonrpc.server import ServerEvents, JSON_RPC |
38 | 39 |
|
39 |
-class JSONRPCTest(ServerEvents): |
|
40 |
+class ExampleServer(ServerEvents): |
|
41 |
+ # inherited hooks |
|
40 | 42 |
def log(self, responses, txrequest): |
41 | 43 |
if isinstance(responses, list): |
42 | 44 |
for response in responses: |
43 |
- print txrequest, response.id, response.result or response.error |
|
45 |
+ msg = self.get_msg(response) |
|
46 |
+ print txrequest, msg |
|
44 | 47 |
else: |
45 |
- print txrequest, responses.id, responses.result or responses.error |
|
48 |
+ msg = self.get_msg(response) |
|
49 |
+ print txrequest, msg |
|
46 | 50 |
|
47 | 51 |
def findmethod(self, method): |
48 |
- if method in set(['add', 'subtract']): |
|
52 |
+ if method in self.methods: |
|
49 | 53 |
return getattr(self, method) |
50 | 54 |
else: |
51 | 55 |
return None |
52 | 56 |
|
57 |
+ # helper methods |
|
58 |
+ methods = set(['add', 'subtract']) |
|
59 |
+ def _get_msg(self, response): |
|
60 |
+ return ' '.join([response.id, response.result or response.error]) |
|
61 |
+ |
|
53 | 62 |
def subtract(self, a, b): |
54 | 63 |
return a-b |
55 | 64 |
|
... | ... |
@@ -65,6 +74,3 @@ PORT = 8007 |
65 | 74 |
print 'Listening on port %d...' % PORT |
66 | 75 |
reactor.listenTCP(PORT, site) |
67 | 76 |
reactor.run() |
68 |
- |
|
69 |
- |
|
70 |
-__version__ = "$Revision: 1.8 $".split(":")[1][:-1].strip() |
... | ... |
@@ -80,11 +80,12 @@ class ProxyEvents(object): |
80 | 80 |
'''An event handler for JSONRPCProxy''' |
81 | 81 |
|
82 | 82 |
#: an instance of a class which defines a __get__ method, used to generate a request id |
83 |
- IDGen = IDGen() |
|
83 |
+ IDGen = IDGen |
|
84 | 84 |
|
85 | 85 |
|
86 | 86 |
def __init__(self, proxy): |
87 | 87 |
'''Allow a subclass to do its own initialization, gets any arguments leftover from __init__''' |
88 |
+ self.IDGen = self.IDGen() |
|
88 | 89 |
self.proxy = proxy |
89 | 90 |
|
90 | 91 |
def get_postdata(self, args, kwargs): |
... | ... |
@@ -98,9 +99,32 @@ class ProxyEvents(object): |
98 | 99 |
return data |
99 | 100 |
|
100 | 101 |
|
102 |
+class Request(object): |
|
103 |
+ |
|
104 |
+ def __init__(self, id, method, args=None, kwargs=None): |
|
105 |
+ self.version = '2.0' |
|
106 |
+ self.id = id |
|
107 |
+ self.method = method |
|
108 |
+ self.args = args |
|
109 |
+ self.kwargs = kwargs |
|
110 |
+ |
|
111 |
+ def json_equivalent(self): |
|
112 |
+ if kwargs.has_key('__args'): |
|
113 |
+ raise ValueError, 'invalid argument name: __args' |
|
114 |
+ |
|
115 |
+ result = dict( |
|
116 |
+ jsonrpc = self.version, |
|
117 |
+ id = self.id, |
|
118 |
+ method = self.method |
|
119 |
+ ) |
|
120 |
+ |
|
121 |
+ if self.args and self.kwargs: |
|
122 |
+ self.kwargs['__args'] = self.args |
|
123 |
+ |
|
124 |
+ |
|
125 |
+ |
|
101 | 126 |
|
102 | 127 |
|
103 |
-inst = lambda x:x() |
|
104 | 128 |
class JSONRPCProxy(object): |
105 | 129 |
'''A class implementing a JSON-RPC Proxy. |
106 | 130 |
|
... | ... |
@@ -128,20 +152,6 @@ class JSONRPCProxy(object): |
128 | 152 |
return serviceURL, path |
129 | 153 |
|
130 | 154 |
|
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 | 155 |
## Public interface |
146 | 156 |
@classmethod |
147 | 157 |
def from_url(cls, url, ctxid=None, serviceName=None): |
... | ... |
@@ -169,6 +179,12 @@ class JSONRPCProxy(object): |
169 | 179 |
return self.__class__(self.serviceURL, path=self._path, serviceName=name) |
170 | 180 |
|
171 | 181 |
|
182 |
+ def _get_postdata(self, args=None, kwargs=None): |
|
183 |
+ args,kwargs = self._eventhandler.get_postdata(args, kwargs) |
|
184 |
+ id = self._eventhandler.IDGen |
|
185 |
+ return Request(id, self._serviceNams, args, kwargs) |
|
186 |
+ postdata = jsonrpc.jsonutil.encode({ }) |
|
187 |
+ return postdata |
|
172 | 188 |
|
173 | 189 |
|
174 | 190 |
def __call__(self, *args, **kwargs): |