Skip to content

Add a import type feature (like typescript) #928

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

Open
KotlinIsland opened this issue Nov 9, 2021 · 4 comments
Open

Add a import type feature (like typescript) #928

KotlinIsland opened this issue Nov 9, 2021 · 4 comments
Labels
topic: feature Discussions about new features for Python's type annotations

Comments

@KotlinIsland
Copy link

KotlinIsland commented Nov 9, 2021

Importing types can lead to circular imports, longer start up times and unwanted side effects.

I want something like:

from foo.bar import type Baz

def foo(baz: Baz):
    print(baz.bat)

Such that foo, foo.bar and foo.bar.Baz are not actually loaded at all.
It could 'import' the symbol as a forward-ref or some type machinery thing. (maybe just the string "Baz")

Alternatives:

from __future__ import annotations

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from foo.bar import Baz

def foo(baz: Baz):
    print(baz.bat)

This is mega boilerplate, gross, and leads to messy wacky 'type-time' side effects (python/mypy#11503).

@h-joo
Copy link

h-joo commented Oct 15, 2024

+1 to this feature, it would help the code to be much readable to the alternative that we have right now

@Gobot1234
Copy link
Contributor

I was personally interested in this but if it gave you lazy imports so it'd still be introspectable

@mkzeender
Copy link

mkzeender commented Oct 29, 2024

An easy solution is to have

from foo import type Baz

be syntactic sugar for

type Baz[_] = __import__('foo').Baz

In other words, create a (generic) type alias whose __value__ is lazily resolved to the actual Baz class.

@Lordfirespeed
Copy link

Lordfirespeed commented Mar 6, 2025

Would be nice to avoid needing to specify type for a collection of type-only imports.

Python named imports (from ... import ...) are analogous to TypeScript 'named' imports.
Python doesn't have an equivalent to TypeScript 'default' imports, so those can be ignored.
Python module imports (import ...) are analogous to TypeScript 'namespace' imports.
Python wildcard imports (from ... import *) don't have an analogue in TypeScript, but can be considered much the same as named imports for this purpose, I believe.

Current TypeScript import examples (restricted to syntax with appropriate Python analogue)

// region named imports
import { foo, bar } from "..."  // value-only named imports
import { foo, bar } from "..."  // as above, with 'rename'

import type { Foo, Bar } from "..."  // type-only named imports
import type { Foo: FooByAnotherName, Bar } from "..."  // as above, with 'rename'

import { type Foo, type Bar } from "..."  // type-only named imports, specified per-name
import { type Foo: FooByAnotherName, type Bar } from "..."  // as above, with 'rename'

import { foo, type Foo } from "..."  // mixed named imports, specified per-name
import { foo as fooByAnotherName, type Foo } from "..."  // as above, with 'rename'
// endregion

// region namespace imports
import * as module from "..."  // value namespace import with 'rename'
import type * as Module from "..."  // type namespace import with 'rename'
// endregion

Here's the Python syntax I propose, covering Python's various 'flavours' of import:

# region named imports
from ... import foo, bar  # value-only named imports (unparenthesised)
from ... import foo as foo_by_another_name, bar  # as above, with 'rename'
from ... import (foo, bar)  # value-only named imports (parenthesised)
from ... import (foo as foo_by_another_name, bar)  # as above, with 'rename'

from ... import type (Foo, Bar)  # type-only named imports (parenthesis required)
from ... import type (Foo as FooByAnotherName, Bar)  # as above, with 'rename'

from ... import type Foo, type Bar  # type-only named imports, specified per-name (unparenthesised)
from ... import type Foo as FooByAnotherName, type Bar  # as above, with 'rename'
from ... import (type Foo, type Bar)  # type-only named imports, specified per-name (parenthesised)
from ... import (type Foo as FooByAnotherName, type Bar)  # as above, with 'rename'

from ... import foo, type Foo  # mixed named imports, specified per-name (unparenthesised)
from ... import foo as foo_by_another_name, type Foo  # as above, with 'rename'
from ... import (foo, type Foo)  # mixed named imports, specified per-name (parenthesised)
from ... import (foo as foo_by_another_name, type Foo  # as above, with 'rename'
# endregion

# region module imports
import ...  # value module import
import ... as module  # as above, with 'rename'
import ..., ...  # value multiple module import
import ... as module, ...  # as above, with 'rename'
import type ...  # type module import
import type ... as Module  # as above, with 'rename'
import type ..., type ...  # type multiple module import
import type ... as Module, type ...  # as above, with 'rename'
# endregion

# region wildcard imports
from ... import *  # value wildcard import
from ... import type *  # type wildcard import
# endregion

Here's a modified import_stmt grammar that I think matches the above: https://gist.github.com/Lordfirespeed/8a8140d94007c45679a3701b90968c12

An alternative to the 'parenthesis required' rule would be to use types vs type to distinguish between type-only and mixed imports. However, that would conflict with the existing types module, and I don't know enough about grammars/parsers to know whether that would break e.g. import types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: feature Discussions about new features for Python's type annotations
Projects
None yet
Development

No branches or pull requests

6 participants