GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — annotationlib: `ref.evaluate(format=Format.FORWARDREF)` returns a ForwardRef with a copied `__globals__` that no longer updates · Issue #137969 · python/cpython

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

New issue
Copy link
New issue
Copy link
Closed
Closed
annotationlib: ref.evaluate(format=Format.FORWARDREF) returns a ForwardRef with a copied __globals__ that no longer updates#137969
Copy link
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@DavidCEllis
DavidCEllis
opened on Aug 19, 2025
Issue body actions

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:

cpython/Lib/annotationlib.py

Lines 164 to 167 in e39255e

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

  • gh-137969: Fix evaluation of ref.evaluate(format=Format.FORWARDREF) objects #138075
  • [3.14] gh-137969: Fix evaluation of ref.evaluate(format=Format.FORWARDREF) objects (GH-138075) #140929
  • Revert "gh-137969: Fix evaluation of ref.evaluate(format=Format.FORWARDREF) objects (#138075)" #140930
  • [3.14] Revert "gh-137969: Fix evaluation of ref.evaluate(format=Format.FORWARDREF) objects (GH-138075) (#140929)" #140931
  • gh-137969: Fix double evaluation of ForwardRefs which rely on globals #140974
  • [3.14] gh-137969: Fix double evaluation of ForwardRefs which rely on globals (GH-140974) #141527

Activity

  1. DavidCEllis
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Aug 19, 2025
  2. brianschubert
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    topic-typing
    on Aug 19, 2025
  3. dr-carlos commented on Aug 22, 2025

    @dr-carlos
    dr-carlos
    on Aug 22, 2025
    Contributor
    More actions

    A simple (possibly naive?) fix for this is just to add a new dictionary to store type param values instead of copying the globals.
    Then, a check against this dict is added where globals is accessed later in evaluate(), logically between the locals and globals.

    See dr-carlos@f8ac2c0

    This seemingly fixes the provided example and works with type parameters (as far as my simple tests have shown).
    Happy to open a PR, just wanting to make sure I haven't missed the problem (or created a new one).

  4. DavidCEllis commented on Aug 22, 2025

    @DavidCEllis
    DavidCEllis
    on Aug 22, 2025
    ContributorAuthor
    More actions

    Make sure you run the annotationlib tests with:

    ./python -m test -v test_annotationlib

    It looks like your commit breaks the tests as you shadow the types module with your new variable which breaks the check a few lines up for isinstance(owner, types.ModuleType).

    I thought I'd tried putting the type_params at the start of the locals and had test failures I didn't have time to investigate, but I think I had some logic in the wrong order.

    Reacted by dr-carlos
  5. dr-carlos commented on Aug 22, 2025

    @dr-carlos
    dr-carlos
    on Aug 22, 2025
    Contributor
    More actions

    Thanks for the heads up re the test command!

    I've renamed the variable to something much more sensible (forgot I was going to do that before posting earlier 🤦 )
    All tests are now passing. A new test would possibly be warranted for this use case, but that should probably be discussed in some future PR.

  6. JelleZijlstra commented on Aug 22, 2025

    @JelleZijlstra
    JelleZijlstra
    on Aug 22, 2025
    Member
    More actions

    That sounds reasonable, please send a PR and I'll take a look.

    Reacted by dr-carlos
  7. JelleZijlstra
    added a commit that references this issue on Nov 3, 2025

    gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWARDREF)`…

    63e01d6
  8. miss-islington
    added a commit that references this issue on Nov 3, 2025

    pythongh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWAR…

    e9aab5e
  9. JelleZijlstra
    added 4 commits that reference this issue on Nov 3, 2025

    [3.14] gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWA…

    cdb6fe8

    Revert "pythongh-137969: Fix evaluation of `ref.evaluate(format=Forma…

    b6820fc

    Revert "[3.14] pythongh-137969: Fix evaluation of `ref.evaluate(forma…

    d7608f0

    Revert "gh-137969: Fix evaluation of `ref.evaluate(format=Format.FORW…

    349de57
  10. encukou
    added a commit that references this issue on Nov 3, 2025

    [3.14] Revert "gh-137969: Fix evaluation of `ref.evaluate(format=Form…

    1b376b8
  11. JelleZijlstra commented on Nov 3, 2025

    @JelleZijlstra
    JelleZijlstra
    on Nov 3, 2025
    Member
    More actions

    @dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?

    Reacted by dr-carlos
  12. dr-carlos commented on Nov 3, 2025

    @dr-carlos
    dr-carlos
    on Nov 3, 2025
    Contributor
    More actions

    @dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?

    Yep, I'll take a look!

    Reacted by Jelle Zijlstra
  13. dr-carlos commented on Nov 4, 2025

    @dr-carlos
    dr-carlos
    on Nov 4, 2025
    Contributor
    More actions

    @dr-carlos your fix had to be reverted because of failures on some buildbots. Are you able to make another PR and try to fix the issues?

    It seems that the issue was with the test:

    • test_re_evaluate_generics tests globals, so uses a global declaration, but this shadows a name used in other test functions' scopes.
    • Renaming this variable to something unique fixed the other tests breaking
    • For some reason, if you run the tests with the same command as the buildbots use, it seems that the global scope is shared across test runs? Anyway, if you just check whether the global is still set and then delete it, it fixes the issue.

    Anyway, I've made a PR which re-implements the fix now that #138164 is implemented and includse a working test.
    It's a slightly different approach to last time, but I haven't found any issues with it so far, so let me know if you'd prefer it implemented with a separate dict instead of injecting into locals.

  14. JelleZijlstra commented on Nov 4, 2025

    @JelleZijlstra
    JelleZijlstra
    on Nov 4, 2025
    Member
    More actions

    Thanks, I think the difference is that buildbots run the same test twice in the same process. Should be able to repro with something like ./python -m test test_annotationlib test_annotationlib.

  15. JelleZijlstra
    added a commit that references this issue on Nov 13, 2025

    gh-137969: Fix double evaluation of `ForwardRef`s which rely on globa…

    209eaff
  16. miss-islington
    added a commit that references this issue on Nov 13, 2025

    pythongh-137969: Fix double evaluation of `ForwardRef`s which rely on…

    2d7525c
  17. CuriousLearner
    added a commit that references this issue on Nov 13, 2025

    Merge branch 'main' into fix-issue21914

    b9f473c
  18. JelleZijlstra
    added a commit that references this issue on Nov 13, 2025

    [3.14] gh-137969: Fix double evaluation of `ForwardRef`s which rely o…

    dd28db6
  19. CuriousLearner
    added 2 commits that reference this issue on Nov 14, 2025

    Merge branch 'main' of github.com:python/cpython into fix-Issue18387

    c46ecc5

    Merge branch 'main' of github.com:python/cpython into fix-issue24255

    c240289
  20. StanFromIreland
    added 3 commits that reference this issue on Dec 6, 2025

    pythongh-137969: Fix evaluation of `ref.evaluate(format=Format.FORWAR…

    d549af7

    Revert "pythongh-137969: Fix evaluation of `ref.evaluate(format=Forma…

    27195bf

    pythongh-137969: Fix double evaluation of `ForwardRef`s which rely on…

    f84043b
  21. DavidCEllis
    closed this as completedon Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.