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
{{ message }}
Repository navigation
mypy caches types of attributes for too long, ignoring side effects of methods #14010
# run mypy with '--show-error-codes --strict --warn-unreachable'classSomeClass:
cache_hot: bool=Falsedefsome_method(self) ->None:
# expensive computation goes hereself.cache_hot=Truedeftest_some_class() ->None:
instance=SomeClass()
assertnotinstance.cache_hotinstance.some_method()
# mypy fails to notice that 'cache_hot' has changedreveal_type(instance.cache_hot)
# mypy: note: Revealed type is "Literal[False]"# (this is incorrect)assertinstance.cache_hot# mypy treats this as 'assert False'assertisinstance(instance, SomeClass)
# mypy: error: Statement is unreachable [unreachable]
mypy is caching the type of instance.cache_hot, which is narrowed by the assert statement to Literal[False]; it relies on this type even after side effects of some_method() have changed the value of that attribute. This makes is difficult or impossible to test that the attribute is changed as a side-effect of calling some_method().
The issue affects properties as well as attributes.
Workarounds
Edit: see much better suggestions in comments, below.
mypy does not cache function return values (provided they're not decorated to be a @property).
To Reproduce
See above.
Expected Behavior
If mypy cannot notice the side effect of some_method(), there should be some way to reset the type of attributes on a particular instance.
Actual Behavior
The exact output is:
thecodeabove.py:22: note: Revealed type is "Literal[False]"
thecodeabove.py:27: error: Statement is unreachable [unreachable]
Found 1 error in 1 file (checked 1 source file)
Not sure it could even analyze that statically (in more complex cases) because of the mutation.
You can, however, decide to never typeguard directly on your mutated property. This way mypy and pyright will keep seeing it as a bool.
Yeah, this is a heuristic mypy uses that is needed in practice. Other type checkers do this too. Would need some complicated type system feature to make this expressible.
There are a couple workarounds, but one good one is a dummy re-assignment instance = instance
I agree this would be very difficult to definitively fix, but I was surprised I couldn't find an issue (open or closed) describing this problem or ways to work around it.
I wonder: for the benefit of others who encounter this issue in future, is there some way to make this thread (or the gist of it) easier to find? I anticipate most people won't search for closed issues.
Yeah this issue is difficult to find, I nearly ended up creating a duplicate.
Interestingly upgrading from mypy version 0.910 to 0.991 and from Python 3.8 to 3.11 caused some similar issues for us - we're getting a false positive about Statement is unreachable too.
This self-contained example does the same thing (although I know mypy struggles with @property sometimes):
Our actual code is slightly more complex of the form:
assertnotx.some_prop, "..."assertnoty.some_prop, "..."assertnotz.some_prop, "..."x.some_func("...", y.other_prop, z.other_prop)
assertx.some_prop, "..."# Flagged as unreachable, all 3 statements here assume `some_prop` is `false`.asserty.some_prop, "..."assertz.some_prop, "..."
I find it a little strange that it didn't previously narrow this type (Python 3.8, mypy 0.910) and I'm not sure when it changed given how many releases have happened since then. In any case, the upgrade has fixed some of our other type: ignore statements, so on balance I think it's still a win 😄
Activity
Avasam commentedon Nov 4, 2022
Not sure it could even analyze that statically (in more complex cases) because of the mutation.
You can, however, decide to never typeguard directly on your mutated property. This way mypy and pyright will keep seeing it as a bool.
hauntsaninja commentedon Nov 4, 2022
Yeah, this is a heuristic mypy uses that is needed in practice. Other type checkers do this too. Would need some complicated type system feature to make this expressible.
There are a couple workarounds, but one good one is a dummy re-assignment
instance = instancefinite-state-machine commentedon Nov 4, 2022
I agree this would be very difficult to definitively fix, but I was surprised I couldn't find an issue (open or closed) describing this problem or ways to work around it.
@Avasam @hauntsaninja thank you both for the very helpful suggestions!
finite-state-machine commentedon Nov 5, 2022
I wonder: for the benefit of others who encounter this issue in future, is there some way to make this thread (or the gist of it) easier to find? I anticipate most people won't search for closed issues.
ShadowLNC commentedon Dec 3, 2022
Yeah this issue is difficult to find, I nearly ended up creating a duplicate.
Interestingly upgrading from
mypyversion0.910to0.991and from Python 3.8 to 3.11 caused some similar issues for us - we're getting a false positive aboutStatement is unreachabletoo.This self-contained example does the same thing (although I know
mypystruggles with@propertysometimes):Our actual code is slightly more complex of the form:
I find it a little strange that it didn't previously narrow this type (Python 3.8, mypy 0.910) and I'm not sure when it changed given how many releases have happened since then. In any case, the upgrade has fixed some of our other
type: ignorestatements, so on balance I think it's still a win 😄comparison-overlap#15509 as a duplicate of this issue