Improve exception handling (#3290)

* fix: except only non-system-exiting exceptions

see https://youtu.be/zrVfY9SuO64

* chore: add myself to CONTRIBUTORS file

* refactor: explicitly specify possible exceptions

If an exception is not an Exception, there are only three options left.
see https://docs.python.org/3/library/exceptions.html#exception-hierarchy

* refactor: use BaseException for fallback

Co-authored-by: Damien Elmes <dae@users.noreply.github.com>

* chore: add myself to contributors
This commit is contained in:
David Culley 2024-08-04 15:51:13 +02:00 committed by GitHub
parent a5a39c9302
commit c0349ea9da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 39 additions and 38 deletions

View file

@ -35,7 +35,7 @@ def runHook(hook: str, *args: Any) -> None:
for func in hookFuncs:
try:
func(*args)
except:
except Exception:
hookFuncs.remove(func)
raise
@ -46,7 +46,7 @@ def runFilter(hook: str, arg: Any, *args: Any) -> Any:
for func in hookFuncs:
try:
arg = func(arg, *args)
except:
except Exception:
hookFuncs.remove(func)
raise
return arg

View file

@ -99,15 +99,15 @@ class TextImporter(NoteImporter):
if not self.delimiter:
try:
self.dialect = sniffer.sniff("\n".join(self.data[:10]), self.patterns)
except:
except Exception:
try:
self.dialect = sniffer.sniff(self.data[0], self.patterns)
except:
except Exception:
pass
if self.dialect:
try:
reader = csv.reader(self.data, self.dialect, doublequote=True)
except:
except Exception:
err()
else:
if not self.delimiter:
@ -126,7 +126,7 @@ class TextImporter(NoteImporter):
if row:
self.numFields = len(row)
break
except:
except Exception:
err()
self.initMapping()

View file

@ -199,7 +199,7 @@ def get_def_lang(user_lang: str | None = None) -> tuple[int, str]:
# this will return a different format on Windows (e.g. Italian_Italy), resulting in us falling back to en_US
# further below
(sys_lang, enc) = locale.getlocale()
except:
except Exception:
# fails on osx
sys_lang = "en_US"
if user_lang in compatMap:

View file

@ -170,7 +170,7 @@ def _err_msg(col: anki.collection.Collection, type: str, texpath: str) -> str:
if not log:
raise Exception()
msg += f"<small><pre>{html.escape(log)}</pre></small>"
except:
except Exception:
msg += col.tr.media_have_you_installed_latex_and_dvipngdvisvgm()
return msg

View file

@ -722,7 +722,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE
tot = bad + good
try:
pct = good / float(tot) * 100
except:
except Exception:
pct = 0
i.append(
"Correct: <b>%(pct)0.2f%%</b><br>(%(good)d of %(tot)d)"

View file

@ -29,7 +29,7 @@ try:
to_json_bytes: Callable[[Any], bytes] = orjson.dumps
from_json_bytes = orjson.loads
except:
except Exception:
print("orjson is missing; DB operations will be slower")
def to_json_bytes(obj: Any) -> bytes:
@ -215,7 +215,7 @@ def call(argv: list[str], wait: bool = True, **kwargs: Any) -> int:
info = subprocess.STARTUPINFO() # type: ignore
try:
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore
except:
except Exception:
# pylint: disable=no-member
info.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW # type: ignore
else:
@ -286,7 +286,7 @@ def plat_desc() -> str:
else:
theos = system
break
except:
except Exception:
continue
return theos

View file

@ -26,7 +26,7 @@ def clear_tempfile(tf):
try:
tf.close()
os.unlink(tf.name)
except:
except Exception:
pass

View file

@ -127,7 +127,7 @@ class {self.classname()}:
for hook in self._hooks:
try:
hook({", ".join(arg_names)})
except:
except Exception:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
@ -163,7 +163,7 @@ class {self.classname()}:
for filter in self._hooks:
try:
{arg_names[0]} = filter({", ".join(arg_names)})
except:
except Exception:
# if the hook fails, remove it
self._hooks.remove(filter)
raise

View file

@ -232,7 +232,7 @@ def setupLangAndBackend(
global _qtrans
try:
locale.setlocale(locale.LC_ALL, "")
except:
except Exception:
pass
# add _ and ngettext globals used by legacy code
@ -630,7 +630,7 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None:
pmLoadResult = pm.setupMeta()
Collection.initialize_backend_logging()
except:
except Exception:
# will handle below
traceback.print_exc()
pm = None
@ -720,7 +720,7 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None:
# we must have a usable temp dir
try:
tempfile.gettempdir()
except:
except Exception:
QMessageBox.critical(
None,
tr.qt_misc_error(),

View file

@ -87,6 +87,7 @@ def show(mw: aqt.AnkiQt) -> QDialog:
"Christian Krause",
"Christian Rusche",
"Dave Druelinger",
"David Culley",
"David Smith",
"Dmitry Mikheev",
"Dotan Cohen",

View file

@ -248,7 +248,7 @@ class AddonManager:
__import__(addon.dir_name)
except AbortAddonImport:
pass
except:
except Exception:
name = html.escape(addon.human_name())
page = addon.page()
if page:
@ -341,7 +341,7 @@ class AddonManager:
except json.JSONDecodeError as e:
print(f"json error in add-on {module}:\n{e}")
return dict()
except:
except Exception:
# missing meta file, etc
return dict()
@ -644,7 +644,7 @@ class AddonManager:
try:
with open(path, encoding="utf8") as f:
return json.load(f)
except:
except Exception:
return None
def set_config_help_action(self, module: str, action: Callable[[], str]) -> None:

View file

@ -243,7 +243,7 @@ class DataModel(QAbstractTableModel):
self._state = self._state.toggle_state()
try:
self._search_inner(context)
except:
except Exception:
# rollback to prevent inconsistent state
self._state = self._state.toggle_state()
raise

View file

@ -293,7 +293,7 @@ class DebugConsole(QDialog):
try:
# pylint: disable=exec-used
exec(text, vars)
except:
except Exception:
self._output += traceback.format_exc()
self._captureOutput(False)
buf = ""

View file

@ -267,7 +267,7 @@ class DeckConf(QDialog):
if i == int(i):
i = int(i)
ret.append(i)
except:
except Exception:
# invalid, don't update
showWarning(tr.scheduling_steps_must_be_numbers())
return

View file

@ -200,7 +200,7 @@ class AnkiQt(QMainWindow):
self.setupUI()
self.setupAddons(args)
self.finish_ui_setup()
except:
except Exception:
showInfo(tr.qt_misc_error_during_startup(val=traceback.format_exc()))
sys.exit(1)
# must call this after ui set up
@ -351,7 +351,7 @@ class AnkiQt(QMainWindow):
f.profiles.addItems(profs)
try:
idx = profs.index(self.pm.name)
except:
except Exception:
idx = 0
f.profiles.setCurrentRow(idx)
@ -681,7 +681,7 @@ class AnkiQt(QMainWindow):
self.maybeOptimize()
if not dev_mode:
corrupt = self.col.db.scalar("pragma quick_check") != "ok"
except:
except Exception:
corrupt = True
try:
@ -693,7 +693,7 @@ class AnkiQt(QMainWindow):
force=False,
wait_for_completion=False,
)
except:
except Exception:
print("backup on close failed")
self.col.close(downgrade=False)
except Exception as e:

View file

@ -44,7 +44,7 @@ def _patch_pkgutil() -> None:
reader = module.__loader__.get_resource_reader(package) # type: ignore[attr-defined]
with reader.open_resource(resource) as f:
return f.read()
except:
except Exception:
return None
pkgutil.get_data = get_data_custom

View file

@ -384,7 +384,7 @@ class Preferences(QDialog):
lang = lang.replace("-", "_")
try:
return codes.index(lang)
except:
except Exception:
return codes.index("en_US")
def on_language_index_changed(self, idx: int) -> None:

View file

@ -216,7 +216,7 @@ class ProfileManager:
self.name = name
try:
self.profile = self._unpickle(data)
except:
except Exception:
print(traceback.format_exc())
QMessageBox.warning(
None,
@ -285,7 +285,7 @@ class ProfileManager:
showWarning(tr.profiles_anki_could_not_rename_your_profile())
else:
raise
except:
except BaseException:
self.db.rollback()
raise
else:
@ -386,7 +386,7 @@ class ProfileManager:
if self.db:
try:
self.db.close()
except:
except Exception:
pass
for suffix in ("", "-journal"):
fpath = path + suffix
@ -406,7 +406,7 @@ create table if not exists profiles
data = self.db.scalar(
"select cast(data as blob) from profiles where name = '_global'"
)
except:
except Exception:
traceback.print_stack()
if result.loadError:
# already failed, prevent infinite loop
@ -420,7 +420,7 @@ create table if not exists profiles
try:
self.meta = self._unpickle(data)
return result
except:
except Exception:
traceback.print_stack()
print("resetting corrupt _global")
result.loadError = True

View file

@ -13,7 +13,7 @@ from typing import TypeVar, Union
try:
import PyQt6
except:
except Exception:
from .qt5 import * # type: ignore
else:
if os.getenv("ENABLE_QT5_COMPAT"):

View file

@ -504,13 +504,13 @@ if is_win:
def _voice_to_objects(self, voice: Any) -> list[WindowsVoice]:
try:
langs = voice.GetAttribute("language")
except:
except Exception:
# no associated language; ignore
return []
langs = lcid_hex_str_to_lang_codes(langs)
try:
name = voice.GetAttribute("name")
except:
except Exception:
# some voices may not have a name
name = "unknown"
name = self._tidy_name(name)