views:

164

answers:

2

Hello,

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

+2  A: 

Notice that with your first echo function, the only way to get the results you do is when it is called this way:

echo(u"one=1, two=3")
# in words: one unicode string literal, as a positional arg

# *very* different from:
echo(one=1, two=3) # which seems to be what you expect

Because of this, you must write echo to accept positional arguments or change how it is called.

Roger Pate
@Roger Pate: thanks, that clarifies this a bit. If this message prefix is something pyAMF always adds, I can get rid of it on the server side, but what's the meaning of this 'u'?
Dragan Chupacabrovic
@Roger: if i understand this correctly, if i'm dealing w pyamf, ** is useless, since i'd never get my key value pairs, but instead i'd get u[params]. i should stick to using *args and parse the params myself. True or False?
Dragan Chupacabrovic
That would be Unicode string in ancient versions of Python. In v. 3.1 (probably 2.7 as well) all strings are Unicode.
Hamish Grubijan
Looks to me like you are expecting a single argument (a string)
Hamish Grubijan
thanks guys this was helpful
Dragan Chupacabrovic
Ipthnc is right about `u""` (but 2.x is hardly even close to ancient yet), they are `unicode` (as in the Python type, named after Unicode, the standard) literals; however, 2.7 will maintain backwards compatibility with 2.x, but already in 2.6 there is `__future__.unicode_literals`.
Roger Pate
Dragan: I'm not familiar with pyamf, perhaps someone can provide a better answer (and you should accept it if they do), but I could tell from your well-asked question how echo was being called, and how it differed from what you expected.
Roger Pate
+2  A: 

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)
njoyce