So your cyclic dependency issue becomes a problem that only gets seen by runtime introspection tools rather than you fixing it. Deferring an import won’t solve a cyclic dependency, you just make it only visible to runtime annotation users, and you’re advocating using it everywhere, making the problem prolific for them.
That sounds like a mischaracterization. Seeing fewer issues caused by circular dependencies is a side effect of allowing type-only imports, but as mentioned previously in this thread, it is not the only motivation for them.
If the imports need to be resolved at runtime, they need to be resolvable without a circular dependency. deferring them doesn’t get rid of this requirement without also changing import semantics in a larger way.
Just for reference, runtime resolvability and cyclic imports are a known thing, and the current “best practice” involves not having a circular import even within TYPE_CHECKING
Not much else to add, this topic is retreading ground other people have covered in multiple ways, and there’s good reasons why lazy importing wasn’t accepted in general, why those who have a need for it have advocated not over-using it (see the lazy_loader from scientific python linked above), and that this shouldn’t be used as a bandaid over design issues so that they have less visibility.
Not necessarily, since the import is delayed, the circular dependency may now no longer be a circular dependency, since both modules have already been fully initialized, so it depends on at which point the runtime introspection happens. Granted, that this scenario is far less likely when using from __future__ import annotations
.
But I really shouldn’t have even pointed out the thing about cyclic dependencies, because it’s a pure aside. I care about the runtime overhead. I’m perfectly happy to keep using if TYPE_CHECKING
for this and use linters to help me determine when I actually do need one of those imports at runtime, but it would be nicer if those linters didn’t need special casing for pydantic models and the like, because the imports could still be resolved at runtime when needed.
Thank you to everyone who has educated me in this conversation. My personal takeaway is:
- We do not need any Python changes
- I certainly won’t be writing module getattrs
- I will be a lot happier using “from future import annotations” and turning on Ruff’s auto-typing-only-import rule now!
I don’t think any existing runtime type checkers will even look up my runtime library types (except @singledispatch
and I know on what that is likely to be used), and I think any hypothetical future library can find typing imports via the AST if it needs to.