From 18198eb869806275767509852cf2ed1acd04a79c Mon Sep 17 00:00:00 2001 From: "Michael J. Pedersen" Date: Thu, 6 May 2010 00:58:06 -0400 Subject: [PATCH] system. methods are now supported. Code is ready for use, but still needs documenting. --- tgext.xmlrpc.wpu | 429 ++++++++++++++------------ tgext/xmlrpc/controllers/__init__.py | 58 +++- tgext/xmlrpc/test/controllers/root.py | 4 +- tgext/xmlrpc/test/test_controller.py | 29 ++ 4 files changed, 316 insertions(+), 204 deletions(-) diff --git a/tgext.xmlrpc.wpu b/tgext.xmlrpc.wpu index 0b04a42..08aeb2f 100644 --- a/tgext.xmlrpc.wpu +++ b/tgext.xmlrpc.wpu @@ -4,10 +4,6 @@ # Wing IDE project file : User-specific branch # ################################################################## [user attributes] -debug.breakpoints = {loc('tgext/xmlrpc/test/test_controller.py'): {51: (0, - None, - 1, - 0)}} debug.err-values = {None: {}, loc('../../../../usr/lib/wingide3.2/src/testing/runners/run_nosetests_xml.py'): {}} debug.var-col-widths = [0.40536277602523657, @@ -70,277 +66,290 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window', 'primary_view_state': {'editor_states': {'bookmarks': ([(loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75), + ('XmlRpcController._system_listMethods.gatherMeth'\ + 'ods', + 78)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 76, - 'sel-line-start': 2590, - 'selection_end': 2590, - 'selection_start': 2590}, - 1273117526.4031119), + 'sel-line': 86, + 'sel-line-start': 2897, + 'selection_end': 2954, + 'selection_start': 2954}, + 1273121397.9797959), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75), + ('XmlRpcController._system_listMethods.gatherMet'\ + 'hods', + 78)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 76, - 'sel-line-start': 2590, - 'selection_end': 2672, - 'selection_start': 2672}, - 1273117529.403482), + 'sel-line': 85, + 'sel-line-start': 2842, + 'selection_end': 2896, + 'selection_start': 2896}, + 1273121422.164367), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75), + ('XmlRpcController._system_listMethods.gatherMet'\ + 'hods', + 78)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 71, - 'sel-line-start': 2398, - 'selection_end': 2398, - 'selection_start': 2398}, - 1273117570.5117171), + 'sel-line': 86, + 'sel-line-start': 2897, + 'selection_end': 2938, + 'selection_start': 2938}, + 1273121449.124222), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75), + ('XmlRpcController._system_listMethods.gatherMet'\ + 'hods', + 78)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 72, - 'sel-line-start': 2432, - 'selection_end': 2432, - 'selection_start': 2432}, - 1273117570.903069), + 'sel-line': 87, + 'sel-line-start': 2956, + 'selection_end': 2956, + 'selection_start': 2956}, + 1273121462.279243), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 73, - 'sel-line-start': 2459, - 'selection_end': 2459, - 'selection_start': 2459}, - 1273117571.9100201), + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3007, + 'selection_start': 3007}, + 1273121469.7918451), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 64, + 'folded-linenos': [], + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3061, + 'selection_start': 3061}, + 1273121474.1063049), + (loc('tgext/xmlrpc/test/controllers/root.py'), + {'attrib-starts': [('TestRpcController', + 11)], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 14, + 'sel-line-start': 468, + 'selection_end': 495, + 'selection_start': 495}, + 1273121500.0380731), + (loc('tgext/xmlrpc/test/test_controller.py'), + {'attrib-starts': [('TestXmlRpcController', + 34), + ('TestXmlRpcController.test_system_listmethods', + 52)], + 'first-line': 37, 'folded-linenos': [], - 'sel-line': 74, - 'sel-line-start': 2510, - 'selection_end': 2510, - 'selection_start': 2510}, - 1273117572.419673), + 'sel-line': 56, + 'sel-line-start': 2199, + 'selection_end': 2243, + 'selection_start': 2243}, + 1273121500.232759), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 64, 'folded-linenos': [], - 'sel-line': 75, - 'sel-line-start': 2533, - 'selection_end': 2533, - 'selection_start': 2533}, - 1273117573.4107881), + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3018, + 'selection_start': 3018}, + 1273121547.8343551), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 76, - 'sel-line-start': 2590, - 'selection_end': 2590, - 'selection_start': 2590}, - 1273117573.9178641), + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3031, + 'selection_start': 3031}, + 1273121555.0733931), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 71, - 'sel-line-start': 2398, - 'selection_end': 2398, - 'selection_start': 2398}, - 1273117574.908329), + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3035, + 'selection_start': 3035}, + 1273121594.9987521), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._system_listMethods', + 75)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 72, - 'sel-line-start': 2432, - 'selection_end': 2432, - 'selection_start': 2432}, - 1273117576.4206541), + 'sel-line': 89, + 'sel-line-start': 2986, + 'selection_end': 3040, + 'selection_start': 3040}, + 1273121595.186727), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._gather_all_methods', + 84)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 82, - 'sel-line-start': 2920, - 'selection_end': 2920, - 'selection_start': 2920}, - 1273117580.9237871), + 'sel-line': 93, + 'sel-line-start': 3063, + 'selection_end': 3092, + 'selection_start': 3092}, + 1273121595.34517), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 82, - 'sel-line-start': 2920, - 'selection_end': 2938, - 'selection_start': 2938}, - 1273117586.9225049), + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2622, + 'selection_start': 2622}, + 1273121711.584764), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 82, - 'sel-line-start': 2920, - 'selection_end': 2940, - 'selection_start': 2940}, - 1273117594.9924941), + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2642, + 'selection_start': 2642}, + 1273121799.2306941), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 82, - 'sel-line-start': 2920, - 'selection_end': 2948, - 'selection_start': 2948}, - 1273117595.2270629), + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2643, + 'selection_start': 2643}, + 1273121799.432482), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 78, - 'sel-line-start': 2701, - 'selection_end': 2778, - 'selection_start': 2766}, - 1273117595.4605911), + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2649, + 'selection_start': 2649}, + 1273121799.585665), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 43, - 'folded-linenos': [], - 'sel-line': 82, - 'sel-line-start': 2908, - 'selection_end': 2949, - 'selection_start': 2937}, - 1273117601.986285), - (loc('tgext/xmlrpc/controllers/__init__.py'), - {'attrib-starts': [], - 'first-line': 0, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 0, - 'sel-line-start': 0, - 'selection_end': 1, - 'selection_start': 1}, - 1273117608.235532), + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2650, + 'selection_start': 2650}, + 1273121799.724463), (loc('tgext/xmlrpc/controllers/__init__.py'), {'attrib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 44, - 'folded-linenos': [], - 'sel-line': 71, - 'sel-line-start': 2398, - 'selection_end': 2415, - 'selection_start': 2415}, - 1273117616.7941439), - (loc('tgext/xmlrpc/test/test_controller.py'), - {'attrib-starts': [('TestXmlRpcController', - 34), - ('TestXmlRpcController.test_subrpc', - 47)], - 'first-line': 21, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 49, - 'sel-line-start': 1882, - 'selection_end': 1917, - 'selection_start': 1917}, - 1273117643.4081891), - [loc('tgext/xmlrpc/test/test_controller.py'), - {'attrib-starts': [('TestXmlRpcController', - 34), - ('TestXmlRpcController.test_subrpc', - 47)], - 'first-line': 21, + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2661, + 'selection_start': 2661}, + 1273121799.8568611), + [loc('tgext/xmlrpc/controllers/__init__.py'), + {'attrib-starts': [('XmlRpcController', + 47), + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 49, - 'sel-line-start': 1882, - 'selection_end': 1882, - 'selection_start': 1882}, - 1273117657.6482799]], + 'sel-line': 79, + 'sel-line-start': 2615, + 'selection_end': 2661, + 'selection_start': 2661}, + 1273121799.98176]], 19), - 'current-loc': loc('tgext/xmlrpc/test/test_controller.py'), + 'current-loc': loc('tgext/xmlrpc/controllers/__init__.py'), 'editor-states': {loc('tgext/xmlrpc/controllers/__init__.py'): {'att'\ 'rib-starts': [('XmlRpcController', 47), - ('XmlRpcController._dispatch', - 56)], - 'first-line': 44, + ('XmlRpcController._gather_all_methods', + 79)], + 'first-line': 52, 'folded-linenos': [], - 'sel-line': 71, - 'sel-line-start': 2398, - 'selection_end': 2415, - 'selection_start': 2415}, + 'sel-line': 88, + 'sel-line-start': 2995, + 'selection_end': 3031, + 'selection_start': 3031}, loc('tgext/xmlrpc/test/controllers/root.py'): {'at'\ 'trib-starts': [('TestRpcController', 11)], 'first-line': 0, 'folded-linenos': [], - 'sel-line': 13, - 'sel-line-start': 414, - 'selection_end': 418, - 'selection_start': 418}, + 'sel-line': 14, + 'sel-line-start': 468, + 'selection_end': 495, + 'selection_start': 495}, loc('tgext/xmlrpc/test/test_controller.py'): {'att'\ 'rib-starts': [('TestXmlRpcController', 34), - ('TestXmlRpcController.test_subrpc', - 47)], - 'first-line': 21, + ('TestXmlRpcController.test_system_listmethods', + 52)], + 'first-line': 37, 'folded-linenos': [], - 'sel-line': 49, - 'sel-line-start': 1882, - 'selection_end': 1923, - 'selection_start': 1923}}, + 'sel-line': 56, + 'sel-line-start': 2199, + 'selection_end': 2243, + 'selection_start': 2243}}, 'has-focus': True}, 'open_files': [u'tgext/xmlrpc/test/controllers/root.py', - u'tgext/xmlrpc/controllers/__init__.py', - u'tgext/xmlrpc/test/test_controller.py']}, + u'tgext/xmlrpc/test/test_controller.py', + u'tgext/xmlrpc/controllers/__init__.py']}, 'split_percents': {0: 0.33735909822866345}, 'splits': 1, 'tab_location': 'top', @@ -438,7 +447,6 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window', 'method.__class__\n', 'method.__func__\n', 'p[0]\n', - 'p\n', 'kw\n', 'from tg import request\n', 'request.body\n', @@ -472,12 +480,31 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window', 'resp[0][0]\n', 'mvals\n', 'method\n', + 'p\n', + 'self._find_method(p[0])\n', + "self._find_method('subrpc.joinit')\n", 'resp\n', - 'resp.body\n']}, - 'sel-line': 27, - 'sel-line-start': 1329, - 'selection_end': 1333, - 'selection_start': 1333}), + 'resp.body\n', + 'xmlrpclib.loads(resp.body)\n', + 'dir(self)\n', + 'filter(lambda x: True, dir(self))\n', + "filter(lambda x: hasattr(getattr(self, x), 'signatures'), dir(self)"\ + ")\n", + "filter(lambda x: hasattr(x, 'signatures'), dir(self))\n", + "filter(lambda x: hasattr(self.x, 'signatures'), dir(self))\n", + 'currmethods[0].__name__\n', + 'dir(controller)\n', + 'filter(lambda x: True, dir(controller))\n', + "filter(lambda x: hasattr(getattr(controller, x), 'signatures'), dir"\ + "(controller))\n", + 'currmethods\n', + 'currmethods[0]\n', + 'c=currmethods[0]\n', + 'methods\n']}, + 'sel-line': 11, + 'sel-line-start': 566, + 'selection_end': 570, + 'selection_start': 570}), ('debug-exceptions', 'window-wide', 0, @@ -605,8 +632,8 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window', 38, 1918, 1040)}]} -guimgr.recent-documents = [loc('tgext/xmlrpc/test/test_controller.py'), - loc('tgext/xmlrpc/controllers/__init__.py'), +guimgr.recent-documents = [loc('tgext/xmlrpc/controllers/__init__.py'), + loc('tgext/xmlrpc/test/test_controller.py'), loc('tgext/xmlrpc/test/controllers/root.py')] guimgr.visual-state = {loc('setup.py'): {'attrib-starts': [], 'first-line': 0, diff --git a/tgext/xmlrpc/controllers/__init__.py b/tgext/xmlrpc/controllers/__init__.py index 36dee18..c9591d4 100644 --- a/tgext/xmlrpc/controllers/__init__.py +++ b/tgext/xmlrpc/controllers/__init__.py @@ -54,6 +54,55 @@ def index(self): def rpcfault(self, *p, **kw): return xmlrpclib.dumps(xmlrpclib.Fault(1, p[0])) + @expose(content_type='text/xml') + def _system_methodHelp(self, *p, **kw): + try: + method = self._find_method(p[0]) + rpcresponse = xmlrpclib.dumps((method.helpstr,), methodresponse=1) + except: + rpcresponse = xmlrpclib.dumps(('Invalid method',), methodresponse=1) + return rpcresponse + + @expose(content_type='text/xml') + def _system_methodSignature(self, *p, **kw): + try: + method = self._find_method(p[0]) + rpcresponse = xmlrpclib.dumps((method.signatures,), methodresponse=1) + except: + rpcresponse = xmlrpclib.dumps(('Invalid method',), methodresponse=1) + return rpcresponse + + @expose(content_type='text/xml') + def _system_listMethods(self, *p, **kw): + methods = self._gather_all_methods('', self) + return xmlrpclib.dumps((methods,), methodresponse=1) + + def _gather_all_methods(self, prefix, controller): + methods = [] + if prefix != '': + prefix = prefix + '.' + for attrname in dir(controller): + attr = getattr(controller, attrname) + if hasattr(attr, 'signatures'): + methods.append('%s%s' % (prefix, attrname)) + if isinstance(attr, XmlRpcController): + methods.extend(self._gather_all_methods(prefix + attrname, attr)) + return methods + + def _find_method(self, method): + mvals = method.split('.') + midx = 0 + controller = self + result = None + while not result and controller and midx < len(mvals): + controller = getattr(controller, mvals[midx], None) + if controller is None: + break + if midx == len(mvals)-1: + result = controller + midx = midx + 1 + return result + def _dispatch(self, state, remainder, parms=None, method=None): if remainder: return self._dispatch_first_found_default_or_lookup(state, remainder) @@ -68,7 +117,14 @@ def _dispatch(self, state, remainder, parms=None, method=None): state.add_method(self.rpcfault, ['Unable to decode request body "||%s||"' % (request.body)]) return state - # TODO: Check for special methods (help/etc) and return them when appropriate + if method.startswith('system.'): + methodname = '_%s' % (method.replace('.', '_')) + if not getattr(self, methodname, None): + state.add_method(self.rpcfault, ['Invalid system method called: %s' % (method)]) + else: + state.add_method(getattr(self, methodname), parms) + return state + mvals = method.split('.') if len(mvals) > 1: subcon = getattr(self, mvals[0], None) diff --git a/tgext/xmlrpc/test/controllers/root.py b/tgext/xmlrpc/test/controllers/root.py index 5f97fe0..f45f674 100644 --- a/tgext/xmlrpc/test/controllers/root.py +++ b/tgext/xmlrpc/test/controllers/root.py @@ -5,14 +5,14 @@ from repoze.what.predicates import Not, is_anonymous, has_permission class TestRpcSubController(XmlRpcController): - @xmlrpc([]) + @xmlrpc([], helpstr='joins an array of strings with spaces') def joinit(self, *p, **kw): return " ".join(p) class TestRpcController(XmlRpcController): subrpc = TestRpcSubController() - @xmlrpc([]) + @xmlrpc(['int', 'array'], helpstr='sums an array of numbers') def addit(self, *p, **kw): return sum(p) diff --git a/tgext/xmlrpc/test/test_controller.py b/tgext/xmlrpc/test/test_controller.py index a802f4b..97a41c0 100644 --- a/tgext/xmlrpc/test/test_controller.py +++ b/tgext/xmlrpc/test/test_controller.py @@ -50,6 +50,35 @@ def test_subrpc(self): resp = xmlrpclib.loads(resp.body) assert resp[0][0] == 'hello world i mean it', resp + def test_system_listmethods(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('addit',), 'system.listMethods')) + assert 'addit' in resp, resp + assert 'genfault' in resp, resp + assert 'subrpc.joinit' in resp, resp + + def test_system_methodsignature(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('addit',), 'system.methodSignature')) + assert 'int' in resp, resp + assert 'string' in resp, resp + + def test_system_methodhelp(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('addit',), 'system.methodHelp')) + assert 'sums an array of numbers' in resp, resp + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('subrpc.joinit',), 'system.methodHelp')) + assert 'joins an array of strings with spaces' in resp, resp + + def test_system_methodsignature_bad(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('subrpc.nonexistant',), 'system.methodSignature')) + assert 'Invalid method' in resp, resp + + def test_system_methodhelp_bad(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps(('subrpc.nonexistant',), 'system.methodHelp')) + assert 'Invalid method' in resp, resp + + def test_system_badcall(self): + resp = self.app.post('/xmlrpc', xmlrpclib.dumps((), 'system.badcall')) + assert 'Invalid system method called' in resp, resp + def test_xmlrpc_too_many_args(self): try: resp = self.app.get('/xmlrpc/myurl/what')