You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
tldr, since PR #17817 release in mypy 1.12, and still this way in master11cc21c, I have seen this behaviour change in the interaction of type narrowing, assert and unreachability checking:
# prereq foo.x: int = 0
assert not foo.x # narrows foo.x to Literal[0]
foo.reset() # modifies `foo.x=4`, so `foo.x: Literal[0] = 4` (!)
assert foo.x # at runtime this passes, but mypy reasons Literal[0] cannot pass this assert
# BAD: anything after here is warned as unreachable
mypy believes assert not foo.x and assert foo.x cannot both pass, mediated via type narrowing, across an opportunity for an object to change its own state.
To Reproduce
$ cat mp818.py
from typing import Union
class Foo:
def __init__(self):
self.x: int = 0
def reset(self):
self.x=4
foo=Foo()
reveal_type(foo.x)
assert not foo.x
reveal_type(foo.x)
# SWAP THESE LINES TO MAKE MYPY HAPPY
# foo.x = 4
foo.reset()
reveal_type(foo.x)
assert foo.x
print(foo.x)
$ mypy mp818.py --warn-unreachable
# BAD, >= mypy PR #17818
mp818.py:5: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs [annotation-unchecked]
mp818.py:12: note: Revealed type is "builtins.int"
mp818.py:14: note: Revealed type is "Literal[0]"
mp818.py:20: note: Revealed type is "Literal[0]"
mp818.py:23: error: Statement is unreachable [unreachable]
Found 1 error in 1 file (checked 1 source file)
# GOOD, < PR #17818 *or* swap foo.reset() to foo.x=4
mp818.py:5: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs [annotation-unchecked]
mp818.py:12: note: Revealed type is "builtins.int"
mp818.py:14: note: Revealed type is "builtins.int"
mp818.py:20: note: Revealed type is "builtins.int"
Success: no issues found in 1 source file
I expected the behaviour to be the pre-17818 behaviour: if i assert on a field inside an object, don't carry reasoning from earlier asserts about that value across calls that might modify that value.
Actual Behavior
An assertion about the state of an object is carried across a mutation of that state and used to make incorrect reasoning about later asserts.
Bug Report
tldr, since PR #17817 release in mypy 1.12, and still this way in
master
11cc21c, I have seen this behaviour change in the interaction of type narrowing, assert and unreachability checking:mypy believes
assert not foo.x
andassert foo.x
cannot both pass, mediated via type narrowing, across an opportunity for an object to change its own state.To Reproduce
I'm encountering this in real life at https://github.com/Parsl/parsl/blob/34a2890c65f239bb145dca6af76bbdcc0443bc3e/parsl/tests/test_curvezmq.py#L310 where we assert that a socket is open, do something that should close it, then assert that it is really is closed.
Expected Behavior
I expected the behaviour to be the pre-17818 behaviour: if i assert on a field inside an object, don't carry reasoning from earlier asserts about that value across calls that might modify that value.
Actual Behavior
An assertion about the state of an object is carried across a mutation of that state and used to make incorrect reasoning about later asserts.
Your Environment
works with: mypy 1.11, 1.5.1
fails with: mypy 1.12, f6520c8,
master
yesterday ccf05dbmypy mp818.py --warn-unreachable
(mp818.py is my reproducer above)mypy.ini
(and other config files):none
Python 3.12
The text was updated successfully, but these errors were encountered: