Batu Lab NotesPractical developer guides

Reject a bool where an integer option is required

By Batu ยท English technical notes

Also published in our primary archive.

To reject a bool where an integer option is required, do not use isinstance(value, int) alone. Use type(value) is int when exact built-in integers are the contract. This fixture shows the failure plainly: isinstance(True, int) accepts True. The corrected validator then accepts only 3 and rejects True and 3.0.

Python deliberately makes bool a subclass of int, so an isinstance check is broad enough to include Boolean values. That can be appropriate for a numeric API, but it is usually wrong for a constrained option such as a retry count or a numbered menu choice. Checking only value == 3 is also not a type boundary: equality permits comparisons across numeric types. The corrected function first requires the exact type and then checks the allowed value.

The assertions document this specific contract. legacy records that the broad check accepts both True and 3; accepted and rejected record the outcome after the correction. They do not prove that every integer-like object should be rejected. If the API intentionally supports integer subclasses, define that rule explicitly and exclude bool separately.

This example uses no newer API; it runs on supported Python 3 versions.

AI assistance disclosure: this article was drafted with AI assistance and the synthetic example is intended to be run locally.

Source: Python documents isinstance() and bool, including that bool is a subclass of int.

def legacy_accepts(option):
    return isinstance(option, int)


def require_exact_option(option):
    if type(option) is not int or option != 3:
        raise ValueError("option must be the exact integer 3")
    return option


inputs = [True, 3, 3.0]
legacy = [legacy_accepts(option) for option in inputs]
accepted = []
rejected = []

for option in inputs:
    try:
        accepted.append(require_exact_option(option))
    except ValueError:
        rejected.append(repr(option))

assert legacy == [True, True, False]
assert accepted == [3]
assert rejected == ["True", "3.0"]

print(f"legacy accepts True: {legacy[0]}")
print(f"accepted: {accepted}")
print(f"rejected: {rejected}")
legacy accepts True: True
accepted: [3]
rejected: ['True', '3.0']