-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
MyPy should report error if variable imported in TYPE_CHECKING block? #6104
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
Comments
This is highly questionable, TBH. Although implementation is not hard, it still will require some work, while the value is relatively small. Also this fails immediately at runtime with a (relatively) clear error. |
Thanks for your response! It doesn't always fail immediately—I have had situations where the imports are used somewhere other than the top-level, so the failure sometimes happens an hour into a long-running job. I'd be interested to hear thoughts from other maintainers, but if you're sure this is not worth doing, then feel free to close the issue. |
I think it’s worth fixing. |
I agree that this would be worth fixing. The implementation would have to be clever enough to catch If if TYPE_CHECKING:
from m import A
else:
class A: pass
class B(A): pass # Should be OK It would also be nice to require quotes to be used in type annotations (if not using |
I would like to raise this point again. For a command-line application I am optimising global imports to increase its performance; that often means I need to move imports into a Since Mypy doesn't catch that the value is undefined at runtime, if I make the mistake of not adding the local import, it results in a With some pointers, I'm happy to look into a contribution for this fix. (I am very unfamiliar with the Mypy codebase). |
Related use case:
if TYPE_CHECKING:
MyType = int
# No if TYPE_CHECKING block
from module1 import MyType
|
@JelleZijlstra To quote from the linked duplicate issue #16587 (comment)
I would argue that this is a special case, because it is a required technique for breaking import cycles as documented. Without this feature, breaking import cycles cannot be done cleanly, or even the contrary: It is likely to cause bugs! First a general observation: import cycles are in fact more prominent when using strict typing compared to untyped Python, because even techniques like dependency injection (which typically is intended to break runtime cyclic dependencies) now require imports, but just for the type annotations (i.e., untyped Python would not have these imports). Since import cycles are a more common problem now, it is sensible to provide a clean way to support breaking them. Now without having a check that a symbol got only imported for type annotation usage, we are actually opening the door for a lot of runtime bugs! In other words, without this feature, the recommendation given in the documentation is actually very likely cause bugs, i.e., "type checks fine, but crashes at runtime" -- which is exactly what we want to prevent with type checking. To demonstrate: from dataclasses import dataclass, field
from typing import TYPE_CHECKING
# Pure "type import" to break runtime import cycle e.g. for dependency injection.
if TYPE_CHECKING:
from other_module import Foo
# The following usages are valid, because they only use the symbol in type
# annotation contexts. Note that such usages are common for dependency
# injection.
Alias = list[Foo]
def f1(x: Foo):
...
@dataclass
class ClassUsage1:
foo: Foo
# On the other hand, all the following usages are invalid, because they make
# runtime use of the symbol. Unfortunately the type checker happily accepts
# all these usages in the same module, even though they are all bugs leading
# to crashes at runtime. Ideally the type checker should be aware of the fact
# that the symbol does not exist at runtime, and report all these usages as type
# check errors.
foo = Foo()
def f2(x: Foo = Foo()):
...
@dataclass
class ClassUsage2:
foo: Foo = field(default_factory=lambda: Foo()) We've been running into such bugs rather frequently now. |
Currently, MyPy doesn't report an error when a variable is imported in the
if TYPE_CHECKING
block and then used elsewhere.For example, MyPy accepts this code:
even though, at runtime, it fails:
(I'm using MyPy version 0.650.)
Should this be fixed? I think this could be fixed by adding an
is_type_checking_only
variable toSymbolTableNode
, which is True if the variable was imported or assigned within aTYPE_CHECKING
block, and ensuring that this flag is false for any variables used in places other than type annotations in the actual code.The text was updated successfully, but these errors were encountered: