Elegant Python REST toolkit built on top of falcon
Project description
|PyPI| |PyPI| |Build Status| |Coverage Status| |Documentation Status|
|Join the chat at https://gitter.im/graceful-for-falcon/Lobby|
graceful
========
``graceful`` is an elegant Python REST toolkit built on top of
`falcon <http://github.com/falconry/falcon>`__ framework. It is highly
inspired by `Django REST
framework <http://www.django-rest-framework.org/>`__ - mostly by how
object serialization is done but more emphasis here is put on API to be
self-descriptive.
Features:
- generic classes for list and single object resources
- simple but extendable pagination
- simple but extendable authentication and authorization
- structured responses with content/meta separation
- declarative fields and parameters
- self-descriptive-everything: API description accessible both in
python and through ``OPTIONS`` requests
- painless validation
- 100% tests coverage
- falcon>=0.3.0 (tested up to 1.4.x)
- python3 exclusive (tested from 3.3 to 3.6)
Community behind graceful is starting to grow but we don’t have any
mailing list yet. There was one on
`Librelist <http://librelist.com/browser/graceful>`__ but no one used it
and it seems that librelist became dead (see GitHub issue
`#36 <https://github.com/swistakm/graceful/issues/36>`__). For now let’s
use gitter chat until we decide on something new. Chat is available
`here <https://gitter.im/graceful-for-falcon/Lobby>`__.
python3 only
------------
**Important**: ``graceful`` is python3 exclusive because **right now**
should be a good time to forget about python2. There are no plans for
making ``graceful`` python2 compatible although it would be pretty
straightforward to do so with existing tools (like six).
usage
-----
For extended tutorial and more information please refer to
`guide <https://graceful.readthedocs.org/en/latest/guide/>`__ included
in documentation.
Anyway here is simple example of working API made made with
``graceful``:
.. code:: python
import falcon
from graceful.serializers import BaseSerializer
from graceful.fields import IntField, RawField
from graceful.parameters import StringParam
from graceful.resources.generic import (
RetrieveAPI,
PaginatedListAPI,
)
api = application = falcon.API()
# lets pretend that this is our backend storage
CATS_STORAGE = [
{"id": 0, "name": "kitty", "breed": "saimese"},
{"id": 1, "name": "lucie", "breed": "maine coon"},
{"id": 2, "name": "molly", "breed": "sphynx"},
]
# this is how we represent cats in our API
class CatSerializer(BaseSerializer):
id = IntField("cat identification number", read_only=True)
name = RawField("cat name")
breed = RawField("official breed name")
class Cat(RetrieveAPI):
"""
Single cat identified by its id
"""
serializer = CatSerializer()
def get_cat(self, cat_id):
try:
return [
cat for cat in CATS_STORAGE if cat['id'] == int(cat_id)
][0]
except IndexError:
raise falcon.HTTPNotFound
def retrieve(self, params, meta, **kwargs):
cat_id = kwargs['cat_id']
return self.get_cat(cat_id)
class CatList(PaginatedListAPI):
"""
List of all cats in our API
"""
serializer = CatSerializer()
breed = StringParam("set this param to filter cats by breed")
def list(self, params, meta, **kwargs):
if 'breed' in params:
filtered = [
cat for cat in CATS_STORAGE
if cat['breed'] == params['breed']
]
return filtered
else:
return CATS_STORAGE
api.add_route("/v1/cats/{cat_id}", Cat())
api.add_route("/v1/cats/", CatList())
Assume this code is in python module named ``example.py``. Now run it
with `gunicorn <https://github.com/benoitc/gunicorn>`__:
::
gunicorn -b localhost:8888 example
And you’re ready to query it (here with awesome
`httpie <http://httpie.org>`__ tool):
::
$ http localhost:8888/v0/cats/?breed=saimese
HTTP/1.1 200 OK
Connection: close
Date: Tue, 16 Jun 2015 08:43:05 GMT
Server: gunicorn/19.3.0
content-length: 116
content-type: application/json
{
"content": [
{
"breed": "saimese",
"id": 0,
"name": "kitty"
}
],
"meta": {
"params": {
"breed": "saimese",
"indent": 0
}
}
}
Or access API description issuing ``OPTIONS`` request:
::
$ http OPTIONS localhost:8888/v0/cats
HTTP/1.1 200 OK
Connection: close
Date: Tue, 16 Jun 2015 08:40:00 GMT
Server: gunicorn/19.3.0
allow: GET, OPTIONS
content-length: 740
content-type: application/json
{
"details": "List of all cats in our API",
"fields": {
"breed": {
"details": "official breed name",
"label": null,
"spec": null,
"type": "string"
},
"id": {
"details": "cat identification number",
"label": null,
"spec": null,
"type": "int"
},
"name": {
"details": "cat name",
"label": null,
"spec": null,
"type": "string"
}
},
"methods": [
"GET",
"OPTIONS"
],
"name": "CatList",
"params": {
"breed": {
"default": null,
"details": "set this param to filter cats by breed",
"label": null,
"required": false,
"spec": null,
"type": "string"
},
"indent": {
"default": "0",
"details": "JSON output indentation. Set to 0 if output should not be formated.",
"label": null,
"required": false,
"spec": null,
"type": "integer"
}
},
"path": "/v0/cats",
"type": "list"
}
contributing
------------
Any contribution is welcome. Issues, suggestions, pull requests -
whatever. There is only short set of rules that guide this project
development you should be aware of before submitting a pull request:
- Only requests that have passing CI builds (Travis) will be merged.
- Code is checked with ``flakes8`` and ``pydocstyle`` during build so
this implicitly means that compliance with PEP-8 and PEP-257 is
mandatory.
- No changes that decrease coverage will be merged.
One thing: if you submit a PR please do not rebase it later unless you
are asked for that explicitly. Reviewing pull requests that suddenly had
their history rewritten just drives me crazy.
license
-------
See ``LICENSE`` file.
.. |PyPI| image:: https://img.shields.io/pypi/v/graceful.svg
:target: https://pypi-hypernode.com/pypi/graceful/
.. |PyPI| image:: https://img.shields.io/pypi/pyversions/graceful.svg
:target: https://pypi-hypernode.com/pypi/graceful/
.. |Build Status| image:: https://travis-ci.org/swistakm/graceful.svg?branch=master
:target: https://travis-ci.org/swistakm/graceful
.. |Coverage Status| image:: https://coveralls.io/repos/swistakm/graceful/badge.svg?branch=master
:target: https://coveralls.io/r/swistakm/graceful?branch=master
.. |Documentation Status| image:: https://readthedocs.org/projects/graceful/badge/?version=latest
:target: https://graceful.readthedocs.io/en/latest/
.. |Join the chat at https://gitter.im/graceful-for-falcon/Lobby| image:: https://badges.gitter.im/graceful-for-falcon/Lobby.svg
:target: https://gitter.im/graceful-for-falcon/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|Join the chat at https://gitter.im/graceful-for-falcon/Lobby|
graceful
========
``graceful`` is an elegant Python REST toolkit built on top of
`falcon <http://github.com/falconry/falcon>`__ framework. It is highly
inspired by `Django REST
framework <http://www.django-rest-framework.org/>`__ - mostly by how
object serialization is done but more emphasis here is put on API to be
self-descriptive.
Features:
- generic classes for list and single object resources
- simple but extendable pagination
- simple but extendable authentication and authorization
- structured responses with content/meta separation
- declarative fields and parameters
- self-descriptive-everything: API description accessible both in
python and through ``OPTIONS`` requests
- painless validation
- 100% tests coverage
- falcon>=0.3.0 (tested up to 1.4.x)
- python3 exclusive (tested from 3.3 to 3.6)
Community behind graceful is starting to grow but we don’t have any
mailing list yet. There was one on
`Librelist <http://librelist.com/browser/graceful>`__ but no one used it
and it seems that librelist became dead (see GitHub issue
`#36 <https://github.com/swistakm/graceful/issues/36>`__). For now let’s
use gitter chat until we decide on something new. Chat is available
`here <https://gitter.im/graceful-for-falcon/Lobby>`__.
python3 only
------------
**Important**: ``graceful`` is python3 exclusive because **right now**
should be a good time to forget about python2. There are no plans for
making ``graceful`` python2 compatible although it would be pretty
straightforward to do so with existing tools (like six).
usage
-----
For extended tutorial and more information please refer to
`guide <https://graceful.readthedocs.org/en/latest/guide/>`__ included
in documentation.
Anyway here is simple example of working API made made with
``graceful``:
.. code:: python
import falcon
from graceful.serializers import BaseSerializer
from graceful.fields import IntField, RawField
from graceful.parameters import StringParam
from graceful.resources.generic import (
RetrieveAPI,
PaginatedListAPI,
)
api = application = falcon.API()
# lets pretend that this is our backend storage
CATS_STORAGE = [
{"id": 0, "name": "kitty", "breed": "saimese"},
{"id": 1, "name": "lucie", "breed": "maine coon"},
{"id": 2, "name": "molly", "breed": "sphynx"},
]
# this is how we represent cats in our API
class CatSerializer(BaseSerializer):
id = IntField("cat identification number", read_only=True)
name = RawField("cat name")
breed = RawField("official breed name")
class Cat(RetrieveAPI):
"""
Single cat identified by its id
"""
serializer = CatSerializer()
def get_cat(self, cat_id):
try:
return [
cat for cat in CATS_STORAGE if cat['id'] == int(cat_id)
][0]
except IndexError:
raise falcon.HTTPNotFound
def retrieve(self, params, meta, **kwargs):
cat_id = kwargs['cat_id']
return self.get_cat(cat_id)
class CatList(PaginatedListAPI):
"""
List of all cats in our API
"""
serializer = CatSerializer()
breed = StringParam("set this param to filter cats by breed")
def list(self, params, meta, **kwargs):
if 'breed' in params:
filtered = [
cat for cat in CATS_STORAGE
if cat['breed'] == params['breed']
]
return filtered
else:
return CATS_STORAGE
api.add_route("/v1/cats/{cat_id}", Cat())
api.add_route("/v1/cats/", CatList())
Assume this code is in python module named ``example.py``. Now run it
with `gunicorn <https://github.com/benoitc/gunicorn>`__:
::
gunicorn -b localhost:8888 example
And you’re ready to query it (here with awesome
`httpie <http://httpie.org>`__ tool):
::
$ http localhost:8888/v0/cats/?breed=saimese
HTTP/1.1 200 OK
Connection: close
Date: Tue, 16 Jun 2015 08:43:05 GMT
Server: gunicorn/19.3.0
content-length: 116
content-type: application/json
{
"content": [
{
"breed": "saimese",
"id": 0,
"name": "kitty"
}
],
"meta": {
"params": {
"breed": "saimese",
"indent": 0
}
}
}
Or access API description issuing ``OPTIONS`` request:
::
$ http OPTIONS localhost:8888/v0/cats
HTTP/1.1 200 OK
Connection: close
Date: Tue, 16 Jun 2015 08:40:00 GMT
Server: gunicorn/19.3.0
allow: GET, OPTIONS
content-length: 740
content-type: application/json
{
"details": "List of all cats in our API",
"fields": {
"breed": {
"details": "official breed name",
"label": null,
"spec": null,
"type": "string"
},
"id": {
"details": "cat identification number",
"label": null,
"spec": null,
"type": "int"
},
"name": {
"details": "cat name",
"label": null,
"spec": null,
"type": "string"
}
},
"methods": [
"GET",
"OPTIONS"
],
"name": "CatList",
"params": {
"breed": {
"default": null,
"details": "set this param to filter cats by breed",
"label": null,
"required": false,
"spec": null,
"type": "string"
},
"indent": {
"default": "0",
"details": "JSON output indentation. Set to 0 if output should not be formated.",
"label": null,
"required": false,
"spec": null,
"type": "integer"
}
},
"path": "/v0/cats",
"type": "list"
}
contributing
------------
Any contribution is welcome. Issues, suggestions, pull requests -
whatever. There is only short set of rules that guide this project
development you should be aware of before submitting a pull request:
- Only requests that have passing CI builds (Travis) will be merged.
- Code is checked with ``flakes8`` and ``pydocstyle`` during build so
this implicitly means that compliance with PEP-8 and PEP-257 is
mandatory.
- No changes that decrease coverage will be merged.
One thing: if you submit a PR please do not rebase it later unless you
are asked for that explicitly. Reviewing pull requests that suddenly had
their history rewritten just drives me crazy.
license
-------
See ``LICENSE`` file.
.. |PyPI| image:: https://img.shields.io/pypi/v/graceful.svg
:target: https://pypi-hypernode.com/pypi/graceful/
.. |PyPI| image:: https://img.shields.io/pypi/pyversions/graceful.svg
:target: https://pypi-hypernode.com/pypi/graceful/
.. |Build Status| image:: https://travis-ci.org/swistakm/graceful.svg?branch=master
:target: https://travis-ci.org/swistakm/graceful
.. |Coverage Status| image:: https://coveralls.io/repos/swistakm/graceful/badge.svg?branch=master
:target: https://coveralls.io/r/swistakm/graceful?branch=master
.. |Documentation Status| image:: https://readthedocs.org/projects/graceful/badge/?version=latest
:target: https://graceful.readthedocs.io/en/latest/
.. |Join the chat at https://gitter.im/graceful-for-falcon/Lobby| image:: https://badges.gitter.im/graceful-for-falcon/Lobby.svg
:target: https://gitter.im/graceful-for-falcon/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
graceful-0.6.1.tar.gz
(33.1 kB
view details)
Built Distribution
File details
Details for the file graceful-0.6.1.tar.gz
.
File metadata
- Download URL: graceful-0.6.1.tar.gz
- Upload date:
- Size: 33.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f66262d24d9689103d4ac641a017cb264e60acfa25e640397a5948e20c3fd6e3 |
|
MD5 | bd5d2e831a948501669d89875a4f46bf |
|
BLAKE2b-256 | d5ab6d81610594fd60f3f5cd8247233116d0bb61fc89f6d54b16f36a78c507cb |
File details
Details for the file graceful-0.6.1-py2.py3-none-any.whl
.
File metadata
- Download URL: graceful-0.6.1-py2.py3-none-any.whl
- Upload date:
- Size: 67.9 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | fab988f0d6b731e309fc879e7a17ab4055bf73dfbdfaa54cab71483966d4f909 |
|
MD5 | cbe6d58fedf3b9734eb1f5aad3cf73f9 |
|
BLAKE2b-256 | 0743f8b6afdbe7b556d9aaa221c72359eb9c374fb37ffe91687efd45f837cbb8 |