Durga: Easy Python REST resources¶
Create easy to use Python objects for REST resources including schema validation.
Features¶
- Light weight REST client
- Easy to use API concept consisting of Resource, Collection, Element corresponds to Django ORM with Model, Manager, Instance
- Describe a resource as schema and validate incoming JSON against it
- Compatible with Python 2.7, 3.4 and PyPy
Contents¶
Installation¶
$ pip install durga
If you have virtualenvwrapper installed:
$ mkvirtualenv durga
$ pip install durga
Usage¶
Flickr example¶
To use Durga in a project define a class
that extends
durga.Resource
. This example uses the
Flickr API flickr.photos.search with Python 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import pytest
from durga.element import Element
str = six.string_types[0]
class FlickrResource(durga.Resource):
base_url = 'https://api.flickr.com/services'
path = 'rest'
objects_path = ('photos', 'photo')
schema = durga.schema.Schema({
'farm': durga.schema.Use(int, error='Invalid farm'),
'id': durga.schema.Use(int, error='Invalid id'),
'isfamily': durga.schema.Use(bool, error='Invalid isfamily'),
'isfriend': durga.schema.Use(bool, error='Invalid isfriend'),
'ispublic': durga.schema.Use(bool, error='Invalid ispublic'),
'owner': durga.schema.And(str, len, error='Invalid owner'),
'secret': durga.schema.And(str, len, error='Invalid secret'),
'server': durga.schema.Use(int, error='Invalid server'),
'title': durga.schema.And(str, len, error='Invalid title'),
})
query = {
'method': 'flickr.photos.search',
'api_key': 'a33076a7ae214c0d12931ae8e38e846d',
'format': 'json',
'nojsoncallback': 1,
|
Note
For convenience durga.Resource
. and
the schema library are available at
the top module level.
Now you can search for the first 10 cat images:
cats = FlickrResource().collection.filter(text='Cat', per_page=10)
This will return a durga.Collection
with a durga.Element
for each result.
MusicBrainz example¶
Musicbrainz is an open music encyclopedia that collects music metadata and makes it available to the public. With Artist you get detailed entry for a single artist.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | from uuid import UUID
from dateutil import parser
import durga
class MusicBrainzResource(durga.Resource):
base_url = 'http://musicbrainz.org/ws/2'
id_attribute = 'id'
path = 'artist'
schema = durga.schema.Schema({
'country': durga.schema.And(str, len, error='Invalid country'),
'ipis': [durga.schema.Optional(str)],
'area': {
'disambiguation': durga.schema.Optional(str),
'iso_3166_3_codes': [durga.schema.Optional(str)],
'sort-name': str,
'name': str,
'id': durga.schema.And(str, len, lambda n: UUID(n, version=4)),
'iso_3166_2_codes': [durga.schema.Optional(str)],
'iso_3166_1_codes': [durga.schema.Optional(str)],
},
'sort-name': str,
'name': str,
'disambiguation': durga.schema.Optional(str),
'life-span': {
'ended': bool,
'begin': durga.schema.And(str, len, parser.parse, error='Invalid begin'),
'end': durga.schema.Or(
None,
durga.schema.And(str, len, parser.parse, error='Invalid end')),
},
'end_area': durga.schema.Or(None, {
'disambiguation': durga.schema.Optional(str),
'iso_3166_3_codes': [durga.schema.Optional(str)],
'sort-name': str,
'name': str,
'id': durga.schema.And(str, len, lambda n: UUID(n, version=4)),
'iso_3166_2_codes': [durga.schema.Optional(str)],
'iso_3166_1_codes': [durga.schema.Optional(str)],
}),
'id': durga.schema.And(str, len, lambda n: UUID(n, version=4), error='Invalid id'),
'type': str,
'begin_area': {
'disambiguation': durga.schema.Optional(str),
'iso_3166_3_codes': [durga.schema.Optional(str)],
'sort-name': str,
'name': str,
'id': durga.schema.And(str, len, lambda n: UUID(n, version=4)),
'iso_3166_2_codes': [durga.schema.Optional(str)],
'iso_3166_1_codes': [durga.schema.Optional(str)],
},
'gender': durga.schema.Or(None, str),
})
query = {
'method': '',
'fmt': 'json',
'nojsoncallback': 1,
}
|
Note
In the example above you can see a more complex usage of validation.
For example to validate UUIDs:
'id': durga.schema.And(str, len, lambda n: UUID(n, version=4)),
For example to validate date:
'begin': durga.schema.And(str, len, parser.parse, error='Invalid begin'),
Now let’s use the MusicBrainzResource:
MusicBrainzResource().collection.get(id='05cbaf37-6dc2-4f71-a0ce-d633447d90c3').name
That returns name of artist with given id:
'東方神起'
How to define a resource¶
base_url (required)¶
Each REST API has a fixed main URL behind that contains all other resources. You can find this value for your application of durga in the used API documentation.
path (required)¶
Defines path of API resource you would like to use. You can find it in your API method description.
path_params¶
With setting path_params you lists your all your placeholder in path.
Example¶
path = 'movies/{movie_name}/{movie_year}/actors'
path_params = ('movie_name', 'movie_year')
id_attribute¶
Attribute url is taken per default to has a complete unique resource url. If you would like to change this, you can define a id_attribute by your own. And set your defined attribute manually.
Default¶
url = 'https://api.example.com/movies/23'
Changed id_attribute¶
id_attribute = 'id'
id = '23'
object_path¶
Indicates the sub path behind the base_url.
objects_path¶
It is used if a single resource is returned where the data is somewhere deeper inside the response. Often your response contains meta information next to your necesssary objects. So you have show the resource the path to that data.
Example¶
If your JSON response looks like kind of this
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 3
},
"objects": [
{
"id": 1,
"resource_uri": "https://api.example.com/movies/1",
"runtime": 154,
"title": "Pulp Fiction",
"director": "Quentin Tarantino",
"year": 1994
}
]
}
Then your attribute objects_path is defined in this way
path = 'movies'
query¶
Describes all your query specific params like format, method or params and so on.
Example¶
})
query = {
'method': 'flickr.photos.search',
'api_key': 'a33076a7ae214c0d12931ae8e38e846d',
'format': 'json',
'nojsoncallback': 1,
schema¶
Is a data type representation of your API response which is necessary if you would like to validate your incoming data. Look at schema documentation and examples in Usage to define your own schema.
durga¶
durga package¶
Submodules¶
durga.collection module¶
-
class
durga.collection.
Collection
(url, resource)[source]¶ Bases:
object
-
create
(data)[source]¶ Creates a new remote resource from a dictionary.
At first the data will be validated. After successful validation the data is converted to JSON. The response of the POST request is returned afterwards.
-
delete
()[source]¶ Deletes all Elements of this Collection.
Returns the response for each deleted Element as a list.
-
elements
¶
-
get_values
(data)[source]¶ Returns either a dictionary, a tuple or a single field.
The data type and the fields returned are defined by using values() or values_list().
-
response
= None¶
-
update
(data)[source]¶ Updates all Elements of this Collection with data from a dictionary.
The data dictionary is used to update the data of all Elements of this Collection. The updated Elements are validated and their data is converted to JSON. A PUT request is made for each Element. Finally a list of all responses is returned.
-
values
(*fields)[source]¶ Returns a list of dictionaries instead of Element instances.
The optional positional arguments, *fields, can be used to limit the fields that are returned.
-
values_list
(*fields, **kwargs)[source]¶ Returns a list of tuples instead of Element instances.
The optional positional arguments, *fields, can be used to limit the fields that are returned.
If only a single field is passed in, the flat parameter can be passed in too. If True, this will mean the returned results are single values, rather than one-tuples.
-
durga.element module¶
-
class
durga.element.
Element
(resource, data)[source]¶ Bases:
object
durga.exceptions module¶
-
exception
durga.exceptions.
MultipleObjectsReturned
[source]¶ Bases:
durga.exceptions.DurgaError
The query returned multiple objects when only one was expected.
That is, if a get request returns more than one result.
-
exception
durga.exceptions.
ObjectNotFound
[source]¶ Bases:
durga.exceptions.DurgaError
The requested object does not exist.
durga.resource module¶
Module contents¶
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at the GitHub issue tracker.
If you are reporting a bug, please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” is open to whoever wants to implement it.
Implement Features¶
Look through the GitHub issues for features. Anything tagged with “feature” is open to whoever wants to implement it.
Write Documentation¶
Durga could always use more documentation, whether as part of the official Durga docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at the GitHub issue tracker.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up durga for local development.
Fork the durga repo on GitHub.
Clone your fork locally:
$ git clone git@github.com:your_name_here/durga.git
Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:
$ mkvirtualenv durga $ cd durga $ make develop
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, check that your changes pass pep8, pyflakes and the tests, including testing other Python versions with tox:
$ make test $ make test-all
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
- The pull request should include tests.
- If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in
README.rst
. - The pull request should work for Python 2.7 and 3.4. Check Travis CI and make sure that the tests pass for all supported Python versions.