nebius/SWE-bench-extra huggingface dataset:
Repository: dls-controls/aioca

Issue:
Possible memory leak
In __create_signal_task(), a task is appended to the list self.__tasks, but never removed from the list.
This list grows without bound.
The list of tasks is needed so that we can cancel them when closing. 
But do we need that for events?


Comments:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 6c5a2eb..2dd7afa 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,7 @@ Unreleased_
 Fixed:
 
 - `camonitor(all_updates=True) now works <../../pull/24>`_
+- `Fixed memory leak in camonitor <../../pull/26>`
 
 1.3_ - 2021-10-15
 -----------------
diff --git a/aioca/_catools.py b/aioca/_catools.py
index bc41e02..7892f9e 100644
--- a/aioca/_catools.py
+++ b/aioca/_catools.py
@@ -330,7 +330,8 @@ class Subscription(object):
 
     def __create_signal_task(self, value):
         task = asyncio.ensure_future(self.__signal(value))
-        self.__tasks.append(task)
+        self.__tasks.add(task)
+        task.add_done_callback(self.__tasks.remove)
 
     async def __signal(self, value):
         """Wrapper for performing callbacks safely: only performs the callback
@@ -428,13 +429,13 @@ class Subscription(object):
         # background, as we may have to wait for the channel to become
         # connected.
         self.state = self.OPENING
-        self.__tasks = [
+        self.__tasks = {
             asyncio.ensure_future(
                 self.__create_subscription(
                     events, datatype, format, count, connect_timeout
                 )
             )
-        ]
+        }
 
     async def __wait_for_channel(self, timeout):
         try:


Test patch:
diff --git a/tests/test_aioca.py b/tests/test_aioca.py
index 2a5fbb0..9710564 100644
--- a/tests/test_aioca.py
+++ b/tests/test_aioca.py
@@ -270,6 +270,9 @@ async def test_monitor(ioc: subprocess.Popen) -> None:
     ioc.communicate("exit")
 
     await asyncio.sleep(0.1)
+    # Check that all callback tasks have terminated, and all that is left
+    # is the original __create_subscription task (which has now completed)
+    assert len(m._Subscription__tasks) == 1  # type: ignore
     m.close()
 
     assert [42, 43, 44] == values[:3]