Skip to content

[3.14] annotationlib - Union '|' syntax and typing.Union[...] generate different forward references. #132805

Closed
@DavidCEllis

Description

@DavidCEllis

Bug report

Bug description:

While looking into #125618 I ran into this case.

Using the union syntax gives a ForwardRef that can't be evaluated as the class that can be evaluated gets converted into its repr via ast.Constant. Using the typing.Union class however gives a proper union object where only the undefined value is a forwardref.

  • str | undefined -> ForwardRef("<class 'str'> | undefined")
  • Union[str, undefined] -> str | ForwardRef("undefined")

Example:

from annotationlib import get_annotations, Format
from typing import Union

class DifferentUnions:
    attrib: str | undefined
    other_attrib: Union[str, undefined]

different_unions = get_annotations(DifferentUnions, format=Format.FORWARDREF)

print(different_unions)

Formatted Output:

{
    'attrib': ForwardRef("<class 'str'> | undefined", is_class=True, owner=<class '__main__.DifferentUnions'>), 
    'other_attrib': str | ForwardRef('undefined', is_class=True, owner=<class '__main__.DifferentUnions'>)
}

One possible solution to this is to add a create_unions attribute to the _StringifierDict. If this is True then the __or__ and __ror__ methods should create types.UnionType instances instead of calling __make_new. This is False for Format.STRING in order to keep a | b in the reproduction in that case.

I already have a branch with this approach so I can make a PR.

Doing this will break the current test_nonexistent_attribute ForwardRef test as some | obj would evaluate to ForwardRef('some') | ForwardRef('obj') instead of ForwardRef('some | obj') but I think this is probably correct and the test should be changed.

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.14bugs and security fixesstdlibPython 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