Skip to content

KI protection f_locals materialization results in reference cycles on 3.12 and below #3108

@graingert

Description

@graingert

consider:

import trio
import gc

class MyException(Exception):
    pass

async def demo():
    async def handle_error():
        try:
            raise MyException
        except MyException as e:
            exceptions.append(e)

    exceptions = []
    try:
        async with trio.open_nursery() as n:
            n.start_soon(handle_error)
        raise ExceptionGroup("errors", exceptions)
    finally:
        del exceptions

async def main():
    exc = None
    try:
        await demo()
    except* MyException as excs:
        exc = excs.exceptions[0]

    assert exc is not None
    print(gc.get_referrers(exc))
    exc.__traceback__.tb_frame.f_locals  # re-materialize f_locals to sync any deletions
    print(gc.get_referrers(exc))

trio.run(main)

this prints:

[[MyException()]]
[]

so there's a reference cycle from the exc.__traceback__.tb_frame.f_locals["exceptions"][0] is exc, which gets cleared when you re-materialize f_locals

this is caused by this materialization of the frame f_locals

coro.cr_frame.f_locals.setdefault(LOCALS_KEY_KI_PROTECTION_ENABLED, system_task)

See also agronholm/anyio#809

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions