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

TYPE_CHECKING only imports should trigger errors on non-annotation usage #16587

Closed
RonnyPfannschmidt opened this issue Nov 29, 2023 · 7 comments
Labels
bug mypy got something wrong topic-runtime-semantics mypy doesn't model runtime semantics correctly

Comments

@RonnyPfannschmidt
Copy link

Bug Report

this should warn for all non-annotation usages of FunctionType

from typing import TYPE_CHECKING, cast

if TYPE_CHECKING:
    from types import FunctionType
    
def testit(func: FunctionType) -> None:
    assert isinstance(func, FunctionType)


def something() -> None:
    pass


testit(cast(FunctionType, something)

Expected Behavior

warn that FunctionType can only be used in annotations

@RonnyPfannschmidt RonnyPfannschmidt added the bug mypy got something wrong label Nov 29, 2023
@AlexWaygood AlexWaygood added the topic-runtime-semantics mypy doesn't model runtime semantics correctly label Nov 29, 2023
@JelleZijlstra
Copy link
Member

I don't think we should warn here. if TYPE_CHECKING means that the type checker should treat this condition as true, and therefore assume that FunctionType is imported. It's a structured way of lying to the type checker. And if you lie to the type checker, you're responsible for the consequences.

It's also easy for simpler linters to catch this sort of thing.

@RonnyPfannschmidt
Copy link
Author

My understanding is that this was primarily added to help avoid recursive imports just to add annotations

It would be nice if it would hint at.missuse of those when type-checking

@flisboac
Copy link

I belive this could work if you use postponed evaluation of annotations, but it requires some changes in the code:

from __future__ import annotations

from typing import TYPE_CHECKING, cast

if TYPE_CHECKING:
    from types import FunctionType
    
def testit(func: FunctionType) -> None:
    from types import FunctionType # <<--- Without this, you won't have `FunctionType` available at runtime
    assert isinstance(func, FunctionType)


def something() -> None:
    pass


# ------

# As it is, this won't work.
# You must import it beforehand, but the import is conditional to a static type-checking scenario. 
testit(cast(FunctionType, something)

# An option is to enclose it in a function instead, and import your circular type inside the implementation,
# just like what we did before.
def execute_testit() -> None:
    from types import FunctionType
    testit(cast(FunctionType, something)

Not pretty, I know. Also, I'm not entirely sure if you can use typing.get_type_hints on those cases, though.

An alternative to from __future__ import annotations is to just "stringify" your lazy-loaded types instead, e.g. def testit(func: "FunctionType") -> None.

@RonnyPfannschmidt
Copy link
Author

@flisboac what i want is a warning when the specific import for runtime use is missing

mypy wont error if a import is turned from normal to type checking only and there are actual usages instead of just annotation usages

@flisboac
Copy link

flisboac commented Nov 30, 2023

Well, then I think @JelleZijlstra 's reasoning is sound.

TYPE_CHECKING was made for the exact purpose of conditionally executing code (or in this case importing types) in the context of static type checking. This would allow you to avoid things like importing modules with costly runtime side-effects just for the sake of type checking. Python is not a statically-typed language, so this is reasonable.

@cmeyer
Copy link

cmeyer commented Jan 29, 2024

Type checking, in general, was made to reduce errors to code. If it's possible to flag potential errors arising from incomplete types, it seems more reasonable to report those errors.

I don't buy the argument that using TYPE_CHECKING is lying to the type checker. Forward types in other languages (C++) are well established and in no way are they "lying to the [compiler]".

Also, I think this should produce an error during strict type checking, not a warning.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Jan 29, 2024

Closing as a duplicate of #6104

Jelle is correct that mypy's behaviour matches the spec, but I agree that it would be nice to do something non-standard to issue diagnostics to users if mypy could be sure the code will fail at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-runtime-semantics mypy doesn't model runtime semantics correctly
Projects
None yet
Development

No branches or pull requests

6 participants