typing-only-first-party-import (TC001)
Added in 0.8.0 · Related issues · View source
Derived from the flake8-type-checking linter.
Fix is sometimes available.
What it does
Checks for first-party imports that are only used for type annotations, but aren't imported lazily or defined in a type-checking block.
Why is this bad?
Imports that are only used for type annotations add a performance overhead
at runtime. For first-party imports, they can also contribute to import
cycles. If an import is only used in typing-only contexts, it can instead
be imported conditionally under an if TYPE_CHECKING: block to minimize
runtime overhead.
If lint.flake8-type-checking.quote-annotations is set to true,
annotations will be wrapped in quotes if doing so would enable the
corresponding import to be moved into an if TYPE_CHECKING: block.
If a class requires that type annotations be available at runtime (as is
the case for Pydantic, SQLAlchemy, and other libraries), consider using
the lint.flake8-type-checking.runtime-evaluated-base-classes and
lint.flake8-type-checking.runtime-evaluated-decorators settings to mark them
as such.
If lint.future-annotations is set to true, from __future__ import annotations will be added if doing so would enable an import to be
moved into an if TYPE_CHECKING: block. This takes precedence over the
lint.flake8-type-checking.quote-annotations setting described above if
both settings are enabled.
On Python 3.15 and later, lazy imports are also exempt, including imports
made lazy by a literal __lazy_modules__ declaration. The fix prefers adding
lazy to single-name import statements where the syntax is legal and
lint.flake8-tidy-imports.ban-lazy allows it. This defers the import while
keeping the name available for runtime annotation inspection.
Example
from __future__ import annotations
from . import local_module
def func(sized: local_module.Container) -> int:
return len(sized)
Use instead:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from . import local_module
def func(sized: local_module.Container) -> int:
return len(sized)
On Python 3.15 and later, using a lazy import is also an option:
Fix safety
This rule's fixes are unsafe because changing when a module is imported can affect runtime behavior, including import-time side effects.
Options
lint.flake8-tidy-imports.ban-lazylint.flake8-type-checking.quote-annotationslint.flake8-type-checking.runtime-evaluated-base-classeslint.flake8-type-checking.runtime-evaluated-decoratorslint.flake8-type-checking.strictlint.typing-moduleslint.future-annotations