Skip to content

Commit f697e88

Browse files
committed
Another try at tracking down ResourceWarning with tracemalloc.
See the explanation; we don't want to use TRACEMALLOC=20 on the fulll test suite, and enable for a single test is a bit more complicated. See explanation in fixture itself.
1 parent 72d7ca8 commit f697e88

File tree

4 files changed

+39
-17
lines changed

4 files changed

+39
-17
lines changed

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ filterwarnings= [
173173
# Ignore jupyter_client warnings
174174
"module:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning",
175175

176+
# we do not want to raise on resources warnings, or we will not have a chance to
177+
# collect the messages and print the location of the leak
178+
"always::ResourceWarning",
179+
176180
# ignore unclosed sqlite in traits
177181
"ignore:unclosed database in <sqlite3.Connection:ResourceWarning",
178182

tests/conftest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
22
import logging
33
import os
4+
import tracemalloc
5+
import warnings
46
from math import inf
57
from typing import Any, Callable, no_type_check
68
from unittest.mock import MagicMock
@@ -207,3 +209,34 @@ async def ipkernel(anyio_backend):
207209
yield kernel
208210
kernel.destroy()
209211
ZMQInteractiveShell.clear_instance()
212+
213+
214+
@pytest.fixture()
215+
def tracemalloc_resource_warning(recwarn, N=10):
216+
"""fixture to enable tracemalloc for a single test, and report the
217+
location of the leaked resource
218+
219+
We cannot only enable tracemalloc, as otherwise it is stopped just after the test,
220+
the frame cache is clear by tracemalloc.stop() and thus the warning running code
221+
get None when doing `tracemalloc.get_object_traceback(r.source)`.
222+
223+
So we need to both filter the warnings to enable ResourceWarning,
224+
and loop through it print the stack before we stop tracemalloc and continue
225+
226+
"""
227+
228+
tracemalloc.start(N)
229+
with warnings.catch_warnings():
230+
warnings.simplefilter("always", category=ResourceWarning)
231+
yield None
232+
try:
233+
for r in recwarn:
234+
if r.category is ResourceWarning and r.source is not None:
235+
tb = tracemalloc.get_object_traceback(r.source)
236+
if tb:
237+
info = f"Leaking resource (-):{r}\n |" + "\n |".join(tb.format())
238+
# technically an Error and not a failure as we fail in the fixture
239+
# and not the test
240+
pytest.fail(info)
241+
finally:
242+
tracemalloc.stop()

tests/test_connect.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,6 @@
1919

2020
from .utils import TemporaryWorkingDirectory
2121

22-
23-
@pytest.fixture(scope="module", autouse=True)
24-
def _enable_tracemalloc():
25-
try:
26-
import tracemalloc
27-
except ModuleNotFoundError:
28-
# pypy
29-
tracemalloc = None
30-
if tracemalloc is not None:
31-
tracemalloc.start()
32-
yield
33-
if tracemalloc is not None:
34-
tracemalloc.stop()
35-
36-
3722
sample_info: dict = {
3823
"ip": "1.2.3.4",
3924
"transport": "ipc",
@@ -133,7 +118,7 @@ def test_port_bind_failure_recovery(request):
133118
app.init_sockets()
134119

135120

136-
def test_port_bind_failure_gives_up_retries(request):
121+
def test_port_bind_failure_gives_up_retries(request, tracemalloc_resource_warning):
137122
cfg = Config()
138123
with TemporaryWorkingDirectory() as d:
139124
cfg.ProfileDir.location = d

tests/test_ipkernel_direct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async def test_direct_execute_request_aborting(ipkernel):
5151
assert reply["content"]["status"] == "aborted"
5252

5353

54-
async def test_complete_request(ipkernel):
54+
async def test_complete_request(ipkernel, tracemalloc_resource_warning):
5555
reply = await ipkernel.test_shell_message("complete_request", dict(code="hello", cursor_pos=0))
5656
assert reply["header"]["msg_type"] == "complete_reply"
5757
ipkernel.use_experimental_completions = False

0 commit comments

Comments
 (0)