HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

A generic REST API consuming Python library

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericrestconsumingpythonlibraryapi

Problem

I wrote this, which essentially acts as a single library for all possible REST services.

Here is the main file, as of alpha version 1:

import requests, json

class RestConsumer(object):

    def __init__(self,base_url,append_json=False,**kwargs):
        self.base_url = base_url
        self.uriparts = []
        self.append_json = append_json
        self.kwargs = kwargs

    def __getattr__(self,k):
        if k=='spc':
            return self.spc
        if not k[0] == '_':
            self.uriparts.append(k)
        else:
            if k[1:3]=='in':
                self.uriparts.append(k[3:])
            elif k[1:3] == 'uh':
                self.uriparts.append(k.replace('_','-'))
        return self

    def spc(self,k):
        self.uriparts.append(k)
        return self

    def __call__(self, **kwargs):
        uri_constructed = '/'.join(self.uriparts)
        self.uriparts = []
        self.uri_constructed = "%s%s"%(uri_constructed,'.json') if self.append_json else uri_constructed
        self.url = '/'.join([self.base_url,self.uri_constructed])
        return self.get(self.url,**kwargs)

    def get(self,url,**kwargs):
        print url
        r = requests.get(url,**kwargs)
        return json.loads(r.content)

    def post(self,**kwargs):
        r = requests.post(**kwargs)
        return json.loads(r.content)

if __name__=='__main__':
    from pprint import pprint
    t = RestConsumer(base_url='https://api.twitter.com/1',append_json=True)
    public_timeline = t.statuses.public_timeline()
    pprint(public_timeline)

    g = RestConsumer(base_url='https://api.github.com')
    repos = g.users.kennethreitz.repos()
    pprint(repos)

    s = RestConsumer(base_url='http://api.stackoverflow.com/1.1')
    sr = s.users.spc('55562').questions.unanswered()
    pprint(sr)

    sr2 = s.tags.python.spc('top-answerers').spc('all-time')()
    pprint(sr2)


The rationale and usage is all explained in the README.

Please critique the library, the idea, implem

Solution

import requests, json

class RestConsumer(object):

    def __init__(self,base_url,append_json=False,**kwargs):
        self.base_url = base_url
        self.uriparts = []
        self.append_json = append_json
        self.kwargs = kwargs


Since you treat attributes "special", I recommend putting _ in front of these attributes so that are clearly offset.

def __getattr__(self,k):


I recommend not using the random letter k for the name.

if k=='spc':
            return self.spc


__getattr__ only gets invoked if the attribute cannot be found. Since spc will be found this won't ever get used.

if not k[0] == '_':
            self.uriparts.append(k)
        else:
            if k[1:3]=='in':
                self.uriparts.append(k[3:])


What's the point of this rule? _infoo means foo?

elif k[1:3] == 'uh':
                self.uriparts.append(k.replace('_','-'))


uh?

return self


I don't like the way you've implemented this. Accessing attributes modifying the object's state is deeply counter-intuitive.

def spc(self,k):
        self.uriparts.append(k)
        return self


spc is not a clear name. Its hard to guess what it means.

def __call__(self, **kwargs):
        uri_constructed = '/'.join(self.uriparts)
        self.uriparts = []
        self.uri_constructed = "%s%s"%(uri_constructed,'.json') if self.append_json else uri_constructed
        self.url = '/'.join([self.base_url,self.uri_constructed])
        return self.get(self.url,**kwargs)

    def get(self,url,**kwargs):
        print url
        r = requests.get(url,**kwargs)
        return json.loads(r.content)

    def post(self,**kwargs):
        r = requests.post(**kwargs)
        return json.loads(r.content)


get prints while post doesn't. Probably neither should be printing, a log facility might be useful.

The way you've designed this, some useful things won't work:

public_timeline = twitter.statuses.public_timeline
while True:
      print public_timeline()


or

user = s.users.spc(55562)
print user.questions.unanswered()
print user.questions.answered()


I suggest that instead of modifying the object, you return new objects. That way it will work in an intuitive manner.

Instead of spc I suggest that you provide a __getitem__. That way you can do:

s.users[55562].questions.unanswered()


Which I think will make it cleaner.

Code Snippets

import requests, json

class RestConsumer(object):

    def __init__(self,base_url,append_json=False,**kwargs):
        self.base_url = base_url
        self.uriparts = []
        self.append_json = append_json
        self.kwargs = kwargs
def __getattr__(self,k):
if k=='spc':
            return self.spc
if not k[0] == '_':
            self.uriparts.append(k)
        else:
            if k[1:3]=='in':
                self.uriparts.append(k[3:])
elif k[1:3] == 'uh':
                self.uriparts.append(k.replace('_','-'))

Context

StackExchange Code Review Q#8455, answer score: 4

Revisions (0)

No revisions yet.