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? ShouldNone
be omitted? Yes & Yes. - Allow
declare
to be used as a class decorator. Copydocstring
from decorated classes. - Decorated classes can provide defaults other than
None
.
Next steps
Is this something people are interested in pursuing?