My love for python was critically hurt when I learned about typing.TYPE_CHECKING.
For those unaware, due to the dynamic nature of Python, you declare a variable type like this
foo: Type
This might look like Typescript, but it isn't because "Type" is actually an object. In python classes and functions are first-class objects that you can pass around and assign to variables.
The obvious problem of this is that you can only use as a type an object that in "normal python" would be available in the scope of that line, which means that you can't do this:
def foo() -> Bar:
return Bar()
class Bar:
pass
Because "Bar" is defined AFTER foo() it isn't in the scope when foo() is declared. To get around this you use this weird string-like syntax:
def foo() -> "Bar":
return Bar()
This already looks ugly enough that should make Pythonists ask "Python... what are you doing?" but it gets worse.
If you have a cyclic reference between two files, something that works out of the box in statically typed languages like Java, and that works in Python when you aren't using type hints because every object is the same "type" until it quacks like a duck, that isn't going to work if you try to use type hints in python because you're going to end up with a cyclic import. More specifically, you don't need cyclic imports in Python normally because you don't need the types, but you HAVE to import the types to add type hints, which introduces cyclic imports JUST to add type hints. To get around this, the solution is to use this monstrosity:
if typing.TYPE_CHECKING:
import Foo from foo
And that's code that only "runs" when the static type check is statically checking the types.
Nobody wants Python 4 but this was such an incredibly convoluted way to add this feature, specially when you consider that it means every module now "over-imports" just to add type hints that they previously didn't have to.
Every time I see it makes me think that if type checks are so important maybe we shouldn't be programming Python to begin with.