Skip to content

Add optional "Annotation Complexity" checker to flag deeply nested type hints #10748

@qequ

Description

@qequ

Current problem

Motivation

As Python type hints become widely adopted, it's increasingly common to see deeply nested or verbose type annotations across codebases, e.g.:

def f() -> tuple[list[dict], dict[str, Any]]:
    ...

While technically valid, such annotations are difficult to read and maintain. A linting rule that flags excessive annotation complexity (e.g., nested generics beyond a certain depth) could help maintain readability and encourage usage of type aliases or TypedDict/helper types.

Desired solution

Proposal

Add an optional checker (e.g. annotation-complexity) to Pylint that:

  1. Inspects function return and parameter annotations
  2. Computes a complexity metric (for example, nesting depth of generics, or element count) for each annotation
  3. Emits a warning when the metric exceeds a configurable threshold (e.g., max-annotation-depth = 5)
  4. Allows configuration in .pylintrc (e.g., disable the check, adjust threshold, treat as error vs warning)

Example behavior

C9001 annotation-too-deep: Type annotation too deep (depth=7, max=5). Consider using a TypeAlias.
C9002 annotation-too-many-elements: Type annotation has too many elements (count=11, max=10). Consider using a TypeAlias.
I9001 annotation-needs-alias: Consider extracting this complex annotation into a TypeAlias for better readability.

On this code:

def process(
    data: dict[str, dict[str, list[tuple[int, str]]]]
) -> dict[str, list[dict[str, Any]]]:
    ...

If either annotation's metric exceeds the threshold, the checker warns.

Configuration Example

[MASTER]
load-plugins = pylint_annotation_complexity

[annotation-complexity]
max-annotation-depth = 5
max-annotation-elements = 10
suggest-type-alias = yes

Complexity Metrics

Depth Metric - Measures maximum nesting level:

  • int → depth 1
  • list[int] → depth 2
  • dict[str, list[int]] → depth 3
  • tuple[list[dict], dict[str, Any]] → depth 4

Element Count Metric - Counts total type references:

  • int → 1 element
  • list[int] → 2 elements
  • dict[str, list[int]] → 4 elements
  • Union[int, str, float] → 4 elements

I have a working prototype implementation that:

  • Supports both depth and element count metrics
  • Works with modern astroid (handles removal of Index node)
  • Provides three message types (error for depth, error for element count, info for suggestions)
  • Is fully configurable via .pylintrc
  • Handles all annotation contexts (parameters, return types, variable annotations)

I'm happy to open a PR with the implementation if the maintainers are interested in this feature.

Additional context

Related checks in other linters

  • flake8-annotations-complexity: Similar plugin for flake8
  • mypy: Validates type correctness but not readability

In the issue tracker #3853 seems to be related but that check is related to require type checks, not checking their complexity

Metadata

Metadata

Assignees

No one assigned

    Labels

    Enhancement ✨Improvement to a componentNeeds specification 🔐Accepted as a potential improvement, and needs to specify edge cases, message names, etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions