git.fiddlerwoaroof.com
Ed L authored on 01/06/2011 20:29:56
Showing 6 changed files
... ...
@@ -49,7 +49,7 @@ import sys, os
49 49
 # If extensions (or modules to document with autodoc) are in another directory,
50 50
 # add these directories to sys.path here. If the directory is relative to the
51 51
 # documentation root, use os.path.abspath to make it absolute, like shown here.
52
-sys.path.insert(0, os.path.abspath('../../..'))
52
+sys.path.insert(0, os.path.abspath('../..'))
53 53
 
54 54
 # -- General configuration -----------------------------------------------------
55 55
 
... ...
@@ -1,4 +1,4 @@
1
-from . import proxy
1
+from jsonrpc import proxy
2 2
 import optparse
3 3
 
4 4
 def main(host, path=None):
... ...
@@ -32,11 +32,14 @@
32 32
 #
33 33
 from twisted.internet import reactor, ssl
34 34
 from twisted.web import server
35
+import traceback
35 36
 
36
-from .server import ServerEvents, JSON_RPC
37
+from jsonrpc.server import ServerEvents, JSON_RPC
37 38
 
38 39
 class JSONRPCTest(ServerEvents):
39
-	def log(self, *a): print a
40
+	def log(self, *a):
41
+		print a
42
+
40 43
 	def findmethod(self, method):
41 44
 		if method in set(['add', 'subtract']):
42 45
 			return getattr(self, method)
... ...
@@ -32,12 +32,8 @@
32 32
 #  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 33
 #
34 34
 #
35
-import cgi
36
-import copy
37
-import time
38
-import itertools
39 35
 import jsonrpc.jsonutil
40
-import json
36
+from jsonrpc.utilities import public
41 37
 
42 38
 # Twisted imports
43 39
 from twisted.web import server
... ...
@@ -48,19 +44,20 @@ from twisted.web.resource import Resource
48 44
 import UserDict, collections
49 45
 collections.Mapping.register(UserDict.DictMixin)
50 46
 
47
+@public
51 48
 class ServerEvents(object):
52 49
 	'''Subclass this and pass to :py:meth:`jsonrpc.customize` to customize the jsonrpc server'''
53 50
 
54
-	def __init__(self, jsonrpc):
51
+	def __init__(self, server):
55 52
 		#: A link to the JSON-RPC server instance
56
-		self.server = jsonrpc
53
+		self.server = server
57 54
 
58
-	def callmethod(self, request, method, kwargs, args, **kw):
55
+	def callmethod(self, txrequest, rpcrequest):
59 56
 		'''Finds the method and calls it with the specified args'''
60
-		method = self.findmethod(method)
57
+		method = self.findmethod(rpcrequest.method)
61 58
 		if method is None: raise MethodNotFound
62 59
 
63
-		return method(*args, **kwargs)
60
+		return method(*rpcrequest.args, **rpcrequest.kwargs)
64 61
 
65 62
 	def findmethod(self, method):
66 63
 		'''Override to allow server to define methods'''
... ...
@@ -79,29 +76,90 @@ class ServerEvents(object):
79 76
 		return content
80 77
 
81 78
 
79
+@public
82 80
 class ServerError(Exception):
81
+	'''Base Exception for JSON-RPC Errors, if this or a subclass of this is raised by a JSON-RPC method,
82
+	The server will convert it into an appropriate error object
83
+	'''
84
+
85
+	#: Error code
83 86
 	code = 0
87
+	#: Error message
84 88
 	msg = ""
85
-	id = None
86 89
 
87 90
 	def json_equivalent(self):
88
-		return dict(jsonrpc="2.0", error=dict(code=self.code, message=self.msg), id=self.id)
91
+		'''return a dictionary which matches an JSON-RPC Response'''
92
+		return dict(code=self.code, message=self.msg)
93
+
89 94
 	def __str__(self):
90 95
 		return jsonrpc.jsonutil.encode(self)
91 96
 
97
+@public
92 98
 class InvalidRequest(ServerError):
99
+	'''Raise this when the Request object does not match the schema'''
93 100
 	code = -32600
94 101
 	msg = "Invalid Request."
95 102
 
103
+@public
96 104
 class MethodNotFound(ServerError):
105
+	'''Raise this when the desired method is not found'''
97 106
 	code = -32601
98 107
 	msg = "Procedure not found."
99 108
 
109
+@public
100 110
 class ParseError(ServerError):
111
+	'''Raise this when the request contains invalid JSON'''
101 112
 	code = -32700
102 113
 	msg = "Parse error."
103 114
 
115
+class Request(object):
116
+	def __init__(self, content):
117
+		self.version = content.get('jsonrpc')
118
+		self.id = content.get('id')
119
+
120
+		self.method = content.get('method')
121
+
122
+		kwargs = content.get('params', {})
123
+		args = ()
124
+		if not isinstance(kwargs, dict):
125
+			args = tuple(kwargs)
126
+			kwargs = {}
127
+		else:
128
+			args = kwargs.pop('__args', args)
129
+
130
+		self.args = args
131
+		self.kwargs = dict( (str(k), v) for k,v in kwargs.items() )
132
+
133
+	def check(self):
134
+		if self.version != '2.0': raise InvalidRequest
135
+		if not isinstance(self.method, (str, unicode)): raise InvalidRequest
136
+
137
+	@classmethod
138
+	def from_list(cls, content):
139
+		result = []
140
+		for req in content:
141
+			result.append(cls(req))
142
+		return result
143
+
144
+
145
+class Response(object):
146
+	def __init__(self, id=None, result=None, error=None):
147
+		self.version = '2.0'
148
+		self.id = id
149
+		self.result = result
150
+		self.error = error
151
+
152
+	def json_equivalent(self):
153
+		res = dict(jsonrpc=self.version, id=self.id)
154
+		if self.error is None:
155
+			res['result'] = self.result
156
+		else:
157
+			res['error'] = self.error
158
+		return res
159
+
160
+
104 161
 ## Base class providing a JSON-RPC 2.0 implementation with 2 customizable hooks
162
+@public
105 163
 class JSON_RPC(Resource):
106 164
 	'''This class implements a JSON-RPC 2.0 server as a Twisted Resource'''
107 165
 	isLeaf = True
... ...
@@ -133,14 +191,58 @@ class JSON_RPC(Resource):
133 191
 
134 192
 			content = self.eventhandler.processcontent(content, request)
135 193
 
194
+			if isinstance(content, list):
195
+				content = Request.from_list(content)
196
+			else:
197
+				content = Request(content)
198
+
199
+			try:
200
+				if hasattr(content, 'check'):
201
+					content.check()
202
+				else:
203
+					for item in content: item.check()
204
+
205
+			except ServerError, e:
206
+				self._ebRender(e, request, content.id if hasattr(content, 'id') else None)
207
+
136 208
 			d = threads.deferToThread(self._action, request, content)
137 209
 			d.addCallback(self._cbRender, request)
138
-			d.addErrback(self._ebRender, request, content.get('id') if hasattr(content, 'get') else None)
210
+			d.addErrback(self._ebRender, request, content.id if hasattr(content, 'id') else None)
139 211
 		except BaseException, e:
140 212
 			self._ebRender(e, request, None)
141 213
 
142 214
 		return server.NOT_DONE_YET
143 215
 
216
+	def _action(self, request, contents):
217
+		result = []
218
+
219
+		islist = (True if isinstance(contents, list) else False)
220
+		if not islist: contents = [contents]
221
+
222
+		if contents == []: raise InvalidRequest
223
+
224
+		for rpcrequest in contents:
225
+			res = None
226
+
227
+			try:
228
+				res = Response(id=rpcrequest.id, result=self.eventhandler.callmethod(request, rpcrequest))
229
+				res = self.eventhandler.processrequest(res, request.args)
230
+			except Exception, e:
231
+				res = self.render_error(e, rpcrequest.id)
232
+
233
+			if res.id is not None:
234
+				result.append(res)
235
+
236
+
237
+		self.eventhandler.log(result, request)
238
+
239
+		if result != []:
240
+			if not islist: result = result[0]
241
+		else: result = None
242
+
243
+		return result
244
+
245
+
144 246
 
145 247
 	def _cbRender(self, result, request):
146 248
 		if result is not None:
... ...
@@ -169,69 +271,15 @@ class JSON_RPC(Resource):
169 271
 		request.write(result)
170 272
 		if finish: request.finish()
171 273
 
172
-	def _parse_data(self, content):
173
-		if content.get('jsonrpc') != '2.0': raise InvalidRequest
174
-
175
-		method = content.get('method')
176
-		if not isinstance(method, (str, unicode)): raise InvalidRequest
177
-
178
-		kwargs = content.get('params', {})
179
-		args = ()
180
-		if not isinstance(kwargs, dict):
181
-			args = tuple(kwargs)
182
-			kwargs = {}
183
-		else:
184
-			args = kwargs.pop('__args', args)
185
-		kwargs = dict( (str(k), v) for k,v in kwargs.items() )
186
-
187
-		return method, kwargs, args
188
-
189
-
190 274
 
191 275
 	def render_error(self, e, id):
192 276
 		if isinstance(e, ServerError):
193
-			e.id = id
194
-			err = e.json_equivalent()
277
+			err = Response(id=id, error=e)
195 278
 		else:
196
-			err = dict(
197
-				jsonrpc='2.0',
198
-				id = id,
199
-				error= dict(
200
-					code=0,
201
-					message=str(e),
202
-					data = e.args
203
-			))
279
+			err = Response(id=id, error=dict(code=0, message=str(e), data=e.args))
204 280
 
205 281
 		return err
206 282
 
207 283
 
208 284
 
209
-	def _action(self, request, contents):
210
-		result = []
211
-		ol = (True if isinstance(contents, list) else False)
212
-		if not ol: contents = [contents]
213
-
214
-		if contents == []: raise InvalidRequest
215
-
216
-		for content in contents:
217
-			try:
218
-				res = dict(jsonrpc='2.0', id=content.get('id'), result=self.eventhandler.callmethod(request, *self._parse_data(content)))
219
-
220
-				res = self.eventhandler.processrequest(res, request.args)
221
-
222
-				if res['id'] is not None: result.append(res)
223
-			except Exception, e:
224
-				err = self.render_error(e, content.get('id'))
225
-				if err['id'] is not None: result.append(err)
226
-
227
-
228
-
229
-		self.eventhandler.log(result, request)
230
-
231
-		if result != []:
232
-			if not ol: result = result[0]
233
-		else: result = None
234
-
235
-		return result
236
-
237 285
 
... ...
@@ -40,7 +40,7 @@ class testobj(object):
40 40
 	value = 'This is the json value'
41 41
 	def json_equivalent(self): return self.value
42 42
 
43
-class TestSequenceFunctions(unittest.TestCase):
43
+class TestJSONUTIL(unittest.TestCase):
44 44
 
45 45
 	def setUp(self):
46 46
 		# simple tests
... ...
@@ -92,11 +92,10 @@ class TestJSONRPCServer(unittest.TestCase):
92 92
 
93 93
 		@d.addCallback
94 94
 		def rendered(ignored):
95
-			assert True
96
-			assert resource.eventhandler.processcontent.called
97
-			assert resource.eventhandler.callmethod.called
98
-			assert resource.eventhandler.processrequest.called
99
-			assert resource.eventhandler.log.called
95
+			self.assertTrue(resource.eventhandler.processcontent.called)
96
+			self.assertTrue(resource.eventhandler.callmethod.called)
97
+			self.assertTrue(resource.eventhandler.processrequest.called)
98
+			self.assertTrue(resource.eventhandler.log.called)
100 99
 
101 100
 		return d
102 101
 
... ...
@@ -113,10 +112,10 @@ class TestJSONRPCServer(unittest.TestCase):
113 112
 
114 113
 		@d.addCallback
115 114
 		def rendered(ignored):
116
-			assert len(request.written) == 1
115
+			self.assertEqual(len(request.written), 1)
117 116
 			data = jsonrpc.jsonutil.decode(request.written[0])
118 117
 
119
-			assert data == {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": None}
118
+			self.assertEqual(data, {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": None})
120 119
 
121 120
 		return d
122 121
 
... ...
@@ -132,9 +131,9 @@ class TestJSONRPCServer(unittest.TestCase):
132 131
 
133 132
 		@d.addCallback
134 133
 		def rendered(ignored):
135
-			assert len(request.written) == 1
134
+			self.assertEqual(len(request.written), 1)
136 135
 			data = jsonrpc.jsonutil.decode(request.written[0])
137
-			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_}
136
+			self.assertEqual(data, {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_})
138 137
 		return d
139 138
 
140 139
 
... ...
@@ -150,9 +149,9 @@ class TestJSONRPCServer(unittest.TestCase):
150 149
 
151 150
 		@d.addCallback
152 151
 		def rendered(ignored):
153
-			assert len(request.written) == 1
152
+			self.assertEqual(len(request.written), 1)
154 153
 			data = jsonrpc.jsonutil.decode(request.written[0])
155
-			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_}
154
+			self.assertEqual(data, {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_})
156 155
 		return d
157 156
 
158 157
 	def test_missingmethod(self):
... ...
@@ -167,9 +166,9 @@ class TestJSONRPCServer(unittest.TestCase):
167 166
 
168 167
 		@d.addCallback
169 168
 		def rendered(ignored):
170
-			assert len(request.written) == 1
169
+			self.assertEqual(len(request.written), 1)
171 170
 			data = jsonrpc.jsonutil.decode(request.written[0])
172
-			assert data == {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": self.id_}
171
+			self.assertEqual(data, {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": self.id_})
173 172
 		return d
174 173
 
175 174
 
... ...
@@ -186,11 +185,11 @@ class TestJSONRPCServer(unittest.TestCase):
186 185
 
187 186
 		@d.addCallback
188 187
 		def rendered(ignored):
189
-			assert len(request.written) == 1
188
+			self.assertEqual(len(request.written), 1)
190 189
 			data = jsonrpc.jsonutil.decode(request.written[0])
191 190
 
192
-			assert data['id'] == self.id_
193
-			assert data['result'] == self.param
191
+			self.assertEqual(data['id'], self.id_)
192
+			self.assertEqual(data['result'], self.param)
194 193
 		return d
195 194
 
196 195
 	def test_notify(self):
... ...
@@ -205,7 +204,7 @@ class TestJSONRPCServer(unittest.TestCase):
205 204
 
206 205
 		@d.addCallback
207 206
 		def rendered(ignored):
208
-			assert len(request.written) == 0
207
+			self.assertEqual(len(request.written), 0)
209 208
 
210 209
 		return d
211 210
 
... ...
@@ -222,11 +221,11 @@ class TestJSONRPCServer(unittest.TestCase):
222 221
 
223 222
 		@d.addCallback
224 223
 		def rendered(ignored):
225
-			assert len(request.written) == 1
224
+			self.assertEqual(len(request.written), 1)
226 225
 			data = jsonrpc.jsonutil.decode(request.written[0])
227 226
 
228
-			assert data['id'] == self.id_
229
-			assert data['result'] == self.param
227
+			self.assertEqual(data['id'], self.id_)
228
+			self.assertEqual(data['result'], self.param)
230 229
 
231 230
 		return d
232 231
 
... ...
@@ -243,11 +242,11 @@ class TestJSONRPCServer(unittest.TestCase):
243 242
 
244 243
 		@d.addCallback
245 244
 		def rendered(ignored, *a):
246
-			assert len(request.written) == 1
245
+			self.assertEqual(len(request.written), 1)
247 246
 			data = jsonrpc.jsonutil.decode(request.written[0])
248 247
 
249
-			assert data['id'] == self.id_
250
-			assert data.get('error', False)
248
+			self.assertEqual(data['id'], self.id_)
249
+			self.assertTrue(data.get('error', False))
251 250
 		return rendered
252 251
 
253 252
 	def test_batchcall(self):
... ...
@@ -265,13 +264,13 @@ class TestJSONRPCServer(unittest.TestCase):
265 264
 
266 265
 		@d.addCallback
267 266
 		def rendered(ignored, *a):
268
-			assert len(request.written) == 1
267
+			self.assertEqual(len(request.written), 1)
269 268
 			data = jsonrpc.jsonutil.decode(request.written[0])
270
-			assert len(data) == 2
271
-			assert set(x['id'] for x in data) == set("12")
272
-			assert set(x['result'] for x in data) == set([3,5])
269
+			self.assertEqual(len(data), 2)
270
+			self.assertEqual(set(x['id'] for x in data), set("12"))
271
+			self.assertEqual(set(x['result'] for x in data), set([3,5]))
273 272
 
274
-			assert not any(x.get('error', False) for x in data)
273
+			self.assertFalse(any(x.get('error', False) for x in data))
275 274
 		return rendered
276 275
 
277 276
 	def test_batchcall_1err(self):
... ...
@@ -289,13 +288,13 @@ class TestJSONRPCServer(unittest.TestCase):
289 288
 
290 289
 		@d.addCallback
291 290
 		def rendered(ignored, *a):
292
-			assert len(request.written) == 1
291
+			self.assertEqual(len(request.written), 1)
293 292
 			data = jsonrpc.jsonutil.decode(request.written[0])
294
-			assert len(data) == 2
295
-			assert set(x['id'] for x in data) == set("12")
296
-			assert set(x.get('result', False) for x in data) == set([3,False])
293
+			self.assertEqual(len(data), 2)
294
+			self.assertEqual(set(x['id'] for x in data), set("12"))
295
+			self.assertEqual(set(x.get('result', False) for x in data), set([3,False]))
297 296
 
298
-			assert len(filter(None, [x.get('error') for x in data])) == 1
297
+			self.assertEqual(len(filter(None, [x.get('error') for x in data])), 1)
299 298
 		return rendered
300 299
 
301 300
 
... ...
@@ -313,7 +312,7 @@ class TestJSONRPCServer(unittest.TestCase):
313 312
 		@d.addCallback
314 313
 		def rendered(ignored, *a):
315 314
 			data = jsonrpc.jsonutil.decode(request.written[0])
316
-			assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": None}
315
+			self.assertEqual(data, {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": None})
317 316
 		return rendered
318 317
 
319 318