colander
colander copied to clipboard
Boolean type should fallback to False instead of True!!!
If I deserialize a dictionary containing None as value for a boolean node, Colander falls back to True, which is absolutely wrong... a default for a boolean must always be False by definition.
I currently subclassed Boolean in this way to make it work as expected:
class SmartBoolean(Boolean):
def deserialize(self, node, cstruct):
if cstruct in (null, None, ''):
return null
return super().deserialize(node, cstruct)
But I hope you will consider to replace True with False in the last line of original Boolean deserialize() method. Thanks!
Please create a PR with tests and sign CONTRIBUTORS.txt if you haven't, and we can pull it into colander!
a default for a boolean must always be False by definition
Not to be too pedantic but I don't think that's actually in the definition of a boolean. I can't find anything about what the default should be on wikipedia. Most values actually default to True exception 0, None and a few empty collections in python.
ANYWAY I'm not against changing this, but someone who cares (you) will need to submit a PR with tests as Bret said. :-)
Looking at the source for Boolean it looks like you can define how you want to serialize/deserialize values. It says:
The behaviour for values not contained in :attr:`false_choices`
depends on :attr:`true_choices`: if it's empty, any value is considered
``True``; otherwise, only values contained in :attr:`true_choices`
are considered ``True``, and an Invalid exception would be raised
for values outside of both :attr:`false_choices` and :attr:`true_choices`.
So, because the default for false values is 'false' or 0, None ends up being considered True.
Like most of colander, it's probably a good idea to translate None values from your database into colander.null and then you'll get true, false, and null values.
I will submit a PR soon...
in the meanwhile, just google "default boolen value" or try this in a python shell:
assert bool() is False
then...
assert bool() is True
boom! :D
@daveoncode why not add str(None) as a value to false_choices?
For example:
class Boolean(colander.Boolean):
"""Overrides the default false_choices to include 'None' unless false_choices is provided."""
def __init__(self, **kwargs):
kwargs["false_choices"] = kwargs.get("false_choices", ('false', '0', str(None)))
super().__init__(
**kwargs,
)
In the code sample you supplied in
https://github.com/Pylons/colander/issues/250#issue-127900218 you return colander.null which may do what you expect it to do only if you set missing=False on your SchemaNode, so it will still not return False by default.
this is very surprising behavior that just bit me in the ass in production no less.