From 2c68a38e07ab50799f0fc03b4d3b60c759fe351e Mon Sep 17 00:00:00 2001 From: tadeasf Date: Sun, 26 Jul 2026 21:51:33 +0200 Subject: [PATCH 1/2] Fix update dialog crashing the app when the WebView backend is unusable On Linux (wxGTK), wx.html2.WebView is backed by WebKitGTK. In the AppImage the bundled Ubuntu build of WebKitGTK fails to spawn its helper processes on other distributions, which aborts the whole application a few seconds after startup (issue #2740). On distributions without webkit2gtk-4.0, even importing wx.html2 fails, which breaks startup from source as well. Never use the WebView on wxGTK and fall back to a plain text release notes widget there. On other platforms guard the WebView creation and fall back to plain text when no backend is available. Also guard ShowUpdateBox so a dialog failure can no longer take down startup, default empty release notes bodies to an empty string and put the release tag in the dialog title instead of a literal '{}'. Behavior on Windows and macOS is unchanged. --- gui/mainFrame.py | 12 +++- gui/updateDialog.py | 136 ++++++++++++++++++++++++++++++++------------ 2 files changed, 109 insertions(+), 39 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 44bfc5c496..6d15ef4695 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -242,8 +242,16 @@ def getCommandForFit(self, fitID) -> wx.CommandProcessor: return Fit.getCommandProcessor(fitID) def ShowUpdateBox(self, release, version): - with UpdateDialog(self, release, version) as dlg: - dlg.ShowModal() + # This runs right after startup, so any failure here must not be able + # to take the whole application down with it + try: + with UpdateDialog(self, release, version) as dlg: + dlg.ShowModal() + except (KeyboardInterrupt, SystemExit): + raise + except Exception as e: + pyfalog.error("Caught exception while showing the update notification dialog") + pyfalog.error(e) def LoadPreviousOpenFits(self): sFit = Fit.getInstance() diff --git a/gui/updateDialog.py b/gui/updateDialog.py index fc465c4632..18343cf988 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -21,12 +21,14 @@ import wx # noinspection PyPackageRequirements import dateutil.parser +from logbook import Logger from service.settings import UpdateSettings as svc_UpdateSettings -import wx.html2 import webbrowser import re import markdown2 +pyfalog = Logger(__name__) + _t = wx.GetTranslation # HTML template. We link to a bootstrap cdn for quick and easy css, and include some additional teaks. @@ -51,8 +53,8 @@ class UpdateDialog(wx.Dialog): def __init__(self, parent, release, version): super().__init__( - parent, id=wx.ID_ANY, title="pyfa {}" + _t("Update Available"), pos=wx.DefaultPosition, - size=wx.Size(550, 450), style=wx.DEFAULT_DIALOG_STYLE) + parent, id=wx.ID_ANY, title="pyfa {} - {}".format(release['tag_name'], _t("Update Available")), + pos=wx.DefaultPosition, size=wx.Size(550, 450), style=wx.DEFAULT_DIALOG_STYLE) self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release @@ -61,42 +63,22 @@ def __init__(self, parent, release, version): mainSizer = wx.BoxSizer(wx.VERTICAL) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) + releaseNotes = self.releaseInfo.get('body') or '' notesSizer = wx.BoxSizer(wx.HORIZONTAL) - self.browser = wx.html2.WebView.New(self) - self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) - - link_patterns = [ - (re.compile(r"#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), - (re.compile(r"@(\w+)", re.I), r"https://github.com/\1") - ] - - markdowner = markdown2.Markdown( - extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], - link_patterns=link_patterns) - - release_markup = markdowner.convert(self.releaseInfo['body']) - - # run the text through markup again, this time with the hashing pattern. This is required due to bugs in markdown2: - # https://github.com/trentm/python-markdown2/issues/287 - link_patterns = [ - (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), - ] - markdowner = markdown2.Markdown( - extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], - link_patterns=link_patterns) - - # The space here is required, again, due to bug. Again, see https://github.com/trentm/python-markdown2/issues/287 - release_markup = markdowner.convert(' ' + release_markup) - - self.browser.SetPage(html_tmpl.format( - self.releaseInfo['tag_name'], - releaseDate.strftime('%B %d, %Y'), - "

This is a pre-release, be prepared for unstable features

" if version.is_prerelease else "", - release_markup - ), "") - - notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + self.browser = self.createNotesBrowser(releaseDate, releaseNotes, version) + if self.browser is None: + header = "pyfa {} ({})".format(self.releaseInfo['tag_name'], releaseDate.strftime('%B %d, %Y')) + lines = [header, '=' * len(header), ''] + if version.is_prerelease: + lines += [_t("This is a pre-release, be prepared for unstable features"), ''] + lines.append(releaseNotes) + self.notesText = wx.TextCtrl( + self, wx.ID_ANY, '\n'.join(lines), wx.DefaultPosition, wx.DefaultSize, + wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_WORDWRAP) + notesSizer.Add(self.notesText, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + else: + notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) self.supressCheckbox = wx.CheckBox(self, wx.ID_ANY, _t("Don't remind me again for this release"), @@ -131,6 +113,86 @@ def __init__(self, parent, release, version): self.Centre(wx.BOTH) + @staticmethod + def webViewAvailable(): + """ + On wxGTK the wx.html2.WebView widget is backed by WebKitGTK, which cannot + be instantiated reliably on every Linux setup. In particular, the AppImage + bundles an Ubuntu build of WebKitGTK that fails to spawn its helper + processes on other distributions, aborting the whole application + (see https://github.com/pyfa-org/Pyfa/issues/2740). Never use the WebView + there; on other platforms make sure a backend can be loaded at all. + """ + if 'wxGTK' in wx.PlatformInfo: + return False + try: + from wx import html2 + return html2.WebView.IsBackendAvailable(html2.WebViewBackendDefault) + except (KeyboardInterrupt, SystemExit): + raise + except Exception as e: + pyfalog.warning("Could not check WebView backend availability: {}", e) + return False + + def createNotesBrowser(self, releaseDate, releaseNotes, version): + """ + Build the WebView displaying the release notes. Returns None when the + WebView cannot be used, so the caller can fall back to plain text. + """ + if not self.webViewAvailable(): + return None + + from wx import html2 + try: + browser = html2.WebView.New(self) + except (KeyboardInterrupt, SystemExit): + raise + except Exception as e: + pyfalog.warning("Could not create WebView for the update dialog: {}", e) + return None + + browser.Bind(html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) + + link_patterns = [ + (re.compile(r"#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), + (re.compile(r"@(\w+)", re.I), r"https://github.com/\1") + ] + + markdowner = markdown2.Markdown( + extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], + link_patterns=link_patterns) + + release_markup = markdowner.convert(releaseNotes) + + # run the text through markup again, this time with the hashing pattern. This is required due to bugs in markdown2: + # https://github.com/trentm/python-markdown2/issues/287 + link_patterns = [ + (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), + ] + + markdowner = markdown2.Markdown( + extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], + link_patterns=link_patterns) + + # The space here is required, again, due to bug. Again, see https://github.com/trentm/python-markdown2/issues/287 + release_markup = markdowner.convert(' ' + release_markup) + + try: + browser.SetPage(html_tmpl.format( + self.releaseInfo['tag_name'], + releaseDate.strftime('%B %d, %Y'), + "

This is a pre-release, be prepared for unstable features

" if version.is_prerelease else "", + release_markup + ), "") + except (KeyboardInterrupt, SystemExit): + raise + except Exception as e: + pyfalog.warning("Could not load release notes into WebView: {}", e) + browser.Destroy() + return None + + return browser + def OnClose(self, e): self.Close() From ce4bc21ee2ec0ce6a7e100903e7afc01bfda85a7 Mon Sep 17 00:00:00 2001 From: tadeasf Date: Sun, 26 Jul 2026 21:51:40 +0200 Subject: [PATCH 2/2] AppImage: drop bundled WebKitGTK The update dialog no longer uses wx.html2.WebView on Linux, so nothing in the AppImage loads libwebkit2gtk-4.0-37 anymore and it can be dropped from the bundle. --- dist_assets/linux/AppImageBuilder.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/dist_assets/linux/AppImageBuilder.yml b/dist_assets/linux/AppImageBuilder.yml index ccd0f7adae..5a5f647090 100644 --- a/dist_assets/linux/AppImageBuilder.yml +++ b/dist_assets/linux/AppImageBuilder.yml @@ -30,7 +30,6 @@ AppDir: # wx dependencies - libgtk-3-0 - librsvg2-common # GTK3 recommendation; without it, search in char editor crashes - - libwebkit2gtk-4.0-37 # Needed for wx's HTML lib # Unknown - libpcre2-32-0 # https://github.com/pyfa-org/Pyfa/issues/2572 - libnotify4 # https://github.com/pyfa-org/Pyfa/issues/2598