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.