Repository: ipython/ipython
Pull request: Fix memory leak in Qt event loop integration (#14240)
The QEventLoop object, `event_loop`, created in `IPython/terminal/pt_intputhooks/qt.py` L58 is not deleted when exiting the scope as passing `app` to the constructor parents the object to `app`. This creates a memory leak as QEventLoop objects are being accumulated.

The issue was originally reported by @pag who also suggested the fix.

`pytest IPython/terminal/tests/` runs through without errors. I have tested the changes and see no more accumulation of `QEventLoop` objects.

This fixes #14240


Problem statement:
Memory leak with %matplotlib qt, with simple fix
When using matplotlib event loop integration (e.g. %matplotlib) with pyqt, there is a memory leak caused by a misunderstanding of the lifespan of QObjects.

I found it because I'm trying to debug slowdowns in long-running IPython sessions with autoreload. I'm not sure that this is actually the cause, but it's certainly ugly. Within a session which has been open for a couple of weeks I found:
```
>>> collections.Counter(map(type, gc.get_objects())).most_common(5)
[(PyQt5.QtCore.QEventLoop, 1718061),
 (dict, 1006864),
 (list, 702602),
 (ast.Name, 267460),
 (ast.Attribute, 96929)]
```
There are 1.7m QEventLoop instances kicking around. They are being created by `IPython.terminal.pt_inputhooks.qt.inputhook` on line 58, with `event_loop = QtCore.QEventLoop(app)`. But QEventLoop is a QObject subclass, which means that passing `app` in the constructor parents the instance to `app`, so even when it exits the scope it won't be deleted, even though it's no longer needed by that point.

The fix is simple: just add a line at the end of that function containing `event_loop.setParent(None)`.

Hopefully it's faster for you to just add that single line of code rather than me forking, making a pull request etc, but please tell me if you need me to do so.

Hints from issue discussion:
> The fix is simple: just add a line at the end of that function containing event_loop.setParent(None).

Thanks for finding a fix @pag!

> Hopefully it's faster for you to just add that single line of code rather than me forking, making a pull request etc, but please tell me if you need me to do so.

Unfortunately, it's not because we're busy with other things. So, please submit a pull request to fix this issue.

Commit message: Fix memory leak in Qt event loop integration (#14240)

See: https://github.com/ipython/ipython/issues/14240

- the QEventLoop, `event_loop`, is not deleted when exiting the scope as
passing `app` to the constructor parents the object to `app`. This
creates a memory leak as QEventLoop objects are being accumulated.
- issue reported by and fix suggested by @pag

Patch to review:
diff --git a/IPython/terminal/pt_inputhooks/qt.py b/IPython/terminal/pt_inputhooks/qt.py
index cf6d11ea6ca..638ea2b0f81 100644
--- a/IPython/terminal/pt_inputhooks/qt.py
+++ b/IPython/terminal/pt_inputhooks/qt.py
@@ -84,3 +84,5 @@ def inputhook(context):
                 _exec(event_loop)
         finally:
             notifier.setEnabled(False)
+    # make sure that the QObject is being deleted
+    event_loop.setParent(None)


Code review comments:
diff_hunk: @@ -84,3 +84,5 @@ def inputhook(context):
                 _exec(event_loop)
         finally:
             notifier.setEnabled(False)
+    # make sure that the QObject is being deleted
path: IPython/terminal/pt_inputhooks/qt.py
text: @user1:
```suggestion

    # This makes sure that the event loop is garbage collected.
    # See issue 14240.
```