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:
_Stringifierhas a__getattr__that will return a new_Stringifier- Annotated checks
_is_unpacked_typevartupleto determine if it should fail _is_unpacked_typevartuplechecksgetattr(x, "__typing_is_unpacked_typevartuple__", False)- This returns a new
_Stringifierwhich is truthy - Annotated raises a TypeError as it thinks the
_Stringifieris an unpacked typevartuple call_annotate_functionfalls back to the all-stringifiers backup mode which makes the whole thing aForwardRef
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