Skip to content

Add a import type feature (like typescript) #928

Open

Description

@KotlinIsland

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).

Activity

  1. added
    topic: featureDiscussions about new features for Python's type annotations
    on Nov 9, 2021
  2. h-joo commented on Oct 15, 2024

    @h-joo

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

  3. Gobot1234 commented on Oct 15, 2024

    @Gobot1234
    Contributor

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

  4. mkzeender commented on Oct 29, 2024

    @mkzeender

    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.

  5. Lordfirespeed commented on Mar 6, 2025

    @Lordfirespeed

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: featureDiscussions about new features for Python's type annotations

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions