From 6b8349696d4a0dcceb23c5c307279e94182e3a27 Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Mon, 8 Apr 2013 23:27:15 +0200 Subject: [PATCH] Improve the documentation --HG-- extra : rebase_source : 24b7ddcbfb6fb739787292db49148d3234d6f67a --- doc/api.rst | 9 +-- doc/changes.rst | 4 +- doc/functions.rst | 182 ++++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 1 + doc/integrate.rst | 9 ++- doc/protocols.rst | 5 ++ wsme/api.py | 17 +++++ 7 files changed, 218 insertions(+), 9 deletions(-) create mode 100644 doc/functions.rst diff --git a/doc/api.rst b/doc/api.rst index 364d654..10bb823 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -9,11 +9,7 @@ Public API .. module:: wsme -.. autoclass:: WSRoot - :members: - -.. autoclass:: expose -.. autoclass:: validate +.. autoclass:: signature([return_type, [arg0_type, [arg1_type, ... ] ] ], body=None, status=None) .. autoclass:: wsproperty .. autoclass:: wsattr @@ -22,6 +18,9 @@ Public API Default value of the complex type attributes. +.. autoclass:: WSRoot + :members: + Internals --------- diff --git a/doc/changes.rst b/doc/changes.rst index 8d278f6..f44590b 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -7,7 +7,9 @@ next * Add a special type 'HostRequest' that allow a function to ask for the host framework request object in its arguments. -* New Flask adapter: wsmeext.flask +* New adapter: wsmeext.flask, for the Flask_ framework. + +.. _Flask: http://flask.pocoo.org/ * Fix: the cornice adapter was not usable. diff --git a/doc/functions.rst b/doc/functions.rst new file mode 100644 index 0000000..d657202 --- /dev/null +++ b/doc/functions.rst @@ -0,0 +1,182 @@ +Functions +========= + +WSME is based on the idea that most of the time the input and output of web +services are actually stricly typed. It uses this fact to ease the +implementation of the actual functions by handling those input/output. +It also uses these informations to propose alternate protocols on top of a +proper REST api. + +This chapter explains in details how to 'sign' a function with WSME. + +The decorators +-------------- + +Depending on the framework you are using, you will have to use either a +@signature decorator, either a @wsexpose decorator. + +@signature +~~~~~~~~~~ + +The base @\ :class:`wsme.signature` decorator defines the return and argument types +of the function, and if needed a few more options. + +The Flask and Cornice adapters both propose a specific version of it, which +also wrap the function so that it becomes suitable for the host framework. + +In any case, the use of @signature has the same meaning: tell WSME what is the +signature of the function. + +@wsexpose +~~~~~~~~~ + +The native Rest implementation, and the TG and Pecan adapters add a @wsexpose +decorator. + +It does what @signature does, *and* expose the function in the routing system +of the host framework. + +This decorator is generally used in object-dispatch routing context. + +.. note:: + + Since both decorators plays the same role function-wise, the rest of this + document will alway use @signature. + +Signing a function +------------------ + +Signing a function is just a matter of decorating it with @signature: + +.. code-block:: python + + @signature(int, int, int) + def multiply(a, b): + return a * b + +In this trivial example, we tell WSME that the 'multiply' function returns an +integer, and takes two integer parameters. + +WSME will match the argument types by order, and know the exact type of each +named argument. This is important since most of the web service protocols don't +provide strict argument ordering but only named parameters. + +Optional arguments +~~~~~~~~~~~~~~~~~~ + +Defining an argument as optional is done by providing a default value: + +.. code-block:: python + + @signature(int, int, int): + def increment(value, delta=1): + return value + delta + +In this example, the caller may omit the 'delta' argument, and no +'MissingArgument' error will be raised. + +Additionally this argument will be documented as optional by the sphinx +extension. + +Body argument +~~~~~~~~~~~~~ + +When defining a Rest CRUD api, we generally have a URL on which we POST datas. + +For example: + +.. code-block:: python + + @signature(Author, Author) + def update_author(data): + # ... + return data + +Such a function will take at least one parameter 'data' that is a structured +type. With the default way of handling parameters, the body of the request +would be like this: + +.. code-block:: javascript + + { + "data": + { + "id": 1, + "name": "Pierre-Joseph" + } + } + +If you think (and you should) that it has one extra nest level, the 'body' +argument is here for you:: + + @signature(Author, body=Author) + def update_author(data): + # ... + return data + +With this syntax, we can now post a simpler body: + +.. code-block:: javascript + + { + "id": 1, + "name": "Pierre-Joseph" + } + +Note that it does not prevent from having multiple parameters, it just requires +the body argument to be the last: + +.. code-block:: python + + @signature(Author, bool, body=Author) + def update_author(force_update=False, data=None): + # ... + return data + +In this case, the other arguments can be passed in the URL, in addition to the +body parameter. For example, a POST on ``/author/SOMEID?force_update=true``. + +Status code +~~~~~~~~~~~ + +The default status code returned by WSME are 200, 400 (if the client send wrong +inputs) and 500 (for server-side errors). + +Since a proper Rest API should use different return codes (201, etc), one can +use the 'status=' option of @signature to do so. + +.. code-block:: python + + @signature(Author, body=Author, status=201) + def create_author(data): + # ... + return data + +Of course this code will only be used if no error occur. + +In case the function needs to change the status code on a per-request base, it +can return a :class:`wsme.Response` object, that allow to override the status +code: + +.. code-block:: python + + @signature(Author, body=Author, status=202) + def update_author(data): + # ... + response = Response(data) + if transaction_finished_and_successful: + response.status_code = 200 + return response + +Extra arguments +~~~~~~~~~~~~~~~ + +The default behavior of WSME is to reject requests that gives extra/unknown +arguments. In some (rare) cases, it can be unwanted. + +Adding 'ignore_extra_args=True' to @signature changes this behavior. + +.. note:: + + If using this option seems to solution to your problem, please think twice + before using it ! diff --git a/doc/index.rst b/doc/index.rst index f12727d..7819d57 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,6 +10,7 @@ Contents gettingstarted api types + functions protocols integrate document diff --git a/doc/integrate.rst b/doc/integrate.rst index 2a95fa7..335a455 100644 --- a/doc/integrate.rst +++ b/doc/integrate.rst @@ -24,11 +24,13 @@ This decorator can have two different names depending on the adapter. Generally this decorator is provided for frameworks that expects functions taking a request object as a single parameter and returning a response - object. This is the case of :ref:`adapter-cornice`. + object. This is the case of :ref:`adapter-cornice` and + :ref:`adapter-flask`. Additionnaly, if you want to enable additionnal protocols, you will need to mount a :class:`WSRoot` instance somewhere in the application, generally -``/ws``. This subpath will then handle the additional protocols. +``/ws``. This subpath will then handle the additional protocols. In a future +version, a wsgi middleware will probably play this role. .. note:: @@ -115,7 +117,8 @@ Example Flask ----- - *"Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions. And before you ask: It's BSD licensed! "* + *"Flask is a microframework for Python based on Werkzeug, Jinja 2 and good + intentions. And before you ask: It's BSD licensed! "* .. warning:: diff --git a/doc/protocols.rst b/doc/protocols.rst index 939601f..cce7a56 100644 --- a/doc/protocols.rst +++ b/doc/protocols.rst @@ -8,6 +8,11 @@ last chapter (:ref:`protocols-the-example`). REST ---- +.. note:: + + This chapter applies also for the different adapters, not only the native + REST implementation. + The two REST protocols share common characterics. Each function corresponds to distinct webpath that starts with the diff --git a/wsme/api.py b/wsme/api.py index f51e2b5..4077f8c 100644 --- a/wsme/api.py +++ b/wsme/api.py @@ -136,6 +136,23 @@ class FunctionDefinition(object): class signature(object): + """ + Decorator that specify the argument types of an exposed function. + + :param return_type: Type of the value returned by the function + :param argN: Type of the Nth argument + :param body: If the function takes a final argument that is supposed to be + the request body by itself, its type. + :param status: HTTP return status code of the function. + :param ignore_extra_args: Allow extra/unknow arguments (default to False) + + Most of the time this decorator is not supposed to be used directly, + unless you are not using WSME on top of another framework. + + If an adapter is used, it will provide either a specialised version of this + decororator, either a new decorator named @wsexpose that takes the same + parameters (it will in addition expose the function, hence its name). + """ def __init__(self, *types, **options): self.return_type = types[0] if types else None self.arg_types = []