Yes, agreed, importing things in functions is not common nor encouraged. And for the majority of use cases, even the current behavior of PEP 563 should work fine.
I think the main issue boils down to avoiding frustration from (and improving the inclusion of) newcomers and non-experts, in those particular corner cases.
Here’s an example that would be a bit more reasonable and probably a bit more conventional (some lines longer):
from __future__ import annotations
import typing
from pydantic import BaseModel, PositiveInt
def main():
class Foo(BaseModel):
total: PositiveInt
class Bar(BaseModel):
foo: Foo
# crashes with current PEP 563
typing.get_type_hints(Bar)
main()
This would result in a NameError: name 'Foo' is not defined
.
For a newcomer, after seeing the error, it would probably not be obvious that using the class Foo
(declared inside the function) as a type annotation is not supported. Although Foo
alone (without Bar
) would be supported.
In many cases, it would probably be fine to just move those models outside the function, but it would not be obvious for newcomers why and when that is needed. And I wouldn’t expect people that use these libraries and tools to have enough expertise to know the underlying details and understand the why of the errors and how to handle them (I myself didn’t understand those details until recently ).
Another corner case that could need to be able to import types used in annotations at runtime is avoiding cyclic imports (as in the second example in my previous post).
This possible paper cut becomes a bit more delicate/relevant with the fact that many (most?) users of Pydantic come via FastAPI. And FastAPI is growing in adoption, in particular by newcomers to Python. That’s probably because the intention in FastAPI’s design and docs is to be easy to learn and use by everyone. In several cases I’ve seen, people are even migrating to Python from other languages to use FastAPI. And I can imagine how frustrating it could be for all these developers that are not experts in Python internals to not be able to have some classes or imports inside of functions in some corner cases, although in most cases it would work well. So, in the end, all this is just to better support those newcomers and non-experts, and to avoid non-obvious limitations in functionality.