Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/swistakm/graceful
Elegant Python REST toolkit built on top of falcon
https://github.com/swistakm/graceful
falcon framework http python rest-api restful restful-api
Last synced: about 2 months ago
JSON representation
Elegant Python REST toolkit built on top of falcon
- Host: GitHub
- URL: https://github.com/swistakm/graceful
- Owner: swistakm
- License: bsd-3-clause
- Created: 2015-06-15T15:35:57.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2019-02-03T19:15:02.000Z (almost 6 years ago)
- Last Synced: 2024-11-01T20:12:35.872Z (3 months ago)
- Topics: falcon, framework, http, python, rest-api, restful, restful-api
- Language: Python
- Homepage: https://graceful.readthedocs.org
- Size: 245 KB
- Stars: 75
- Watchers: 6
- Forks: 12
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[![PyPI](https://img.shields.io/pypi/v/graceful.svg)](https://pypi.python.org/pypi/graceful/)
[![PyPI](https://img.shields.io/pypi/pyversions/graceful.svg)](https://pypi.python.org/pypi/graceful/)
[![Build Status](https://travis-ci.org/swistakm/graceful.svg?branch=master)](https://travis-ci.org/swistakm/graceful)
[![Coverage Status](https://coveralls.io/repos/swistakm/graceful/badge.svg?branch=master)](https://coveralls.io/r/swistakm/graceful?branch=master)
[![Documentation Status](https://readthedocs.org/projects/graceful/badge/?version=latest)](https://graceful.readthedocs.io/en/latest/)
[![Join the chat at https://gitter.im/graceful-for-falcon/Lobby](https://badges.gitter.im/graceful-for-falcon/Lobby.svg)](https://gitter.im/graceful-for-falcon/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)# 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`:
```python
import falconfrom 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.HTTPNotFounddef 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_STORAGEapi.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.