Skip to content

Calling del on a not fully closed transport can generate a warning #1426

@ffourcot

Description

@ffourcot

Hello,

We found one pyroute2 related warning in our test case. The simplest reproducer is this code:

from pyroute2 import IPRoute

async def test_main():
    ipr = IPRoute()
    print(ipr.get_addr())
    ipr.close()

When we call pytest with -X dev option, a warning is emitted:

$ python3 -X dev -m pytest reproducer.py 
========================================= test session starts ==========================================
platform linux -- Python 3.11.2, pytest-7.2.1, pluggy-1.0.0+repack
rootdir: /notrelevant, configfile: tox.ini
plugins: asyncio-0.20.3,anyio-3.6.2
asyncio: mode=Mode.AUTO
collected 1 item                                                                                       

[...]

==================================== 1 passed, 2 warnings in 0.73s =====================================
/usr/lib/python3.11/asyncio/selector_events.py:843: ResourceWarning: unclosed transport <_SelectorDatagramTransport closing fd=12>
  _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
ResourceWarning: Enable tracemalloc to get the object allocation traceback

The easiest fix (but maybe not cleanest...) is this patch:

--- a/pyroute2/netlink/core.py
+++ b/pyroute2/netlink/core.py
@@ -462,6 +462,7 @@ class AsyncCoreSocket:
             if hasattr(self.local, 'transport'):
                 self.local.transport.abort()
                 self.local.transport.close()
+                self.event_loop.run_until_complete(asyncio.sleep(0))
                 del self.local.transport
                 del self.local.protocol
             if self.status['event_loop'] == 'new':

From my understanding, you are calling a close() without giving a chance to the event loop to really close the socket. Giving a chance to the event loop to run is enough to really clean the status.

Since it's only when ResourceWarning is enabled, it's probably never seen in production. We managed it in our code base with our own await asyncio.sleep(0) in tests teardown, but it's perhaps something to have a look on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions