I'm writing a client for a group of RESTful services. The body of the REST calls have the same XML structure, given parameters. There are several dozen calls, and I will not be implementing all of them. As such, I want to make them easy to specify and easy to use. The REST methods are grouped by functionality in separate modules and will need to share the same urllib2 opener for authentication and cookies. Here's an example of how a method is declared:
@rest_method('POST', '/document')
def createDocument(id, title, body):
# possibly some validation on the arguments
pass
All the developer has to care about is validation. The format of the XML (for POST and PUT) or the URL (for GET and DELETE) and the deserialization of the response is done in helper methods. The decorated methods are collected in a client object from which they will be executed and processed. For example:
c = RESTClient('http://foo.com', username, password)
c.createDocument(1, 'title', 'body')
The code is done. The only issue is in attaching the decorated methods to the client class. Although all the decorated methods can be seen in the client instance, they all share the same definition, namely the last one to be bound. Here's a brief example which duplicates the behaviour I'm seeing:
import types
class C(object): pass
def one(a): return a
def two(a, b): return a+b
def bracketit(t): return '(%s)' % t
c = C()
for m in (one, two):
new_method = lambda self, *args, **kwargs:\
bracketit(m(*args, **kwargs))
method = types.MethodType(new_method, c, C)
setattr(C, m.__name__, method)
print c.one
print c.two
print c.two(1, 2)
print c.one(1)
When I run this, I get the following output:
<bound method C.<lambda> of <__main__.C object at 0x1003b0d90>>
<bound method C.<lambda> of <__main__.C object at 0x1003b0d90>>
(3)
Traceback (most recent call last):
File "/tmp/test.py", line 19, in <module>
print c.one(1)
File "/tmp/test.py", line 12, in <lambda>
bracketit(m(*args, **kwargs))
TypeError: two() takes exactly 2 arguments (1 given)
I'm not sure why the two methods are bound in the same way. I haven't been able to find much documentation on how instancemethod binds methods to instances. What is going on underneath the hood, and how would I fix the above code so that the second call prints '(1)'?