I finally found a non-trivial reason to implement a Python decorator. I make extensive use of Python keyword arguments in the pysmug API and map those spellings to SmugMug keyword arguments. The algorithm for converting a Python spelling to a SmugMug spelling is pretty straight forward and easily wrapped up in a function. Originally, the first line of every function required formatting the keywords into SmugMug-style spellings by calling self._prepare_keywords(**kwargs). It occurred to me this is a perfect use case for decorators so I converted the code from a class method to a function, moved the call up a line, prefixed a @ and voilà, a far more explicit communication about how this method handles keyword arguments – love it.

In version 0.4 of pysmug I refactored my code from this:

def _login(self, handler, **kwargs):
  kwargs = self._prepare_keywords(**kwargs)
  method = kwargs.get("method", None)
  ...

to this:

@smugmug_keywords
def _login(self, handler, **kwargs):
  method = kwargs.get("method", None)
  ...

by using this function (the function mg was originally the _prepare_keywords function):

def smugmug_keywords(fn):
  """Prepare the keywords for sending to SmugMug.

  The following operations are performed::
    1. If the key is "method", continue.
    2. If the key starts with an upper case letter, continue.
    3. If the key is in {methods.apikeys}, replace the key.
    4. If the key ends with {id}, upper case the first letter
       and {ID} and replace the key.
    5. Else, upper case the first letter only and replace the
       key.

  @param fn: the decorated function
  """
  def mg(*args, **kwargs):
    items = kwargs.items()
    for k, v, in items:
      if k == "method":
        continue
      if k[0].isupper():
        continue
      lk = k.lower()
      if lk in apikeys:
        key, func = apikeys[lk]
        del kwargs[k]
        kwargs[key] = func(v) if func else v
      else:
        del kwargs[k]
        if lk.endswith("id"):
          kwargs[lk[:-2].title() + "ID"] = v
        else:
          kwargs[lk.title()] = v
    return fn(*args, **kwargs)
  return mg

This function is an argument for Erlang-style pattern matching.

comments powered by Disqus

Recent Posts

About

I'm not here.