-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Open
Labels
ruleImplementing or modifying a lint ruleImplementing or modifying a lint rule
Description
Description
I stumbled upon some code of mine where it seems to me that mypy and ruff do not agree whether Literal[True, False] == bool and rule RUF038 suggest a fix which introduces a mypy error.
Edit: I read the docs for the rule again and this general case seems to be mentioned in the "Fix safety" already. Maybe this example is different, but if you feel like it is already covered well enough, feel free to close the issue!
Consider this code containing two @overloads and illustrating perhaps two different problems:
import random
from typing import Literal, overload
class Foo:
@overload
def foo(self, *, bar: Literal[True]) -> int: ...
@overload
def foo(self, *, bar: Literal[False]) -> float: ...
def foo(self, *, bar: Literal[True, False]) -> int | float:
return 0
class FooSub(Foo):
@overload
def foo(self, *, bar: Literal[True]) -> int: ...
@overload
def foo(self, *, bar: Literal[False]) -> float: ...
def foo(self, *, bar: Literal[True, False]) -> int | float:
return super().foo(bar=bar)
def compute_bool() -> bool:
return random.random() > 10
Foo().foo(bar=compute_bool()) # Problem 1: For this line mypy raises an error$ mypy --strict test.py
test.py:31: error: No overload variant of "foo" of "Foo" matches argument type "bool" [call-overload]
test.py:31: note: Possible overload variants:
test.py:31: note: def foo(self, *, bar: Literal[True]) -> int
test.py:31: note: def foo(self, *, bar: Literal[False]) -> float
Found 1 error in 1 file (checked 1 source file)And ruff suggest fixing the annotations like this:
$ ruff check --isolated --select RUF --preview test.py
test.py:12:27: RUF038 `Literal[True, False]` can be replaced with `bool`
|
10 | def foo(self, *, bar: Literal[False]) -> float: ...
11 |
12 | def foo(self, *, bar: Literal[True, False]) -> int | float:
| ^^^^^^^^^^^^^^^^^^^^ RUF038
13 | return 0
|
= help: Replace with `bool`
test.py:23:27: RUF038 `Literal[True, False]` can be replaced with `bool`
|
21 | def foo(self, *, bar: Literal[False]) -> float: ...
22 |
23 | def foo(self, *, bar: Literal[True, False]) -> int | float:
| ^^^^^^^^^^^^^^^^^^^^ RUF038
24 | return super().foo(bar=bar)
|
= help: Replace with `bool`
Found 2 errors.
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).Resulting in:
import random
from typing import Literal, overload
class Foo:
@overload
def foo(self, *, bar: Literal[True]) -> int: ...
@overload
def foo(self, *, bar: Literal[False]) -> float: ...
def foo(self, *, bar: bool) -> int | float:
return 0
class FooSub(Foo):
@overload
def foo(self, *, bar: Literal[True]) -> int: ...
@overload
def foo(self, *, bar: Literal[False]) -> float: ...
def foo(self, *, bar: bool) -> int | float:
return super().foo(bar=bar) # Problem 2 introduced
def compute_bool() -> bool:
return random.random() > 10
Foo().foo(bar=compute_bool()) # Problem 1 again, same as unfixed codeAnd again mypy on the fixed code:
$ mypy --strict test_fixed.py
test_fixed.py:24: error: Returning Any from function declared to return "int | float" [no-any-return]
test_fixed.py:24: error: No overload variant of "foo" of "Foo" matches argument type "bool" [call-overload]
test_fixed.py:24: note: Possible overload variants:
test_fixed.py:24: note: def foo(self, *, bar: Literal[True]) -> int
test_fixed.py:24: note: def foo(self, *, bar: Literal[False]) -> float
test_fixed.py:31: error: No overload variant of "foo" of "Foo" matches argument type "bool" [call-overload]
test_fixed.py:31: note: Possible overload variants:
test_fixed.py:31: note: def foo(self, *, bar: Literal[True]) -> int
test_fixed.py:31: note: def foo(self, *, bar: Literal[False]) -> float
Found 3 errors in 1 file (checked 1 source file)$ python --version
Python 3.11.10
$ ruff --version
ruff 0.9.6
$ mypy --version
mypy 1.15.0 (compiled: yes)Metadata
Metadata
Assignees
Labels
ruleImplementing or modifying a lint ruleImplementing or modifying a lint rule