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
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:
Expected:
typing.Annotated[ForwardRef('undefined', owner=...), '']Actual:
ForwardRef("Annotated[undefined, '']", owner=...)The cause is this:
_Stringifierhas a__getattr__that will return a new_Stringifier_is_unpacked_typevartupleto determine if it should fail_is_unpacked_typevartuplechecksgetattr(x, "__typing_is_unpacked_typevartuple__", False)_Stringifierwhich is truthy_Stringifieris an unpacked typevartuplecall_annotate_functionfalls back to the all-stringifiers backup mode which makes the whole thing aForwardRefI think the solution for
Annotatednot partially evaluating is probably to maketyping._is_unpacked_typevartuplecheck that the value isTruerather than truthy.One reason why this might matter - the exception that occurs internally can also prevent other annotations from partially evaluating:
The addition of the
Annotatedfield makes it soxis 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
Trueinstead of truthy #137712Trueinstead of truthy (GH-137712) #138574