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