
After reading
Julien Danjou's I found
out WSME (Web Service Made Easy) a Python framework which allows us to
easily create Web Services in Python. For
SILPA we needed a REST like interface and I thought
of giving it try as WSME readily advertised the Flask integration, and
this post was born when I read the documentation for Flask
integration.
First of all Flask is a nice framework which will right way allow
development of REST api for simple purposes, but my requirement was
bit complicated where I had to expose function in a separate python
modules through SILPA. I think detailed requirement can be part of
another post, so let me explain how to use WSME with Flask app.
WSME integration with Flask is done via decorator function
wsmeext.flask.signature which expects you to provide it with
signature of function to expose. And
here
is its documentation, basically signature of
signature function is
wsmeext.flask.signature(return_type, *arg_types, **options)
Yeah thats all docs have sadly.
So basically exposing is the only thing WSME handles for us here,
routing and other stuffs need to be done by Flask itself. So lets
consider a example, simple function to add as shown below.
def add(a, b):
return a + b
For providing REST like service, all you need below code.
from flask import Flask
from wsmeext.flask import signature
app = Flask(__name__)
@app.route('/add')
@signature(int, int, int)
def add(a, b):
return a + b
if __name__ == '__main__':
app.run()
So first argument to
signature is return type of function and rest
arguments are the argument to function to be exposed. Now you can
access the newly exposed service by visiting
http://localhost:5000/add but don't forget to pass the arguments
either via query string or through post. You can restrict methods of
access via Flask's
route.
So what's the big deal of not having docs right?.. Well fun part began
when we use bit more complex return type like
dictionaries or
lists . Below is modified code I'm using to demonstrate problem I
faced during using
dict as return type.
from flask import Flask
from wsmeext.flask import signature
app = Flask(__name__)
@app.route('/add')
@signature(dict, int, int)
def add(a, b):
return "result": a + b
if __name__ == '__main__':
app.run()
Basically I'm returning a dictionary containing result now, for
demonstration purpose. When I run the application boom python
barked at me with following message.
Traceback (most recent call last):
File "wsme_dummy.py", line 7, in <module>
@signature(dict, int, int)
File "c:\Users\invakam2\.virtualenvs\wsmetest\lib\site-packages\wsmeext\flask.py", line 48, in decorator
funcdef.resolve_types(wsme.types.registry)
File "c:\Users\invakam2\.virtualenvs\wsmetest\lib\site-packages\wsme\api.py", line 109, in resolve_types
self.return_type = registry.resolve_type(self.return_type)
File "c:\Users\invakam2\.virtualenvs\wsmetest\lib\site-packages\wsme\types.py", line 739, in resolve_type
type_ = self.register(type_)
File "c:\Users\invakam2\.virtualenvs\wsmetest\lib\site-packages\wsme\types.py", line 668, in register
class_._wsme_attributes = None
TypeError: can't set attributes of built-in/extension type 'dict'
After going through code from files involved in above traces this is
what I found
- wsmeext.flask.signature inturn uses wsme.signature which is
just alias of wsme.api.signature.
- Link in documentation in sentence See @signature for parameter
documentation is broken and should actually link to
wsme.signature in docs.
- wsme.signature actually calls resolve_type to check on types of
return and arguments. This function checks if types are instance of
dict or list in such cases it creates instances of
wsme.type.DictType and wsme.type.ArrayType respectively with
values from the argument.
- When I just passed built-in type dict the control went to else
part which just passed the type to wsme.type.Register.registry
function which tries to set the attribute _wsme_attribute which
actually raises TypeError as we can't set attribute for built-in
types.
So by inspecting code of
wsme.type.Registry.resolve_type and
wsme.type.Registry.register its clear that what signature expects
when arguments or return type is dictionary/list is instance of
dictionary/list with
type of value in it. May be sentence is bit
vague but I'm not sure how to put it more clearly, as an example in
our case add function returns dictioanry with key as string and value
as int, so return type argument for signature will be
str:
int . Similarly if you return array with int values it will be
[int].
With above understanding our add function now looks like below.
@signature( str: int , int, int)
def add(a, b):
return 'result': a + b
and now code worked just fine!. What I couldn't figure out here is
there is no way to have
tuple as return value or argument, but I
guess that is not big deal.
So immidiate task for me after finding this is fix the link in
documentation to point to
wsme.signature and probably put some note
some where in documentation about above finding.