Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --strict-bool flag to prohib treating bool as int #17487

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,26 @@ of the above sections.

assert text is not None # OK, check against None is allowed as a special case.

.. option:: --strict-bool

By default ``bool`` values are treated as subtypes of ``int``,
just like in runtime:

.. code-block:: python

>>> bool.__mro__
(<class 'bool'>, <class 'int'>, <class 'object'>)

While it will work in runtime,
some cases might require a little bit more strictness.
With this flag enabled, you will get the following error:

.. code-block:: python

def requires_int(arg: int) -> None: ...

requires_int(5 > 0) # Error: Argument 1 has incompatible type "bool"; expected "int"

.. option:: --extra-checks

This flag enables additional checks that are technically correct but may be
Expand Down
8 changes: 8 additions & 0 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,14 @@ def add_invertible_flag(
group=strictness_group,
)

add_invertible_flag(
"--strict-bool",
default=False,
strict_flag=True,
help="Prohib to treat bool as int",
group=strictness_group,
)

add_invertible_flag(
"--extra-checks",
default=False,
Expand Down
5 changes: 5 additions & 0 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class BuildType:
"mypyc",
"strict_concatenate",
"strict_equality",
"strict_bool",
"strict_optional",
"warn_no_return",
"warn_return_any",
Expand Down Expand Up @@ -209,6 +210,10 @@ def __init__(self) -> None:
# This makes 1 == '1', 1 in ['1'], and 1 is '1' errors.
self.strict_equality = False

# Prohibit to treat `bool` as `int` in subtyping contexts.
# This makes `def a(b: int): ...; a(True)` an error.
self.strict_bool = False

# Deprecated, use extra_checks instead.
self.strict_concatenate = False

Expand Down
7 changes: 7 additions & 0 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,13 @@ def visit_instance(self, left: Instance) -> bool:
if left.type.alt_promote and left.type.alt_promote.type is right.type:
return True
rname = right.type.fullname
if (
self.options
and self.options.strict_bool
and left.type.fullname == "builtins.bool"
and rname == "builtins.int"
):
return False
# Always try a nominal check if possible,
# there might be errors that a user wants to silence *once*.
# NamedTuples are a special case, because `NamedTuple` is not listed
Expand Down
34 changes: 34 additions & 0 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -2284,3 +2284,37 @@ class C(Generic[T]): ...

A = Union[C, List] # OK
[builtins fixtures/list.pyi]

[case testStrictBool]
# flags: --strict-bool --show-error-codes
from typing import List, Union

def a(x: int): ...
a(True) # E: Argument 1 to "a" has incompatible type "bool"; expected "int" [arg-type]
a(False) # E: Argument 1 to "a" has incompatible type "bool"; expected "int" [arg-type]

bl: bool
a(bl) # E: Argument 1 to "a" has incompatible type "bool"; expected "int" [arg-type]

def b() -> int:
return bl # E: Incompatible return value type (got "bool", expected "int") [return-value]

c: List[int] = [
True, # E: List item 0 has incompatible type "bool"; expected "int" [list-item]
False, # E: List item 1 has incompatible type "bool"; expected "int" [list-item]
bl, # E: List item 2 has incompatible type "bool"; expected "int" [list-item]
]

# OK:
def d(x: Union[int, bool], y: bool): ...
d(1, True)
d(True, False)
d(bl, bl)
[builtins fixtures/list.pyi]

[case testStrictBoolWithStrictFlag]
# flags: --strict

def a(x: int) -> None: ...
b: bool
a(b) # E: Argument 1 to "a" has incompatible type "bool"; expected "int"
Loading