Skip to content

Annotated with forward references does not partially evaluate #137706

@DavidCEllis

Description

@DavidCEllis

Bug report

Bug description:

I ran into this while writing tests for #135228 with additional ways to unintentionally make the old class stick around.

MRE:

from typing import Annotated
from annotationlib import get_annotations, Format

def f(x: Annotated[undefined, '']): pass

annos = get_annotations(f, format=Format.FORWARDREF)
print(annos['x'])

Expected: typing.Annotated[ForwardRef('undefined', owner=...), '']
Actual: ForwardRef("Annotated[undefined, '']", owner=...)


The cause is this:

  • _Stringifier has a __getattr__ that will return a new _Stringifier
  • Annotated checks _is_unpacked_typevartuple to determine if it should fail
  • _is_unpacked_typevartuple checks getattr(x, "__typing_is_unpacked_typevartuple__", False)
  • This returns a new _Stringifier which is truthy
  • Annotated raises a TypeError as it thinks the _Stringifier is an unpacked typevartuple
  • call_annotate_function falls back to the all-stringifiers backup mode which makes the whole thing a ForwardRef

I think the solution for Annotated not partially evaluating is probably to make typing._is_unpacked_typevartuple check that the value is True rather than truthy.


One reason why this might matter - the exception that occurs internally can also prevent other annotations from partially evaluating:

from typing import Annotated
from dataclasses import InitVar, dataclass, fields
from pprint import pp

@dataclass
class PartiallyEvaluates:
    x: InitVar[undefined]

@dataclass
class NoLongerEvaluates:
    x: InitVar[undefined]
    y: Annotated[undefined, '']

The addition of the Annotated field makes it so x is no longer correctly identified as an InitVar here.

CPython versions tested on:

3.14, CPython main branch

Operating systems tested on:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtopic-typingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions