coercion
This library provides functions that coerce datastructures into
normalized forms. For example, converting an arbitrary dict
into a form that is suitable for passing to json.dumps
.
The tornado framework has a function called recursive_unicode
in the tornado.escape
module. It is a very simple recursive
walk of datastructure that switches on type and transforms string
values into unicode strings. I use this in production software
regularly and it works like a charm. Or at least it did until my
software encountered a deeply nested dictionary and I received a
RuntimeError: maximum recursion depth exceeded
error in my
service log. This is one of the exceptions that strikes fear into
most engineers when it rears it's head in production.
That is the primary reason for this library existing. It provides the same simple string encoding function iteratively instead of recursively. At the same time, the need to coerce values into a normalized string form is something that I've had to do repeatedly so it might as well be plopped into a reusable library.
Examples
The following example shows one of the underlying reasons that this
library was created. The commonly used msgpack implementation for
python returns everything as byte strings which is problematic if
you want to dump it as JSON since it will raise a TypeError
if
dictionary keys are not strings. (This is where recursive_unicode
was so handy.)
>>> import json
>>> import coercion
>>> import msgpack
>>> bin_msg = msgpack.packb({u'\u00DCnicode': b'bytes', b'bytes': 'str'})
>>> decoded = msgpack.unpackb(bin_msg)
>>> decoded
{b'bytes': b'str', b'\xc3\x9cnicode': b'bytes'}
>>> json.dumps(decoded)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/daveshawley/opt/lib/python3.5/json/__init__.py", line 230, in dumps
return _default_encoder.encode(obj)
File "/Users/daveshawley/opt/lib/python3.5/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/Users/daveshawley/opt/lib/python3.5/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
TypeError: keys must be a string
>>> json.dumps(coercion.normalize_collection(decoded))
'{"bytes": "str", "\\u00dcnicode": "bytes"}'