Merge lp:~gt-launchpad/ladon/test into lp:ladon

Proposed by gthomas
Status: Needs review
Proposed branch: lp:~gt-launchpad/ladon/test
Merge into: lp:ladon
Diff against target: 493 lines (+240/-64)
7 files modified
frameworks/python/src/ladon/ladonizer/decorator.py (+16/-15)
frameworks/python/src/ladon/pf (+1/-0)
frameworks/python/src/ladon/tools/iterencode.py (+49/-0)
frameworks/python/src/ladon/types/__init__.py (+38/-13)
frameworks/python/tests/servicerunner.py (+6/-1)
frameworks/python/tests/services/typetests.py (+27/-1)
frameworks/python/tests/testladon.py (+103/-34)
To merge this branch: bzr merge lp:~gt-launchpad/ladon/test
Reviewer Review Type Date Requested Status
Ladon Developer Pending
Review via email: mp+72302@code.launchpad.net

Description of the change

overwritten from ~ladon-dev-team/ladon/ladon and took those soap xml changes.

To post a comment you must log in.
lp:~gt-launchpad/ladon/test updated
42. By Tamás Gulácsi <email address hidden>

add test for generator types (list of dicts)

43. By Tamás Gulácsi <email address hidden>

trying send generators

44. By Tamás Gulácsi <email address hidden>

jsonwsp: let generators pass through

45. By Tamás Gulácsi <email address hidden>

merged upstream

46. By Tamás Gulácsi <email address hidden>

merge upstream

47. By Tamás Gulácsi <email address hidden>

get rid of "exec" calls

48. By Tamás Gulácsi <email address hidden>

client/jsonwsp get-rid-of-exec, part II.

49. By Tamás Gulácsi <email address hidden>

generator simple test case works for jsonwsp

50. By Tamás Gulácsi <email address hidden>

merge upstream

51. By Tamás Gulácsi <email address hidden>

Merge upstream + add profiling capability to testladon.py + add iaggregate to wsgi

52. By Tamás Gulácsi <email address hidden>

merge upstream

53. By Tamás Gulácsi <email address hidden>

merged upstream

Unmerged revisions

53. By Tamás Gulácsi <email address hidden>

merged upstream

52. By Tamás Gulácsi <email address hidden>

merge upstream

51. By Tamás Gulácsi <email address hidden>

Merge upstream + add profiling capability to testladon.py + add iaggregate to wsgi

50. By Tamás Gulácsi <email address hidden>

merge upstream

49. By Tamás Gulácsi <email address hidden>

generator simple test case works for jsonwsp

48. By Tamás Gulácsi <email address hidden>

client/jsonwsp get-rid-of-exec, part II.

47. By Tamás Gulácsi <email address hidden>

get rid of "exec" calls

46. By Tamás Gulácsi <email address hidden>

merge upstream

45. By Tamás Gulácsi <email address hidden>

merged upstream

44. By Tamás Gulácsi <email address hidden>

jsonwsp: let generators pass through

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'frameworks/python/src/ladon/ladonizer/decorator.py'
2--- frameworks/python/src/ladon/ladonizer/decorator.py 2011-07-24 22:08:25 +0000
3+++ frameworks/python/src/ladon/ladonizer/decorator.py 2012-03-03 20:32:18 +0000
4@@ -1,4 +1,5 @@
5 # -*- coding: utf-8 -*-
6+# vim: set noet:
7
8 """
9 This module contains the entry-point for all Ladon Service methods.
10@@ -22,12 +23,12 @@
11 The ladonize decorator takes exactly the same amount of
12 arguments as the method it is decorating plus one mandatory
13 keyword argument *rtype* that type-defines the return type of
14- the method.
15+ the method.
16 Each argument given to the decorator type-defines the order-
17 wise corresponding parameter of the method being decorated::
18-
19+
20 class SessionService(object):
21-
22+
23 @ladonize(str,str,rtype=str)
24 def login(self,username,password):
25 ...
26@@ -52,15 +53,15 @@
27 user method is called. The result of the userspace-method is then checked before it is
28 passed back to the dispatcher.
29 """
30-
31+
32 # Get the LadonMethodInfo object generated at parsetime which is stored as a member
33 # on the function object
34 lmi = injector._ladon_method_info
35 # Reference the incomming arguments in callargs (filter out the function reference)
36 callargs = args[1:]
37-
38+
39 for argidx in range(len(callargs)):
40- # Check the type of each argument against the type which the method has been
41+ # Check the type of each argument against the type which the method has been
42 # registered to take in the order-wise corresponding argument
43 if not validate_type(lmi._arg_types[argidx],callargs[argidx]):
44 # Raise ArgTypeMismatch
45@@ -70,27 +71,27 @@
46 lmi._arg_names[argidx],
47 lmi._arg_types[argidx],
48 type(callargs[argidx]))
49-
50+
51 # Call the userspace service method (**kw will be used to transport Ladon info
52 # and tools all the way to userspace of the service method. I.e. the TypeConverter
53 # is passed with the keyword "LADON_METHOD_TC")
54 res = f(*args,**kw)
55-
56+
57 # Check the return type
58 if not validate_type(lmi._rtype,res):
59- # Raise Arg-type mismatch
60+ # Raise Arg-type mismatch
61 raise ReturnTypeMismatch(lmi.sinfo,lmi._func_name,lmi._rtype,type(res))
62-
63+
64 # Return the result to the dispatcher
65 return res
66-
67- # Register the service method and all the types required by it
68+
69+ # Register the service method and all the types required by it
70 ladon_method_info = global_service_collection().add_service_method(f,*def_args,**def_kw)
71-
72+
73 # store the LadonMethodInfo object directly on the fuction object
74 injector._ladon_method_info = ladon_method_info
75 injector.__doc__ = ladon_method_info._doc
76 injector.func_name = ladon_method_info._func_name
77 return injector
78-
79- return decorator
80+
81+ return decorator
82
83=== added file 'frameworks/python/src/ladon/pf'
84--- frameworks/python/src/ladon/pf 1970-01-01 00:00:00 +0000
85+++ frameworks/python/src/ladon/pf 2012-03-03 20:32:18 +0000
86@@ -0,0 +1,1 @@
87+exec pyflake --ignore=W191,E225,E303,E302,E231,E501,E202,E501,W391 "$@"
88
89=== added file 'frameworks/python/src/ladon/tools/iterencode.py'
90--- frameworks/python/src/ladon/tools/iterencode.py 1970-01-01 00:00:00 +0000
91+++ frameworks/python/src/ladon/tools/iterencode.py 2012-03-03 20:32:18 +0000
92@@ -0,0 +1,49 @@
93+#!/usr/bin/env python
94+# coding: utf-8
95+# vim: set noet:
96+
97+import logging
98+LOG = logging.getLogger(__name__)
99+import json
100+import types
101+
102+list_types = (tuple, list, types.GeneratorType)
103+
104+class GeneratorAsList(list):
105+ def __init__(self, generator):
106+ self._generator = generator
107+
108+ def __nonzero__(self):
109+ return bool(self._generator)
110+
111+ def __iter__(self):
112+ return self._generator
113+
114+ def __len__(self):
115+ raise NotImplementedError
116+
117+class JSONIterEncoder(json.JSONEncoder):
118+ def default(self, obj):
119+ #LOG.debug('default: %r %s', obj, type(obj))
120+ if isinstance(obj, list_types):
121+ return GeneratorAsList(obj)
122+ else:
123+ super(self.__class__, self).default(obj)
124+
125+
126+def iaggregate(iterobj, chunksize=8192):
127+ '''aggregates iter chunks to the given size'''
128+ n = 0
129+ data = []
130+ for chunk in iterobj:
131+ data.append(chunk)
132+ n += len(chunk)
133+ if n >= chunksize:
134+ #print repr(data)
135+ yield ''.join(data)
136+ data = []
137+ n = 0
138+ #
139+ if data:
140+ yield ''.join(data)
141+
142
143=== modified file 'frameworks/python/src/ladon/types/__init__.py'
144--- frameworks/python/src/ladon/types/__init__.py 2011-07-11 09:37:30 +0000
145+++ frameworks/python/src/ladon/types/__init__.py 2012-03-03 20:32:18 +0000
146@@ -1,16 +1,39 @@
147 # -*- coding: utf-8 -*-
148+# vim: se noet:
149+
150+import types
151+list_types = (tuple, list, types.GeneratorType)
152+import logging
153+LOG = logging.getLogger(__name__)
154+import itertools
155+
156+
157+def generator_peek(generator):
158+ '''returns a (first_item, full_generator) tuple'''
159+ try:
160+ first = generator.next()
161+ except StopIteration:
162+ return (None, [])
163+ else:
164+ return (first, itertools.chain([first], generator))
165+
166
167 def validate_type(typ,val):
168- if [tuple,list].count(type(typ)):
169- if not [tuple,list].count(type(val)):
170- return False
171- for i in val:
172- if type(i)!=typ[0]:
173- return False
174+ # LOG.debug('validate_type(%r, %s)', typ, type(val))
175+ ok = False
176+ if isinstance(typ, list_types):
177+ # LOG.debug('val=%r %s %r', val, isinstance(val, list_types), typ[0])
178+ if isinstance(val, (tuple, list)):
179+ ok = all(isinstance(i, typ[0]) for i in val)
180+ else:
181+ first, val = generator_peek(val)
182+ ok = not val or isinstance(first, typ[0])
183 else:
184- if typ!=type(val):
185- return False
186- return True
187+ ok = isinstance(val, typ)
188+ if not ok:
189+ LOG.warn('%s is NOT %r', type(val), typ)
190+ return ok
191+
192
193 def get_type_info(typ):
194 """
195@@ -18,7 +41,9 @@
196 the type dict will be returned, otherwise None is returned.
197 """
198 from ladon.types.typemanager import TypeManager
199- if not [list,tuple].count(type(typ)) and typ in TypeManager.global_type_dict:
200- return TypeManager.global_type_dict[typ]
201- else:
202- return None
203+ info = None
204+ if not isinstance(typ, list_types) and typ in TypeManager.global_type_dict:
205+ info = TypeManager.global_type_dict[typ]
206+ return info
207+
208+__all__ = ['validate_type', 'get_type_info', 'list_types', 'generator_peek']
209
210=== modified file 'frameworks/python/tests/servicerunner.py'
211--- frameworks/python/tests/servicerunner.py 2011-07-11 09:37:30 +0000
212+++ frameworks/python/tests/servicerunner.py 2012-03-03 20:32:18 +0000
213@@ -1,4 +1,5 @@
214 # -*- coding: utf-8 -*-
215+# vim: set noet:
216
217 from ladon.server.wsgi import LadonWSGIApplication
218 import wsgiref.simple_server
219@@ -34,6 +35,10 @@
220 server = make_server()
221 server.serve_forever()
222 return None
223-
224+
225 if __name__=='__main__':
226+ import sys
227+ import logging
228+ logging.basicConfig(level=logging.DEBUG if '-v' in sys.argv[1:]
229+ else logging.INFO)
230 serve_test_service()
231
232=== modified file 'frameworks/python/tests/services/typetests.py'
233--- frameworks/python/tests/services/typetests.py 2011-07-11 09:37:30 +0000
234+++ frameworks/python/tests/services/typetests.py 2012-03-03 20:32:18 +0000
235@@ -1,6 +1,9 @@
236 # -*- coding: utf-8 -*-
237+# vim: set noet:
238 from ladon.ladonizer import ladonize
239 from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING,PORTABLE_STRING_TYPES
240+import types
241+from ladon.types.ladontype import LadonType
242 # Portable types are defined like this in ladon.compat:
243 #
244 #if sys.version_info[0]==2:
245@@ -12,9 +15,32 @@
246 #PORTABLE_STRING = str
247 #PORTABLE_STRING_TYPES = [str,bytes]
248
249+c_keys = ['%s_%02d' % (k, i) for i in range(20) for k in ('bytes', 'str')]
250+
251+
252+class Record(LadonType):
253+ __slots__ = c_keys
254+
255+ locals().update(dict((k,
256+ PORTABLE_BYTES if k.startswith('bytes_') else PORTABLE_STRING)
257+ for k in c_keys))
258+
259+ def __init__(self, adict={}):
260+ for k, v in adict.iteritems():
261+ setattr(self, k, v)
262+
263+
264 class TypeTestService(object):
265-
266+
267 @ladonize(PORTABLE_BYTES,rtype=PORTABLE_BYTES,encoding='utf-8')
268 def conversion(self,in_bytes):
269 in_str = in_bytes.decode('utf-8')
270 return in_str.encode('utf-8')
271+
272+ @ladonize(PORTABLE_BYTES, int, rtype=[Record], encoding='utf-8')
273+ def gen_dicts(self, in_bytes, count=1000):
274+ import itertools
275+ in_str = in_bytes.decode('utf-8')
276+ values = itertools.cycle((in_bytes, in_str))
277+ rec = Record(dict((k, values.next()) for k in c_keys))
278+ return (rec for _ in xrange(count))
279
280=== modified file 'frameworks/python/tests/testladon.py'
281--- frameworks/python/tests/testladon.py 2011-07-11 09:37:30 +0000
282+++ frameworks/python/tests/testladon.py 2012-03-03 20:32:18 +0000
283@@ -1,4 +1,5 @@
284 # -*- coding: utf-8 -*-
285+# vim: set noet:
286
287 import unittest
288 import servicerunner
289@@ -14,6 +15,9 @@
290 import sys,json
291 from ladon.compat import PORTABLE_BYTES,PORTABLE_STRING,PORTABLE_STRING_TYPES
292
293+import logging
294+LOG = logging.getLogger(__name__)
295+
296 def str_to_portable_string(in_str):
297 """
298 Assumes that we always use UTF-8 encoding in script files
299@@ -25,7 +29,7 @@
300
301
302 class HTTPRequestPoster(object):
303-
304+
305 def __init__(self,url):
306 self.valid_url = True
307 parseres = urlparse(url)
308@@ -40,7 +44,7 @@
309 if str(custom_port).isdigit():
310 self.port = int(custom_port)
311 self.path = parseres.path
312-
313+
314 def post_request(self,data,extra_path="jsonwsp",encoding="utf-8"):
315 headers = {
316 "Content-type": "application/json, charset=%s" % encoding,
317@@ -63,29 +67,30 @@
318 def setUp(self):
319 self.post_helper = HTTPRequestPoster('http://localhost:2376/StringTestService')
320
321- def string_integrety_tests_json(self,methodname):
322+ def string_integrity_tests_json(self,methodname):
323 fp_req = open('data/stringtests/in_out_test.json','rb')
324 req = PORTABLE_STRING(fp_req.read(),'utf-8')
325 fp_req.close()
326 req = json.loads(req)
327 req['methodname'] = methodname
328 req = json.dumps(req)
329-
330+
331 # utf-8 encoded request to bytesEncodingTest
332 status,reason,resdata = self.post_helper.post_request(req.encode('utf-8'),encoding='utf-8')
333- self.assertEqual(status, 200)
334+ self.assertEqual(status, 200, resdata)
335+ LOG.debug('resdata: %r', resdata)
336 res = json.loads(PORTABLE_STRING(resdata,'utf-8'))
337 expected_result = str_to_portable_string('äöüÄÖÄæøåÆØÅß')
338 self.assertEqual(res['result'], expected_result)
339-
340+
341 # latin-1 encoded request to bytesEncodingTest
342 status,reason,resdata = self.post_helper.post_request(req.encode('latin-1'),encoding='latin-1')
343- self.assertEqual(status, 200)
344+ self.assertEqual(status, 200, resdata)
345 res = json.loads(PORTABLE_STRING(resdata,'latin-1'))
346 self.assertEqual(res['result'], expected_result)
347
348
349- def string_integrety_tests_soap(self,methodname):
350+ def string_integrity_tests_soap(self,methodname,mangler=None):
351 fp_req = open('data/stringtests/in_out_test.soap','rb')
352 req = fp_req.read()
353 fp_req.close()
354@@ -94,50 +99,114 @@
355
356 expected_result = str_to_portable_string('äöüÄÖÄæøåÆØÅß')
357
358+ def toxml(xml, encoding='utf-8'):
359+ text = xml.toxml(encoding=encoding)
360+ if mangler:
361+ text = mangler(text)
362+ LOG.debug('toxml: %r\n%s', text, text)
363+ return text
364 # utf-8 encoded request to bytesEncodingTest
365- status,reason,resdata = self.post_helper.post_request(req.toxml(encoding='utf-8'),extra_path='soap',encoding='utf-8')
366- self.assertEqual(status, 200)
367+ status,reason,resdata = self.post_helper.post_request(toxml(req, 'utf-8'),extra_path='soap',encoding='utf-8')
368+ self.assertEqual(status, 200, resdata)
369 res = md.parseString(resdata)
370 result_string = res.getElementsByTagName('result')[0].childNodes[0].data
371 self.assertEqual(result_string, expected_result)
372-
373+
374 # latin-1 encoded request to bytesEncodingTest
375- status,reason,resdata = self.post_helper.post_request(req.toxml(encoding='latin-1'),extra_path='soap',encoding='latin-1')
376- self.assertEqual(status, 200)
377+ status,reason,resdata = self.post_helper.post_request(toxml(req, 'latin-1'),extra_path='soap',encoding='latin-1')
378+ self.assertEqual(status, 200, resdata)
379 res = md.parseString(resdata)
380 result_string = res.getElementsByTagName('result')[0].childNodes[0].data
381 self.assertEqual(result_string, expected_result)
382
383 def test_bytes_in_bytes_out_json(self):
384- self.string_integrety_tests_json('bytes_in_bytes_out')
385-
386+ self.string_integrity_tests_json('bytes_in_bytes_out')
387+
388 def test_bytes_in_uni_out_json(self):
389- self.string_integrety_tests_json('bytes_in_uni_out')
390-
391+ self.string_integrity_tests_json('bytes_in_uni_out')
392+
393 def test_uni_in_bytes_out_json(self):
394- self.string_integrety_tests_json('uni_in_bytes_out')
395-
396+ self.string_integrity_tests_json('uni_in_bytes_out')
397+
398 def test_uni_in_uni_out_json(self):
399- self.string_integrety_tests_json('uni_in_uni_out')
400+ self.string_integrity_tests_json('uni_in_uni_out')
401
402 def test_bytes_in_bytes_out_soap(self):
403- self.string_integrety_tests_soap('bytes_in_bytes_out')
404-
405+ self.string_integrity_tests_soap('bytes_in_bytes_out')
406+
407 def test_bytes_in_uni_out_soap(self):
408- self.string_integrety_tests_soap('bytes_in_uni_out')
409-
410+ self.string_integrity_tests_soap('bytes_in_uni_out')
411+
412 def test_uni_in_bytes_out_soap(self):
413- self.string_integrety_tests_soap('uni_in_bytes_out')
414-
415+ self.string_integrity_tests_soap('uni_in_bytes_out')
416+
417 def test_uni_in_uni_out_soap(self):
418- self.string_integrety_tests_soap('uni_in_uni_out')
419-
420-
421+ self.string_integrity_tests_soap('uni_in_uni_out')
422+
423+ def test_whitespace_soap(self):
424+ def mangler(text):
425+ return text.replace(' ', ' \n ').replace('><', '>\n<')
426+ self.string_integrity_tests_soap('uni_in_uni_out', mangler)
427+
428+ def test_generators(self):
429+ argdict = {'methodname': 'gen_dicts', 'args': {'in_bytes': '0123456789ABCDEFG', 'count': 999}}
430+ post_helper = HTTPRequestPoster('http://localhost:2376/TypeTestService')
431+ status,reason,resdata = post_helper.post_request(json.dumps(argdict),encoding='utf-8')
432+ self.assertEqual(status, 200, resdata)
433+ LOG.debug('resdata: %r', resdata)
434+
435+
436+def is_port_open(port, host='', timeout=1):
437+ import socket
438+ sock = socket.socket()
439+ sock.settimeout(timeout)
440+ try:
441+ sock.connect((host, port))
442+ sock.close()
443+ except:
444+ return False
445+ else:
446+ return True
447
448 if __name__ == '__main__':
449- import servicerunner
450- servicerunner
451- service_thread = servicerunner.serve_test_service(as_thread=True)
452- unittest.main(exit=False)
453- service_thread.server.shutdown()
454+ try:
455+ import attachmenttests
456+ except ImportError:
457+ import sys, os
458+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
459+ 'data')))
460+ try:
461+ from argparse import ArgumentParser
462+ op = ArgumentParser()
463+ op.add_argument('-v', '--verbose', action='store_true', default=False)
464+ op.add_argument('--profile', action='store_true', default=False)
465+ opts, args = op.parse_known_args()
466+ except ImportError:
467+ from optparse import OptionParser
468+ op = OptionParser()
469+ op.add_option('-v', '--verbose', action='store_true', default=False)
470+ op.add_option('--profile', action='store_true', default=False)
471+ opts, args = op.parse_args()
472+
473+ sys.argv[1:] = args
474+ logging.basicConfig(level=(logging.DEBUG if opts.verbose else logging.INFO))
475+ if is_port_open(2376): # already running service
476+ service_thread = None
477+ LOG.warn('using already running service!')
478+ else:
479+ import servicerunner
480+ service_thread = servicerunner.serve_test_service(as_thread=True)
481+ try:
482+ if opts.profile:
483+ prof_fn = 'testladon.prof'
484+ import cProfile
485+ cProfile.run('unittest.main(exit=False)', prof_fn)
486+ import pstats
487+ p = pstats.Stats(prof_fn)
488+ p.sort_stats('cumulative').print_stats(100)
489+ else:
490+ unittest.main(exit=False)
491+ finally:
492+ if service_thread:
493+ service_thread.server.shutdown()
494

Subscribers

People subscribed via source and target branches