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