Fix AttributeError on iconlist when GalleryTab parent dialog closes (bug 13326)#2330
Conversation
|
Is it also related to bug report #12289? |
|
Thanks for the pointer to 0012289 — it's relevant to understand the story behind this one. The key distinction is severity and path. 12289 was a cosmetic GObject warning (instance ... has no handler with id ...) on the normal Person Editor close. 13326 is different in kind: an unhandled AttributeError crash on a specific sequence — add an image to the gallery, then Cancel the dialog — where a selection-changed callback fires after iconlist has been torn down. This resolves the crash without re-touching the part of the cleanup behavior that the earlier reports were wary about. Happy to adjust the approach if there's a side effect I've missed — the guard is specifically there to respect the original concern, |
|
Do not need to worry too much, I often make relations, this does not mean something should be improved. I am fine with this PR. As far as I remember the root of 12289 was pointed during a tracking down session around memory leaks. So, even cosmetic GalleryTab or some gramplets, generated some minor memory issues. Now, with the help of AI, we can just provide something more "professional[1]", quick and maybe more efficient. That's all. |
06add58 to
2f48559
Compare
GalleryTab.clean_up() did not disconnect the iconlist 'selection-changed' signal handler before super().clean_up() deleted the iconlist attribute via track_ref_for_deletion. A late selection-changed emission - which fires when the parent dialog tears down on Cancel, e.g. when the Forms addon hosts a GalleryTab - then re-entered _selection_changed -> get_selected() -> self.iconlist.get_selected_items() and crashed with AttributeError: 'GalleryTab' object has no attribute 'iconlist'. The original 2012 disconnect (r20849) was removed in 2023 to silence a GTK warning when the iconlist had already been disposed. Restore the disconnect, guarded by GObject.signal_handler_is_connected so it stays silent in the dispose-first case. Fixes #13326.
The disconnect restored for bug 13326 is gated by GObject.signal_handler_is_connected so that the case commit 37395da was working around (iconlist already disposed by GTK before clean_up) stays silent. Add a second test case that destroys the iconlist widget before clean_up and asserts no Python warning of the shape "instance '...' has no handler with id '...'" is recorded. Pre-fix the unguarded disconnect emits exactly that warning; post-fix the test passes. Issue #13326.
gramps-ci.yml runs the unit-test suite without Xvfb and exports GDK_BACKEND=-. Constructing a Gtk.IconView against a NULL screen "succeeds" but segfaults when those orphan widgets are garbage collected at process exit, taking the whole suite down with exit code 139. Detect the no-display case via DISPLAY / GDK_BACKEND / Gtk.init_check at module import and skip the entire TestCase class cleanly. The two regression tests still run under xvfb-run or on a real desktop. Issue #13326.
2f48559 to
cba8c69
Compare
|
Added 2 files to We probably should run our CI unit test suite using Xvfb, but that can be done in a separate PR. |
bc41df6
into
gramps-project:maintenance/gramps61
Root cause
GalleryTab.clean_up()callssuper().clean_up(), which removesself.iconlistviatrack_ref_for_deletion("iconlist"), but neverdisconnects the
'selection-changed'signal handler on the iconlistwidget. When the parent dialog tears down on Cancel — the Forms
addon's GalleryTab-in-a-dialog path exercises this reliably — GTK
dispatches a final
selection-changedto the still-live widget; thehandler calls
self.get_selected()→self.iconlist.get_selected_items()and raises
AttributeError: 'GalleryTab' object has no attribute 'iconlist'.Fix
Capture the
'selection-changed'handler id inbuild_interface,then disconnect it at the head of
clean_up()— beforesuper().clean_up()removes the attribute. Guard the disconnectwith
GObject.signal_handler_is_connectedso the already-disposedcase (the reason the original disconnect was removed in 37395da)
stays silent.
Verified against
gramps/gui/editors/displaytabs/gallerytab.py:232,659-660— theunguarded
connectand the no-opclean_upthat the reportedtrace fingers.
gramps/gui/editors/displaytabs/buttontab.py:319— the call sitein the inherited
_selection_changedcallback that reaches intoself.get_selected().51a53ccebd) — same defectclass, same fix shape; removed in
37395da262to silence a GTKwarning on the normal close path. The new
signal_handler_is_connectedguard keeps that path silent whilerestoring teardown-race safety.
Test
gramps/gui/editors/displaytabs/test/gallerytab_test.pycovers boththe bug and the warning the original disconnect was removed to
silence:
test_clean_up_disconnects_selection_changedconstructs a realGalleryTab, records the'selection-changed'signal id, andasserts via
GObject.signal_handler_findthat no handler remainsafter
clean_up(). Pre-fix42 != 0; post-fix passes.test_clean_up_after_iconlist_destroyed_emits_no_warningdestroysthe iconlist widget before
clean_up()— the normal close pathwhere commit 37395da was seeing the GTK critical — and asserts
via
warnings.catch_warningsthat noinstance '...' has no handler with id '...'warning is recorded. Mutating the fix todrop the
signal_handler_is_connectedguard surfaces exactly thatwarning, so future removals of the guard regress here.
Both need a display (
Gtk.IconView), so run underxvfb-run:Fixes #13326.