Skip to main content

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


Project details


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)

Uploaded Source

Built Distribution

graceful-0.6.1-py2.py3-none-any.whl (67.9 kB view details)

Uploaded Python 2 Python 3

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

Hashes for graceful-0.6.1.tar.gz
Algorithm Hash digest
SHA256 f66262d24d9689103d4ac641a017cb264e60acfa25e640397a5948e20c3fd6e3
MD5 bd5d2e831a948501669d89875a4f46bf
BLAKE2b-256 d5ab6d81610594fd60f3f5cd8247233116d0bb61fc89f6d54b16f36a78c507cb

See more details on using hashes here.

File details

Details for the file graceful-0.6.1-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for graceful-0.6.1-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 fab988f0d6b731e309fc879e7a17ab4055bf73dfbdfaa54cab71483966d4f909
MD5 cbe6d58fedf3b9734eb1f5aad3cf73f9
BLAKE2b-256 0743f8b6afdbe7b556d9aaa221c72359eb9c374fb37ffe91687efd45f837cbb8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page