Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Another try at tracking down ResourceWarning with tracemalloc. #1353

Merged
merged 2 commits into from
Feb 22, 2025

Conversation

Carreau
Copy link
Member

@Carreau Carreau commented Feb 21, 2025

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.

@Carreau Carreau requested a review from minrk February 21, 2025 15:20
@Carreau Carreau force-pushed the tramal branch 2 times, most recently from f335b47 to f697e88 Compare February 21, 2025 15:28
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.
@Carreau
Copy link
Member Author

Carreau commented Feb 21, 2025

tested locally with

def test_leak(tracemalloc_resource_warning):
    with zmq.Context() as c:
        c.socket(zmq.PUB)


def test_no_leak(tracemalloc_resource_warning):
    with zmq.Context() as c:
        with c.socket(zmq.PUB):
            pass

foo.py::test_leak PASSED                                                                                                                                                          [ 50%]
foo.py::test_leak ERROR                                                                                                                                                           [ 50%]
foo.py::test_no_leak PASSED                                                                                                                                                       [100%]

======================================================================================== ERRORS =========================================================================================
____________________________________________________________________________ ERROR at teardown of test_leak _____________________________________________________________________________

recwarn = WarningsRecorder(record=True), N = 10

    @pytest.fixture()
    def tracemalloc_resource_warning(recwarn, N=10):
        """fixture to enable tracemalloc for a single test, and report the
        location of the leaked resource

        We cannot only enable tracemalloc, as otherwise it is stopped just after the
        test, the frame cache is cleared by tracemalloc.stop() and  thus the warning
        printing code get None when doing
        `tracemalloc.get_object_traceback(r.source)`.

        So we need to both filter the warnings to enable ResourceWarning,
        and loop through it print the stack before we stop tracemalloc and continue

        """

        tracemalloc.start(N)
        with warnings.catch_warnings():
            warnings.simplefilter("always", category=ResourceWarning)
            yield None
        try:
            for r in recwarn:
                if r.category is ResourceWarning and r.source is not None:
                    tb = tracemalloc.get_object_traceback(r.source)
                    if tb:
                        info = f"Leaking resource (-):{r}\n |" + "\n |".join(tb.format())
                        # technically an Error and not a failure as we fail in the fixture
                        # and not the test
>                       pytest.fail(info)
E                       Failed: Leaking resource (-):{message : ResourceWarning('Unclosed socket <zmq.Socket(zmq.PUB) at 0x108270d00>'), category : 'ResourceWarning', filename : '/Users/bussonniermatthias/dev/ipykernel/foo.py', lineno : 55, line : None}
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_hooks.py", line 513
E                        |    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_manager.py", line 120
E                        |    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_callers.py", line 103
E                        |    res = hook_impl.function(*args)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/_pytest/runner.py", line 174
E                        |    item.runtest()
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/_pytest/python.py", line 1627
E                        |    self.ihook.pytest_pyfunc_call(pyfuncitem=self)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_hooks.py", line 513
E                        |    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_manager.py", line 120
E                        |    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_callers.py", line 103
E                        |    res = hook_impl.function(*args)
E                        |  File "/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/_pytest/python.py", line 159
E                        |    result = testfunction(**testargs)
E                        |  File "/Users/bussonniermatthias/dev/ipykernel/foo.py", line 55
E                        |    c.socket(zmq.PUB)

N          = 10
info       = 'Leaking resource (-):{message : ResourceWarning(\'Unclosed socket <zmq.Socket(zmq.PUB) at 0x108270d00>\'), category :... testfunction(**testargs)\n |  File "/Users/bussonniermatthias/dev/ipykernel/foo.py", line 55\n |    c.socket(zmq.PUB)'
r          = <warnings.WarningMessage object at 0x10825f890>
recwarn    = WarningsRecorder(record=True)
tb         = <Traceback (<Frame filename='/Users/bussonniermatthias/miniconda3/envs/arm64/lib/python3.12/site-packages/pluggy/_hook...-packages/_pytest/python.py' lineno=159>, <Frame filename='/Users/bussonniermatthias/dev/ipykernel/foo.py' lineno=55>)>

@Carreau Carreau merged commit 90133b7 into ipython:main Feb 22, 2025
30 checks passed
@Carreau Carreau added this to the 7.0 milestone Feb 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant