From d796b1ec295b9c2030510e83e3c1a9db4f9192fd Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sat, 20 Apr 2024 17:35:40 +0200 Subject: [PATCH 1/2] PICARD-2869: Register a global exception handler Register a handler on sys.excepthook and have it call the global crash_handler(). This allows to show the exception dialog in otherwise unhandled exceptions. --- picard/__init__.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/picard/__init__.py b/picard/__init__.py index fe5c24aa40..ab84328801 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -3,7 +3,7 @@ # Picard, the next-generation MusicBrainz tagger # # Copyright (C) 2006-2008, 2011-2014 Lukáš Lalinský -# Copyright (C) 2009, 2018-2023 Philipp Wolfer +# Copyright (C) 2009, 2018-2024 Philipp Wolfer # Copyright (C) 2012 Chad Wilson # Copyright (C) 2012-2013 Michael Wiencek # Copyright (C) 2013-2024 Laurent Monin @@ -31,6 +31,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +import sys from picard.version import Version @@ -67,14 +68,12 @@ api_versions_tuple = [Version.from_string(v) for v in api_versions] -def crash_handler(): +def crash_handler(exc: Exception = None): """Implements minimal handling of an exception crashing the application. This function tries to log the exception to a log file and display a minimal crash dialog to the user. This function is supposed to be called from inside an except blog. """ - import sys - # Allow disabling the graphical crash handler for debugging and CI purposes. if set(sys.argv) & {'--no-crash-dialog', '-v', '--version', '-V', '--long-version', '-h', '--help'}: return @@ -83,7 +82,11 @@ def crash_handler(): # with minimum chance to fail. from tempfile import NamedTemporaryFile import traceback - trace = traceback.format_exc() + if exc: + # Since Python 3.10 calling only traceback.format_exception(exc) is possible + trace = "\n".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) + else: + trace = traceback.format_exc() logfile = None try: with NamedTemporaryFile(suffix='.log', prefix='picard-crash-', delete=False) as f: @@ -124,3 +127,12 @@ def crash_handler(): msgbox.setDefaultButton(QMessageBox.StandardButton.Close) msgbox.exec() app.quit() + + +def _global_exception_handler(exctype, value, traceback): + from picard import crash_handler + crash_handler(exc=value) + sys.__excepthook__(exctype, value, traceback) + + +sys.excepthook = _global_exception_handler From b4a6d492f74a3cd073828e92ab13d4c83855f869 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sun, 21 Apr 2024 18:26:53 +0200 Subject: [PATCH 2/2] PICARD-2869: Use newer syntax for getting exception info on Python >= 3.10 --- picard/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/picard/__init__.py b/picard/__init__.py index ab84328801..e60db67457 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -83,8 +83,11 @@ def crash_handler(exc: Exception = None): from tempfile import NamedTemporaryFile import traceback if exc: - # Since Python 3.10 calling only traceback.format_exception(exc) is possible - trace = "\n".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) + if sys.version_info < (3, 10): + trace_list = traceback.format_exception(None, exc, exc.__traceback__) + else: + trace_list = traceback.format_exception(exc) # pylint: disable=no-value-for-parameter + trace = "".join(trace_list) else: trace = traceback.format_exc() logfile = None