legacy-union-syntax
LintProse surfaces Union[A, B] and Optional[T] patterns that should read as modern A | B and T | None.
The from __future__ import annotations directive made forward-reference annotations possible on Python versions where the runtime evaluated annotations eagerly. PEP 749 lands deferred annotation evaluation in Python 3.14 by default whenever the file's annotations are typing-only, and the import becomes redundant.
Three branches actually fire the rewrite. The file may carry zero annotations (the directive is unused outright). The target-version may be 3.14 or higher (the runtime defers annotation evaluation, so the directive carries no runtime weight). Or every name appearing in every annotation may resolve to a module-scope binding before its first annotation use (forward references aren't needed, so the runtime evaluates annotations eagerly without raising). When none of those branches holds, the import stays in place.
The version-gated branch stays quiet. Removal fires only if the file has zero annotations or every annotation resolves to a module-scope binding before use.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Toggle the rule on or off |
The target-version field from the top-level Configuration gates the rewrite per project.
A file whose __future__ import when the
class Node:
pass
def visit(node: Node) -> Node:
return node
An annotated assignment nested in a for loop body still marks the file as carrying Item reference resolves to the class above the loop, so trigger 3 fires and the directive is removed.
The annotations alias sits last in a multi-name from __future__ import division, annotations. The surgical deletion strips the preceding comma alongside the alias, leaving the earlier division entry's formatting intact.
A from __future__ import annotations as legacy still imports the annotations feature regardless of the local as form and deletes the line.
An async def carries its return def, with is_async flipped. The annotation probe sees it, and with Result defined ahead of the annotation the directive is removed.
The annotations alias leads a multi-name from __future__ import annotations, division. Only the annotations entry is removed, taking its separator comma with it, leaving division as the sole import.
A # fmt: off block wrapping the directive keeps it in place. The
A class-body x: int int builtin, which the int at runtime.
Prose surfaces Union[A, B] and Optional[T] patterns that should read as modern A | B and T | None.
For the gate semantics, target-version in the Configuration chapter covers how the field is read across version-gated rules.