pythonsyntaxcherrypypyamf

kwargs sent over pyAMF channel


I'm using cherrypy server to receive requests over a pyAMF channel from a python client. I started with the mock up below and it works fine:

Server:

import cherrypy
from pyamf.remoting.gateway.wsgi import WSGIGateway

def echo(*args, **kwargs):
    return (args, kwargs)

class Root(object):
    def index(self):
        return "running"
    index.exposed = True

services = {
   'myService.echo': echo,
}

gateway = WSGIGateway(services, debug=True)

cherrypy.tree.graft(gateway, "/gateway/")
cherrypy.quickstart(Root())

Client:

from pyamf.remoting.client import RemotingService

path = 'http://localhost:8080/gateway/'
gw = RemotingService(path)
service = gw.getService('myService')

print service.echo('one=1, two=3')

Result: [[u'one=1, two=3'], {}]

now if instead of:

def echo(*args, **kwargs):
    return (args, kwargs)

I use:

def echo(**kwargs):
    return kwargs

and send the same request, I get the following error:

TypeError: echo() takes exactly 0 arguments (1 given)

while at the same time:

>>> def f(**kwargs): return kwargs
... 
>>> f(one=1, two=3)
{'two': 3, 'one': 1}
>>> 

Question: Why is this happening? Please share insights

I'm using: python 2.5.2, cherrypy 3.1.2, pyamf 0.5.1


Solution

  • By default, WSGIGateway sets expose_request=True which means that the WSGI environ dict is set as the first argument to any service method in that gateway.

    This means that echo should be written as:

    def echo(environ, *args):
        return args
    

    PyAMF provides a decorator which allows you to forcibly expose the request even if expose_request=False, an example:

    from pyamf.remoting.gateway import expose_request
    from pyamf.remoting.gateway.wsgi import WSGIGateway
    
    @expose_request
    def some_service_method(request, *args):
        return ['some', 'thing']
    
    services = {
        'a_service_method': some_service_method
    }
    
    gw = WSGIGateway(services, expose_request=False)
    

    Hope that clarifies why you are getting the TypeError in this case.

    You correctly point out that you cannot supply **kwargs directly in a PyAMF client/server call but you can use default named parameters:

    def update(obj, force=False):
        pass
    

    Then you can access the service:

    from pyamf.remoting.client import RemotingService
    
    path = 'http://localhost:8080/gateway/'
    gw = RemotingService(path)
    service = gw.getService('myService')
    
    print service.update('foo', True)