Integer fields should not accept booleans as defaults and vice versa

Erwan Rouchet
2022-04-28T11:01:53Z

In Python, `True` and `False` are both integers and booleans: ```python >>> isinstance(0, bool), isinstance(0, int) (False, True) >>> isinstance(1, bool), isinstance(1, int) (False, True) >>> isinstance(False, bool), isinstance(False, i...

In Python, True and False are both integers and booleans:

>>> isinstance(0, bool), isinstance(0, int)
(False, True)
>>> isinstance(1, bool), isinstance(1, int)
(False, True)
>>> isinstance(False, bool), isinstance(False, int)
(True, True)
>>> isinstance(True, bool), isinstance(True, int)
(True, True)

Interestingly, DRF does the exact opposite:

>>> from rest_framework.serializers import BooleanField, IntegerField
>>> BooleanField().to_internal_value(0)
False
>>> BooleanField().to_internal_value(1)
True
>>> IntegerField().to_internal_value(True)
ValidationError: [ErrorDetail(string='A valid integer is required.', code='invalid')]
>>> IntegerField().to_internal_value(False)
ValidationError: [ErrorDetail(string='A valid integer is required.', code='invalid')]

That is because DRF first does str(True) before using int('True', 10), and while True is an integer, 'True' isn't.

I randomly found a worker where a int field had a default value of false: YAML — Sample process

JavaScript is not happy about this, because false !== 0 and true !== 1, and there are no unit tests on the backend side for either case. This can lead to confusion for worker developers because many are not aware that an integer can be a boolean in Python.

There should be some unit tests (or maybe some self.subTest to test a lot of cases at once) to ensure that an int with default: false, an int with default: true, a boolean with default: 0 and a boolean with default: 1 are all invalid.