enthought / enthought/traitsui

Reference leak from simple editor

Open
#858 5 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
Python
Stars
306
Forks
99
PR merge metrics
No merged PRs in 30d

Description

[Derived from enthought/traits#1164]

Meta: I'm not sure whether this issue belongs in TraitsUI or Pyface, but the relevant code appears to be in Pyface.

Opening and closing a simple TraitsUI editor leaks Python references (but not gc-tracked Python objects).

Here's a script that can be used to reproduce. It needs a debug build of Python, for the `sys.gettotalrefcount` function. On my machine, under Python 3.8, with a `pip`-installed Pyface, PyQt5 and other dependencies, it shows a leak of 30 references per iteration, but the total number of gc-tracked objects stays stable. However, amongst the tracked objects I can only see a reference count increase of 8 per iteration, suggesting that there _are_ untracked objects being leaked.

I'd say this is likely related to PyQt5, except that the script also behaves badly on PySide2. This may be a sign that there's some cleanup somewhere that we should be doing, but aren't (for example, making sure that signals that are connected are always eventually disconnected).

```python
import gc
import sys

from traits.api import HasTraits, Int

from pyface.qt.QtGui import QApplication
from pyface.ui.qt4.gui import GUI
from pyface.ui.qt4.util.event_loop_helper import EventLoopHelper

class EnumCollectionExample(HasTraits):
letters = Int()

def create_and_destroy_editor():
qt_app = QApplication.instance()
if qt_app is None:
qt_app = QApplication([])

gui = GUI()
event_loop_helper = EventLoopHelper(qt_app=qt_app, gui=gui)

# Create and destroy a UI window
obj = EnumCollectionExample()
ui = obj.edit_traits()
try:
gui.process_events()
finally:
with event_loop_helper.delete_widget(ui.control, timeout=10.0):
ui.dispose()

event_loop_helper.event_loop_with_timeout(repeat=5, timeout=10.0)
qt_app.flush()

def main():
for _ in range(20):
create_and_destroy_editor()
while gc.collect():
pass
print("Total refcount: ", sys.gettotalrefcount())

if __name__ == "__main__":
main()
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.