Skip to content

fix(tests): settle genai client finalizers in the test that owns them - #6882

Open
helin-mktech wants to merge 2 commits into
livekit:mainfrom
helin-mktech:fix/genai-finalizer-leak
Open

fix(tests): settle genai client finalizers in the test that owns them#6882
helin-mktech wants to merge 2 commits into
livekit:mainfrom
helin-mktech:fix/genai-finalizer-leak

Conversation

@helin-mktech

Copy link
Copy Markdown

Fixes the flaky Test leaked tasks failure described in #6881.

AsyncClient.__del__ schedules aclose() on whatever event loop is running when the collector reaches it, even for a client the test already closed. Collected late, those tasks surface as leaked tasks in an unrelated test in a later module — usually tests/test_plugin_openai_stt_context.py, which never touches google.

This adds an autouse fixture to the one module that builds genai clients, so the finalizers run and finish while that test still owns the loop.

Verified on the CPython build CI uses (3.12.13), full unit suite:

unit-tests
before fails, every run
after passes, every run

Reverting the fixture brings the failure straight back.

🤖 Generated with Claude Code

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

AsyncClient.__del__ schedules aclose() on whatever event loop is running
when the collector reaches it, with no check for a client that was already
closed explicitly. Collected late, those tasks surface as leaked tasks in
an unrelated test in a later module.

Collect and drain them while the test that built the client still owns the
loop, so they cannot escape into someone else's leak check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@helin-mktech
helin-mktech force-pushed the fix/genai-finalizer-leak branch from 42c04a1 to db4c1d5 Compare August 17, 2026 06:45
@helin-mktech helin-mktech mentioned this pull request Aug 17, 2026
7 tasks
Comment thread tests/test_plugin_google_realtime.py Outdated
if pending := [
task
for task in asyncio.all_tasks()
if not task.done() and "aclose" in (getattr(task.get_coro(), "__qualname__", "") or "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe also check if it's from genai

and (getattr(task.get_coro(), "__module__", "") or "").startswith("google.genai")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — aclose on its own is too loose. Done, keyed on the coroutine's defining module now.

One correction on the mechanics: coroutine objects don't carry module at all, so getattr(coro, "module", "") is always '' and that check would never match — the fixture would have gone quietly inert.
coro.cr_frame.f_globals["name"] gives the module.

@helin-mktech
helin-mktech force-pushed the fix/genai-finalizer-leak branch from 8c6a874 to d321395 Compare August 17, 2026 09:00
`aclose` is a common method name, so matching on it alone could pull in an
unrelated pending task. Key on the coroutine's defining module instead.

Reading the module off the coroutine takes cr_frame.f_globals rather than
__module__, which coroutine objects do not carry at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@helin-mktech
helin-mktech force-pushed the fix/genai-finalizer-leak branch from d321395 to 26f55db Compare August 17, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants