Skip to content

Instantly share code, notes, and snippets.

@Ceasar
Last active October 28, 2015 17:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ceasar/061ddbcb1bfd59fbb612 to your computer and use it in GitHub Desktop.
Save Ceasar/061ddbcb1bfd59fbb612 to your computer and use it in GitHub Desktop.
A representation object for consuming RESTful APIs.
"""
REST client over requests.
"""
import logging
import requests
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class Resource(object):
"""A representation of a Resource.
Naively, using code with requests looks like this::
>>> launchpad = requests.get('https://api.launchpad.net/1.0/').json()
>>> people = requests.get(launchpad['people_collection_link']).json()
>>> people['entries'][0]['display_name']
u'Martin Pitt'
This class should be extended by another in order to add support for
hypermedia controls. To do this, subclass ``_is_link()``. For example, to
use this class with the Launchpad API, which denotes links to other objects
by suffixing keys with '_collection_link' or '_link', you might extend
Resource like so:
>>> class LaunchpadResource(Resource):
... def _is_link(self, key):
... return (
... key.endswith('_collection_link') or key.endswith('_link')
... )
Then you can do things like:
>>> launchpad = LaunchpadResource('https://api.launchpad.net/1.0/')
>>> people = launchpad['people_collection_link']
>>> people['entries'][0]['display_name']
u'Martin Pitt'
"""
def __init__(self, url, representation=None, connection=None):
self._url = url
self._representation = representation
self._connection = requests if connection is None else connection
# Would be nice to use getattr() here for brevity, but key names are more
# flexible than attribute names. We don't offer both so that there's one
# way to do it. This also let's us distinguish data from class attributes.
def __getitem__(self, k):
# Try to load the resource. This helps with brevity.
if self._representation is None:
self.load()
try:
attr = self._representation[k]
except KeyError:
raise KeyError("object has no attribute '%s'" % k)
else:
return attr
def _is_link(self, key):
return False
def _convert_links(self, obj):
if isinstance(obj, dict):
return {
k: type(self)(v, connection=self._connection) if self._is_link(k) else self._convert_links(v)
for k, v in obj.items()
}
elif isinstance(obj, list):
return [self._convert_links(e) for e in obj]
else:
return obj
def load(self, **kwargs):
"""Fetch the resource at *url*."""
data = self._connection.get(self.url, **kwargs).json()
self._representation = self._convert_links(data)
@property
def url(self):
return self._url
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment