Content-Length: 334147 | pFad | https://github.com/python/cpython/issues/137969

D0 annotationlib: `ref.evaluate(format=Format.FORWARDREF)` returns a ForwardRef with a copied `__globals__` that no longer updates · Issue #137969 · python/cpython · GitHub
Skip to content

annotationlib: ref.evaluate(format=Format.FORWARDREF) returns a ForwardRef with a copied __globals__ that no longer updates #137969

@DavidCEllis

Description

@DavidCEllis

Bug report

Bug description:

This can happen internally in get_annotations and means subsequent attempts to evaluate it will fail even if the names have since been defined.

Underlying logic:

from annotationlib import get_annotations, Format

class Demo:
    x: Sequence[undefined]

annos = get_annotations(Demo, format=Format.FORWARDREF)

x_anno = annos['x']

# Try to evaluate the reference, but just give a forwardref if it fails
x_repeat_anno = x_anno.evaluate(format=Format.FORWARDREF)

# The resulting reference no longer shares the globals namespace with the annotate function
print(f"{x_anno.__globals__ is Demo.__annotate__.__globals__ = }")  # True
print(f"{x_repeat_anno.__globals__ is Demo.__annotate__.__globals__ = }")  # False

# Define the previously undefined attributes
from collections.abc import Sequence
undefined = str

# This means evaluation fails in the second case
print(f"{x_anno.evaluate() = }")  # collections.abc.Sequence[str]
print(f"{x_repeat_anno.evaluate() = }")  # NameError

This evaluate call happens internally if get_annotations has to rely on the fallback behaviour for an unexpected exception, such as an AttributeError:

from annotationlib import get_annotations, Format
import typing

class Works:
    a: Sequence[undefined]
    b: unknowable

# Intentionally set up an annotation that will raise AttributeError on evaluation
class Fails:
    a: Sequence[undefined]
    b: typing.doesnotexist

a_works = get_annotations(Works, format=Format.FORWARDREF)['a']
a_fails = get_annotations(Fails, format=Format.FORWARDREF)['a']

# Realise the references
from collections.abc import Sequence
undefined = str

print(f"{a_works.evaluate() = }")  # collections.abc.Sequence[str]
print(f"{a_fails.evaluate() = }")  # NameError

This appears to be caused by the creation of a new globals dict here:

if type_params is not None:
globals = dict(globals)
for param in type_params:
globals[param.__name__] = param

Commenting this out makes these examples succeed, but obviously breaks type parameters.

CPython versions tested on:

CPython main branch, 3.14

Operating systems tested on:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-typingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions









      ApplySandwichStrip

      pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


      --- a PPN by Garber Painting Akron. With Image Size Reduction included!

      Fetched URL: https://github.com/python/cpython/issues/137969

      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy