Skip to content

Commit d532923

Browse files
committed
test: check exc_value instead of args.object in leak hook
When unraisablehook fires from __del__, args.object is the __del__ function itself, not the stream instance. The previous check happened to match because Python's function repr includes the qualname, but the failure message showed '<function MemoryObjectSendStream.__del__ ...>' instead of the actual 'Unclosed <MemoryObjectSendStream at 0x...>' warning. Checking exc_value is semantically correct and gives actionable output when a regression reintroduces the leak.
1 parent 3332cfb commit d532923

File tree

1 file changed

+8
-6
lines changed

1 file changed

+8
-6
lines changed

tests/client/test_transport_stream_cleanup.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import sys
1616
from collections.abc import Iterator
1717
from contextlib import contextmanager
18-
from typing import Any
1918

2019
import httpx
2120
import pytest
@@ -34,22 +33,25 @@ def _assert_no_memory_stream_leak() -> Iterator[None]:
3433
same xdist worker). gc.collect() is forced after the block to make leaks
3534
deterministic.
3635
"""
37-
leaked: list[Any] = []
36+
leaked: list[str] = []
3837
old_hook = sys.unraisablehook
3938

40-
def hook(args: Any) -> None: # pragma: no cover
39+
def hook(args: "sys.UnraisableHookArgs") -> None: # pragma: no cover
4140
# Only executes if a leak occurs (i.e. the bug is present).
41+
# args.object is the __del__ function (not the stream instance) when
42+
# unraisablehook fires from a finalizer, so check exc_value — the
43+
# actual ResourceWarning("Unclosed <MemoryObjectSendStream at ...>").
4244
# Non-MemoryObject unraisables (e.g. PipeHandle leaked by an earlier
4345
# flaky test on the same xdist worker) are deliberately ignored —
4446
# this test should not fail for another test's resource leak.
45-
if "MemoryObject" in repr(args.object):
46-
leaked.append(args)
47+
if "MemoryObject" in str(args.exc_value):
48+
leaked.append(str(args.exc_value))
4749

4850
sys.unraisablehook = hook
4951
try:
5052
yield
5153
gc.collect()
52-
assert not leaked, f"Memory streams leaked: {[repr(x.object) for x in leaked]}"
54+
assert not leaked, f"Memory streams leaked: {leaked}"
5355
finally:
5456
sys.unraisablehook = old_hook
5557

0 commit comments

Comments
 (0)