None safe
nonesafe makes it safe to parse dictionaries.
When parsing a dictionary from an external source, e.g. a JSON request, dictionary keys might be missing or there may be unknown dictionary keys.
For example suppose you know (or only care about) keys a and b at the top level and that a is also a dictionary that has a c.
>>> d_ok = {'a': {'c': 1}, 'b': 0}
This would be easy to use directly as a dictionary:
>>> d_ok['a']
{'c': 1}
>>> d_ok['a']['c']
1
>>> d_ok['b']
0
But if instead from the external source you got:
>>> d_not_ok = {'a': {'c': 1}, 'not_b': 0}
Then the code above using a dictionary would fail. Instead, use:
>>> import nonesafe as ns
>>> A = ns.declare('A', c=int)
>>> Safe = ns.declare('Safe', a=A, b=int)
>>> s = Safe(d_not_ok)
>>> s.a
A(c=1)
>>> s.a.c
1
>>> s.b
The missing value b is replaced by None (in the doctest above None is treated as not returning a value) and the extra value not_b is ignored. The usage s.expr indicates safe (will not raise an access exception but might return None instead).
There is also a utility function onnone(value, otherwise), that takes a value that might be None and if it is returns otherwise. EG:
>>> ns.onnone(s.b, -1)
-1
The function declare is very flexible, the following are all the same as each other:
>>> Ex0 = ns.declare('Ex0', {'a': int, 'b': int})
>>> Ex1 = ns.declare('Ex1', [('a', int), ('b', int)])
>>> Ex2 = ns.declare('Ex2', a=int, b=int)
>>> Ex3 = ns.declare('Ex3', {'a': int}, b=int)
>>> Ex4 = ns.declare('Ex4', [('a', int)], b=int)
Constructing an instance of a nonsafe class is also very flexible, the following are all the same as each other:
>>> ex0 = Ex0({'a': 0, 'b': 1})
>>> ex1 = Ex0([('a', 0), ('b', 1)])
>>> ex2 = Ex0(a=0, b=1)
>>> ex3 = Ex0({'a': 0}, b=1)
>>> ex4 = Ex0([('a', 0)], b=1)
and these are also the same as each other:
>>> ex5 = Ex0({})
>>> ex6 = Ex0([])
>>> ex7 = Ex0(None)
>>> ex8 = Ex0()
Installation
Simply copy nonesafe.py (note LICENCE), run some examples by executing nonesafe.py
Alternatives
Very similar can be achieved with packages like Pydantic, but they are much too heavyweight for casual use and their inclusion has previously been rejected in favour of dataclasses (PEP 557).
There is also a rejected PEP 505 and a proposal to revive it Revisiting PEP 505
that failed to reach a consensus. 505 proposed introducing new None aware operators ?? (same as onnone), ?., and ?[] (last too equivalent to declareâs behaviour). This module is considerably easier to add than three operators (current proof on concept circa 60 lines) and is arguably superior, because it is declarative. Note operators also need to be added to IDEâs, type-checkers, etc. and need to be taught. For newbies and none computer-science people they will be unfamiliar.
TODO
- Check field value is of correct type or
None(auto-convert if possible). Presently ugly error! - Add
todict: should âextrasâ not parsed be added back in? ShouldNonebe omitted? Yes & Yes. - Allow
declareto be used as a class decorator. Copydocstringfrom decorated classes. - Decorated classes can provide defaults other than
None.
Next steps
Is this something people are interested in pursuing?