-
Notifications
You must be signed in to change notification settings - Fork 89
Description
Bug Description
Using the following code (configure_traits_crash.py):
#! /usr/bin/env python
# Minimal test case for demonstrating problem w/ dynamically
# added Traits in modal call of `configure_traits()`.
#
# David Banas <capn.freako@gmail.com>
# March 4, 2025
from traits.api import HasTraits, Int
class Foo(HasTraits):
Bar = Int(5)
def __init__(self):
super().__init__()
self.add_trait("Blatz", Int(10))
if __name__ == "__main__":
foo = Foo()
# foo.configure_traits() # This works.
foo.configure_traits(kind="modal") # This crashes.I'm finding that the second call to configure_traits() crashes:
Traceback (most recent call last):
File "C:\Users\dbanas\tmp\configure_traits_crash.py", line 21, in <module>
foo.configure_traits(kind="modal")
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traits\has_traits.py", line 2099, in configure_traits
rc = toolkit().view_application(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\toolkit.py", line 237, in view_application
return view_application.view_application(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\view_application.py", line 92, in view_application
return ViewApplication(
^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\view_application.py", line 127, in __init__
self.ui = self.view.ui(
^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\view.py", line 457, in ui
ui.ui(parent, kind)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\ui.py", line 234, in ui
self.rebuild(self, parent)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\toolkit.py", line 176, in ui_modal
ui_modal.ui_modal(ui, parent)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_modal.py", line 49, in ui_modal
_ui_dialog(ui, parent, BaseDialog.MODAL)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_modal.py", line 67, in _ui_dialog
BaseDialog.display_ui(ui, parent, style)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_base.py", line 294, in display_ui
ui.owner.init(ui, parent, style)
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_modal.py", line 183, in init
self.add_contents(panel(ui), bbox)
^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_panel.py", line 270, in panel
panel = _GroupPanel(content[0], ui).control
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_panel.py", line 621, in __init__
layout = self._add_items(content, inner)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\qt\ui_panel.py", line 855, in _add_items
editor = factory_method(
^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\editor_factory.py", line 117, in simple_editor
return self.simple_editor_class(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\dbanas\.venv\pybert-dev\Lib\site-packages\traitsui\editor.py", line 540, in __init__
self.old_value = getattr(self.object, self.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Foo' object has no attribute 'Blatz'It's as if a "normal" call to configure_traits() can handle dynamically added Traits, but a modal call cannot.
Why?
My environment:
- OS: Windows 10
$ python --version
Python 3.12.4
$ pip show Traits
Name: traits
Version: 7.0.2
{snip}
$ pip show TraitsUI
Name: traitsui
Version: 8.0.0
{snip}Preliminary Analysis
Inspection of the two backtraces above reveals two things:
-
the modal call is missing the
Blatztrait, and -
the flows diverge in the call of
rebuild()attraitsui/ui.py:234and remerge 3 steps later in the call ofBaseDialog.display_ui()attraitsui/qt/ui_base.py:291, only to immediately diverge again in the next step, before reunifying for good in the following step:(Modal) /- ui_modal() - _ui_dialog() {qt/ui_modal.py:49} -\ /- init() {qt/ui_modal.py:73} -\ rebuild() - - display_ui() - panel() -> (Live) \- ui_live() - _ui_dialog() {qt/ui_live.py:52} -/ \- init() {qt/ui_live.py:78} -/
Inspecting the divergent blocks reveals that the first to make any changes to the passed in parameters is: init().
Aha! Adding the following debugging code to traitsui/qt/ui_modal.py:
(pybert-dev)
dbanas@Dell-XPS-15 MINGW64 ~/prj/traitsui (main)
$ git diff traitsui/qt/ui_modal.py
diff --git a/traitsui/qt/ui_modal.py b/traitsui/qt/ui_modal.py
index 7aad6c29..f7ac0e11 100644
--- a/traitsui/qt/ui_modal.py
+++ b/traitsui/qt/ui_modal.py
@@ -72,6 +72,7 @@ class _ModalDialog(BaseDialog):
def init(self, ui, parent, style):
"""Initialise the object."""
+ print(f"traitsui.qt.ui_modal._ModalDialog.init(): `ui.context['object'].trait_names()` on entry:\n{ui.context['object'].trait_names()}")
self.ui = ui
self.control = ui.control
view = ui.view
@@ -180,6 +181,7 @@ class _ModalDialog(BaseDialog):
else:
bbox = None
+ print(f"traitsui.qt.ui_modal._ModalDialog.init(): `ui.context['object'].trait_names()` on exit:\n{ui.context['object'].trait_names()}")
self.add_contents(panel(ui), bbox)
def close(self, rc=True):yields:
(pybert-dev)
dbanas@Dell-XPS-15 MINGW64 ~/tmp
$ ./configure_traits_crash.py
traitsui.qt.ui_modal._ModalDialog.init(): `ui.context['object'].trait_names()` on entry:
['Bar', 'trait_added', 'trait_modified', 'Blatz']
traitsui.qt.ui_modal._ModalDialog.init(): `ui.context['object'].trait_names()` on exit:
['Bar', 'trait_added', 'trait_modified']showing that the Blatz trait is getting lost somewhere in traitsui.qt.ui_modal._ModalDialog.init().