Writing my Python Marvel API Wrapper Part 3: Refactor

This post is part of the series Writing my Python Marvel API Wrapper

Other posts in this series:

  1. Writing my Python Marvel API Wrapper Part 1: Structure
  2. Writing my Python Marvel API Wrapper Part 2: Requests
  3. Writing my Python Marvel API Wrapper Part 3: Refactor (Current)

This is part three of my ongoing adventure in writing a Python interface to the Marvel API. If you’ve not read Part 1 about structure and Part 2 about adding requests then you may want to do that first.

So we left off with a functioning interface for the Marvel API, but ended up with the realisation that things could be so much simpler if we just refactor all the things.

About refactoring

By refactoring code, you’re essentially restructuring without changing the function. The main purpose is to improve maintainability of your code by, for example, splitting up long processes into smaller subprocesses or working to remove duplication.

In my case, we’ll be getting rid of some of the duplication that is in the code. As we’ve seen, a lot of my methods were building the exact same request URL and so that’s where we’ll start.

createRequestURL()

We can reduce a significant amount of duplicate code by handling the request URL in its own little method function, like getHash().

Step 1: Create a basic request URL

First, we’ll move the code to create the base request URL over to the new function.

def getRequestURL(self):
  request_url = self.API_URL + '?apikey=%s' % self.API_KEY
  return request_url

Step 2: Move the timestamp and hash generation

Next, we’ll have the function handle the timestamp and hash generation so that we don’t have to do so in each function separately.

def getRequestURL(self):
  request_url = self.API_URL + '?apikey=%s' % self.API_KEY

  ts = str(time.time())
  reqHash = self.getHash(ts, self.PRIV_KEY, self.API_KEY)
  request_url += '&ts=%s&hash=%s' % (ts, reqHash)

  return request_url

Step 3: Move additional parameters

Finally, we need to move the additional parameters like id, args, and subtypes. This can get a little tricky, because we don’t always need to use them. Therefor, we need to see if they’re being passed to the function.

So for id this would be something like:

def getRequestURL(self, id=None):
  request_url = self.API_URL

  if id:
    request_url += '/%s' % id

  ts = str(time.time())
  reqHash = self.getHash(ts, self.PRIV_KEY, self.API_KEY)

  request_url += '?apikey=%s&ts=%s&hash=%s' % (self.API_KEY, ts, reqHash)

  return request_url

The subType parameter can be added to the function in pretty much the same way and the arguments can be handled in the exact same way as they are now. The end result of this refactor is the following function:

def getRequestURL(self, id=None, subType=None, args={}):
  request_url = self.API_URL

  if id:
    request_url += '/%s' % id

  if subType:
    request_url += '/%s' % subType

  ts = str(time.time())
  reqHash = self.getHash(ts, self.PRIV_KEY, self.API_KEY)

  request_url += '?apikey=%s&ts=%s&hash=%s' % (self.API_KEY, ts, reqHash)

  for arg in args:
    request_url += "&%s=%s" % (arg, args[arg])		

  return request_url

Having moved all this code to a single function, our methods are now much, much cleaner. Check it out:

def getList(self, args):
  request_url = self.getRequestURL(args=args)

  return requests.get(request_url)

def getOne(self, id):
  request_url = self.getRequestURL(id)

  return requests.get(request_url)

def getCharacters(self, id, args):
  request_url = self.getRequestURL(id=id, subType='characters', args=args)

  return requests.get(request_url)

In the next part

We have a very limited amount of requests to this API. Which, I believe, is around a 1000 when you start out. My account is currently at 3000 requests per day. Generally speaking though, even if you don't have a request limit with an API it's a good idea to cache your requests. You really shouldn't need to make repeated requests to the API for the same data. I'm considering implementing this into a Django app so that we can make use of its Model system to store data we request for a month or more before requesting it again.

I may also consider reorganising the code so that you could potentially do something like:

stan_lee = marvel.creators.getOne(id=30)
stan_lee.getComics()

The options for improvement are almost endless at this point.

As always, you can find this stuff on Github. If you want to let me know how much this stuff sucks, I'm on Twitter of fill out this form.