Fall back to the typing.* namespace in lazy hint contexts

With PEP 649 impending, I wanted to float this idea.

Adding types to a Python code-base requires importing many names from the typing module. After a while it’s natural to start pondering whether some of the more common ones couldn’t be made builtins. However, adding builtins is not something that can be done lightly, and I would expect to see exactly zero progress should that idea be seriously proposed.

Still, it seems a bit silly that the following program requires an import.

from typing import Final

a: Final = 1

So, what if we changed the lookup semantics for all lazy type hints, such that when a NameError would have usually been raised, the lookup function first checks if the name exists in the typing module, and uses that?

Combined with the generic type alias syntax introduced in PEP 695, I imagine this would allow the following program to be valid, without adding any imports.

type MyType = Literal[1, 2]

a: Final[MyType] = 1

Is this something that could be a viable idea?

I was wondering about a rather different option: deferred imports.

Instead of putting annotation-only imports behind an if TYPE_CHECKING guard, could we instead make them lazy?

At it’s simplest, that might be something like

typing = LazyImport("typing")
Final = LazyImport("typing", "Final")

Or perhaps we add new syntax, or perhaps there’s some much smarter option I’ve not thought of.

The key would be integrating this into both type checkers and runtime annotation resolution, so you only pay the cost of the import if you use code that checks annotations at runtime.

This would allow types outside of typing to participate.

Having lazy type imports using the existing soft type keyword would be very convenient: type import ... or from ... type import ....

4 Likes

Lazy imports have been proposed and rejected before, though with a different motivation (PEP 690 – Lazy Imports | peps.python.org). I think a variation of that proposal could pass, but the devil is in the details.

3 Likes

The part of this idea that felt valuable to me is really the simplification, removing the need to have to think about imports for lots of simple typing cases. I think this would be especially useful for newcomers to the language, or for someone starting to learn about type hints.

@srittau yes, I remember that sort of syntax was mentioned in the old typing-sig thread where the soft type keyword was first proposed. It’s a neat idea, and I think these two ideas could be compatible and complementary.

Unlike that pep, I wasn’t imagining these being transparent, except when resolving annotations at runtime. I don’t know if that makes things simpler?

I’m wondering how that would play with PEP 649. But I much prefer this idea as it can extend to non-typing objects as well. Plus not having to add the typing import as initially proposed could be confusing with respect to typing_extensions.

To not clutter this discussion, maybe a new thread regarding this import type syntax could be open? Maybe some people will find it interesting.

2 Likes

I can do that now. Will take me a few minutes to summarise what’s been pointed out here

1 Like

@srittau This is the thread I was thinking of: Mailman 3 [Typing-sig] Re: Type assignment statement (and extending the annotation syntax) - Typing-sig - python.org

Why would typing be privileged above all other modules to have this implicit behavior?

5 Likes

Quite a few useful types are now in collections.abc, the deprecated aliases in typing will be removed. This wouldn’t make any of those common cases more convenient, and would just accentuate the difference.

8 Likes

I created a separate discussion for the lazy import idea. Apologies if I misunderstood anyone!

Yes, this is a somewhat valid concern. However, if we think we want this feature, this seems like an easy problem to solve a bit further ahead in the design process. Perhaps it wouldn’t practically end up being the raw typing module’s namespace, but some other curated list of “the most common canonical types”.

I feel like my previous posts has this justification in it already. If we decide this is a good idea for Python, it would have this privilege because we chose to give it that privilege. Because the net effect of using the language with this change was perceived as positive.

  • It eliminates some extremely common imports. This has been at least a partial driver for previous typing features as well, like special syntax for union.
  • It would seemingly make the language easier to learn.

Is there some other module you have in mind as a better target?

Other than builtins, I don’t think any module should be in this position. Or maybe create a general purpose delayed import mechanism. But I think anointing typing is just wrong.

7 Likes

The point is exactly that though: typed Python needs its own set of builtins. Modifying the runtime set of builtins to accommodate those is out of the question (or is that what you’re suggesting?).

The typing module is already “privileged” because it is the chosen module for all typing special forms to live in. With the example of Final, in most languages that has something equivalent, this is typically a language construct that’s usable without imports.

The proposal I’m making is to make fundamental typing features usable, in a context that exists exclusively for the purpose of typing, without having to add imports. Can you expand on why you think is wrong?

1 Like

Isn’t the proposal the same as adding all of typing to builtins? I think it should be analyzed from that perspective.

Can you explain why “without having to use imports” is important here? Imports are a fundamental part of Python[1], so trying to eliminate them seems misguided.


  1. you even need an import to access the program’s command line arguments ↩︎

3 Likes

Hmm, I think my proposal would really only impact the logic in typing.get_type_hints() and the equivalent in the inspect module, I forget its name. I suspect I just failed to state this with sufficient clarity? (Admittedly, I haven’t explored the idea in depth, I thought it was worth just throwing it out here).

In my opinion, chosing typing as the fallback makes sense, but for sake of exploration, would the proposal look more attractive to you if the fallback namespace was a parameter of those functions? It could even be an ordered sequence of fallback namespaces.

In a way yes, the perspective of comparison to adding all of typing to builtins makes sense. This change should be done with a high level of scrutiny to what gets this special treatment, and perhaps the selection should be more narrow than “whatever lives in typing”.

Does your editor add missing imports as you type? Mine does.

Let me suggest a general idea: A lot of ideas come up that want to minimize typing. Typing doesn’t matter though; reading is what matters. Imports are not a pain to read since they’re already off to the side. Moreover, they’re useful since they make it obvious to a reader where various symbols come from. If typing is annoying, the solution is usually to improve your editor.