Wishful Coding

Didn't you ever wish your computer understood you?

Archaic API helpers

I’ve been working with a number of HTTP API’s using Python recently, and have accumulated some helper functions that I hope you don’t need.

PHP

It might be possible to write a good REST API in PHP, but it’s certainly not easy… or common.

Array parameters

Lets say your brilliant API design requires an object to be passed as a parameter. How do you go about this? Hint: It’s not JSON.

Either you call serialise on the object and send it, or you use this other PHPism that lets you say ?param[field]=val.

In the first case, use the phpserialise package, in the second case, use this handy pre-processing function:

def php_flatten(data):
    res = {}

    def inner_flatten(path, value):
        if isinstance(value, dict):
            for k, v in value.items():
                newpath = "%s[%s]" % (path, k)
                inner_flatten(newpath, v)
        elif isinstance(value, list):
            for k, v in enumerate(value):
                newpath = "%s[%s]" % (path, k)
                inner_flatten(newpath, v)
        else:
            res[path] = value

    for k, v in data.items():
        inner_flatten(k, v)

    return res

XML

I heard Java people like XML, so your API should probably offer it as an alternative to JSON, serialised PHP and INI files.

I was surprised there is no such thing as xml.dumps in Python, so I wrote it. There is no 1-on-1 mapping between dicts and XML, but this a a way to go from Python data to XML.

import lxml.etree as ET

def maybesub(el, tag):
    """
    Create a subelement if a tag was given.
    Otherwise return the current node.
    Used for the root node.
    """
    if tag:
        return ET.SubElement(el, tag)
    else:
        return el

def walk(el, tag, data):
    """
    Recursively add child nodes named tag to el based on data.
    """
    if isinstance(data, dict):
        sub = maybesub(el, tag)
        for key, value in sorted(data.items()):
            walk(sub, key, value)
    elif isinstance(data, list):
        for value in data:
            walk(el, tag, value)
    else:
        sub = maybesub(el, tag)
        sub.text = str(data)

def dumps(data, root="response"):
    root = ET.Element(root)
    walk(root, None, data)
    return ET.tostring(root, pretty_print=True)

INI

That was no joke. Take a look at configparser if you ever encounter this.

The internet is s-l-o-w

So take this handy Redis-based memoization function with you, and twiddle with the TTL.

import redis
from functools import wraps
import pickle
import logging

pool = redis.ConnectionPool()

def memoize(ttl=3600):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            try:
                r = redis.StrictRedis(connection_pool=pool)
                # Compute the function signature
                sig = (f.__module__, f.__name__, args, kwargs)
                pargs = pickle.dumps(sig) 
                # Try to get the result
                pres = r.get(pargs)
                if pres:
                    return pickle.loads(pres)
                else:
                    # Or compute and store
                    res = f(*args, **kwargs)
                    r.setex(pargs, ttl, pickle.dumps(res))
                    return res
            except redis.RedisError:
                # Show must go on!
                logging.exception("redis oopsed")
                return f(*args, **kwargs)

        return wrapper
    return decorator
Pepijn de Vos