Skip to content

annotationlib.get_annotations() infinite loop on __wrapped__ cycle (eval_str=True) #146556

Description

@raminfp

Bug report

Bug description:

annotationlib.get_annotations() hangs indefinitely when called with eval_str=True on a callable that has a circular __wrapped__ reference chain.

Reproducer

importannotationlibdeff(x: 'int') ->'str': passf.__wrapped__=f# self-referential cycle# Hangs forever, never returnsannotationlib.get_annotations(f, eval_str=True)

Two-node cycle also triggers it:

defg(): passf.__wrapped__=gg.__wrapped__=fannotationlib.get_annotations(f, eval_str=True) # hangs

Root Cause

In Lib/annotationlib.py, get_annotations() has an early-return guard at line 1010:

ifnoteval_str:
returndict(ann) # fast path, skips unwrap entirely for default eval_str=False

When eval_str=True the code falls through to a while True: loop (lines 1039–1048)
that unwraps __wrapped__ chains with no cycle detection:

ifunwrapisnotNone:
whileTrue:
ifhasattr(unwrap, "__wrapped__"):
unwrap=unwrap.__wrapped__# no cycle detectioncontinueiffunctools:=sys.modules.get("functools"):
ifisinstance(unwrap, functools.partial):
unwrap=unwrap.func# also no cycle detectioncontinuebreak

Fix

Apply the same cycle-detection pattern used by inspect.unwrap() (visited id-set):

ifunwrapisnotNone:
seen= {id(unwrap)}
whileTrue:
ifhasattr(unwrap, "__wrapped__"):
candidate=unwrap.__wrapped__ifid(candidate) inseen:
breakseen.add(id(candidate))
unwrap=candidatecontinueiffunctools:=sys.modules.get("functools"):
ifisinstance(unwrap, functools.partial):
candidate=unwrap.funcifid(candidate) inseen:
breakseen.add(id(candidate))
unwrap=candidatecontinuebreakifhasattr(unwrap, "__globals__"):
obj_globals=unwrap.__globals__

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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