Narrow bare except blocks so real errors aren't swallowed (roadmap A4)

Replaced all 46 bare 'except:' across the package with specific exception
types, and logged the cases that indicate a real problem:

- config load/save: FileNotFoundError is the silent first-run case; other
  OSError/ValueError/UnicodeDecodeError are logged.
- ffprobe/avprobe: JSON parse failures are now logged (previously a media
  file that ffprobe couldn't read just silently failed to import); decode
  fallbacks -> UnicodeDecodeError; duration parse -> ValueError/TypeError/
  KeyError; install check -> OSError/SubprocessError.
- executor: pre_/post_function and progress-polling hooks now use hasattr
  checks, and a real exception inside a defined hook is logged instead of
  swallowed; decode fallbacks and numeric parses narrowed.
- best-effort filesystem ops (makedirs/unlink) -> OSError.
- all backend check_is_installed probes -> OSError/SubprocessError.
- shutdown: also fixes a latent NameError ('failure' was only set in the
  except branch, so a successful logind PowerOff would crash the handler).

No bare 'except:' remain. Full suite (51 tests) green; GUI starts clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-07 01:02:33 -07:00
parent 63f10f3aa0
commit 6b624a72cd
21 changed files with 70 additions and 62 deletions

View file

@ -30,7 +30,7 @@ class avbase(devedeng.executor.executor):
(stdout, stderr) = handle.communicate() (stdout, stderr) = handle.communicate()
if 0 != handle.wait(): if 0 != handle.wait():
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
self.check_version_txt(stdout) self.check_version_txt(stdout)
@ -54,7 +54,7 @@ class avbase(devedeng.executor.executor):
else: else:
major = int(line[15:pos1].strip()) major = int(line[15:pos1].strip())
minor = int(line[pos1 + 1:pos2].strip()) minor = int(line[pos1 + 1:pos2].strip())
except: except (ValueError, IndexError):
return False return False
self.major_version = major self.major_version = major
self.minor_version = minor self.minor_version = minor

View file

@ -103,7 +103,7 @@ class avconv(devedeng.avbase.avbase):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):
@ -648,7 +648,7 @@ class avconv(devedeng.avbase.avbase):
for e in parts: for e in parts:
try: try:
v = float(e) v = float(e)
except: except ValueError:
continue continue
t = t * 60.0 + v t = t * 60.0 + v
t /= self.final_length t /= self.final_length

View file

@ -43,7 +43,7 @@ class avprobe(devedeng.avbase.avbase):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):
@ -70,7 +70,7 @@ class avprobe(devedeng.avbase.avbase):
try: try:
stdout2 = stdout.decode("utf-8") stdout2 = stdout.decode("utf-8")
except: except UnicodeDecodeError:
stdout2 = stdout.decode("latin1") stdout2 = stdout.decode("latin1")
self.config.append_static_log("AVProbe JSON data: " + str(stdout2)) self.config.append_static_log("AVProbe JSON data: " + str(stdout2))
return self.process_json(stdout2, file_name) return self.process_json(stdout2, file_name)
@ -97,7 +97,9 @@ class avprobe(devedeng.avbase.avbase):
"Getting data for {:s} with avprobe".format(file_name)) "Getting data for {:s} with avprobe".format(file_name))
try: try:
video_data = json.loads(stdout2) video_data = json.loads(stdout2)
except: except ValueError as e:
self.config.append_static_log(
"avprobe returned unparseable JSON for %s: %s" % (file_name, e))
return True # There was an error reading the JSON data return True # There was an error reading the JSON data
if not("streams" in video_data): if not("streams" in video_data):
@ -108,7 +110,7 @@ class avprobe(devedeng.avbase.avbase):
if (self.original_length == -1) and ("duration" in element): if (self.original_length == -1) and ("duration" in element):
try: try:
self.original_length = int(float(element["duration"])) self.original_length = int(float(element["duration"]))
except: except (ValueError, TypeError, KeyError):
self.original_length = -1 self.original_length = -1
if (element["codec_type"] == "video"): if (element["codec_type"] == "video"):
@ -173,7 +175,7 @@ class avprobe(devedeng.avbase.avbase):
try: try:
stdout2 = stdout.decode("utf-8") + \ stdout2 = stdout.decode("utf-8") + \
"\n" + stderr.decode("utf-8") "\n" + stderr.decode("utf-8")
except: except UnicodeDecodeError:
stdout2 = stdout.decode("latin1") + \ stdout2 = stdout.decode("latin1") + \
"\n" + stderr.decode("latin1") "\n" + stderr.decode("latin1")
self.config.append_static_log( self.config.append_static_log(

View file

@ -39,7 +39,7 @@ class brasero(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -103,7 +103,7 @@ class choose_disc_type(GObject.GObject):
(stdout, stderr) = handle.communicate() (stdout, stderr) = handle.communicate()
retval = handle.wait() retval = handle.wait()
return True return True
except: except (OSError, subprocess.SubprocessError):
return False return False
def set_type(self, disc_type): def set_type(self, disc_type):

View file

@ -210,8 +210,11 @@ class configuration(GObject.GObject):
pass pass
continue continue
config_data.close() config_data.close()
except: except FileNotFoundError:
pass pass # no config file yet (first run) — defaults are used
except (OSError, ValueError, UnicodeDecodeError) as e:
self.append_static_log(
"Error reading config file %s: %s" % (config_path, e))
return False return False
@ -285,8 +288,8 @@ class configuration(GObject.GObject):
config_path = os.path.join(os.environ.get("HOME"), ".devedeng") config_path = os.path.join(os.environ.get("HOME"), ".devedeng")
if os.path.exists(config_path): if os.path.exists(config_path):
os.remove(config_path) os.remove(config_path)
except: except OSError as e:
pass self.append_static_log("Error writing config file: %s" % e)
def append_log(self, data, cr=True): def append_log(self, data, cr=True):

View file

@ -201,7 +201,7 @@ class dvd_menu(devedeng.interface_manager.interface_manager):
try: try:
fontsize = float(font_elements[-1]) fontsize = float(font_elements[-1])
except: except (ValueError, IndexError):
fontsize = 12 fontsize = 12
return fontname, fontstyle, fontslant, fontsize return fontname, fontstyle, fontslant, fontsize
@ -635,7 +635,7 @@ class dvd_menu(devedeng.interface_manager.interface_manager):
menu_folder = os.path.join(base_path, "menu") menu_folder = os.path.join(base_path, "menu")
try: try:
os.makedirs(menu_folder) os.makedirs(menu_folder)
except: except OSError:
pass pass
n_page = 0 n_page = 0
self.pages = 1 self.pages = 1

View file

@ -33,7 +33,7 @@ class dvdauthor_converter(devedeng.executor.executor):
movie_path = os.path.join(path, "dvd_tree") movie_path = os.path.join(path, "dvd_tree")
try: try:
os.makedirs(movie_path) os.makedirs(movie_path)
except: except OSError:
pass pass
xml_file = self.create_dvdauthor_xml(path, file_movies, menu_entries, start_with_menu, play_all_opt, menuWide, segment_lengths) xml_file = self.create_dvdauthor_xml(path, file_movies, menu_entries, start_with_menu, play_all_opt, menuWide, segment_lengths)
@ -73,7 +73,7 @@ class dvdauthor_converter(devedeng.executor.executor):
datapath = os.path.join(movie_folder, "dvd_tree") datapath = os.path.join(movie_folder, "dvd_tree")
try: try:
os.makedirs(xmlpath) os.makedirs(xmlpath)
except: except OSError:
pass pass
if (len(file_movies) == 1) and (menu_entries is None): if (len(file_movies) == 1) and (menu_entries is None):

View file

@ -120,20 +120,20 @@ class executor(GObject.GObject):
self.progress_bar[0].set_label(self.text) self.progress_bar[0].set_label(self.text)
self.progress_bar[1].set_fraction(0.0) self.progress_bar[1].set_fraction(0.0)
self.progress_bar[0].show_all() self.progress_bar[0].show_all()
# call, if it exists, the pre-function # call the pre-function if this process defines one
try: if hasattr(self, "pre_function"):
self.pre_function() try:
except: self.pre_function()
pass except Exception as e:
self.config.append_static_log(
"pre_function failed for %s: %s" % (self.text, e))
self.launch_process(self.command_var) self.launch_process(self.command_var)
if self.use_pulse_mode != self.pulse_mode: if self.use_pulse_mode != self.pulse_mode:
self.set_pulse_mode(self.use_pulse_mode) self.set_pulse_mode(self.use_pulse_mode)
# let subclasses start any timer-based progress polling (e.g. ffmpeg # let subclasses start any timer-based progress polling (e.g. ffmpeg
# reads its -progress file on a GLib timer). No-op by default. # reads its -progress file on a GLib timer). No-op by default.
try: if hasattr(self, "start_progress_polling"):
self.start_progress_polling() self.start_progress_polling()
except AttributeError:
pass
def remove_ansi(self, line): def remove_ansi(self, line):
@ -189,11 +189,11 @@ class executor(GObject.GObject):
self.config.append_log(self.launch_command) self.config.append_log(self.launch_command)
try: try:
self.config.append_log(stdout_r.decode("utf-8")) self.config.append_log(stdout_r.decode("utf-8"))
except: except UnicodeDecodeError:
self.config.append_log(stdout_r.decode("latin-1")) self.config.append_log(stdout_r.decode("latin-1"))
try: try:
self.config.append_log(stderr_r.decode("utf-8")) self.config.append_log(stderr_r.decode("utf-8"))
except: except UnicodeDecodeError:
self.config.append_log(stderr_r.decode("latin-1")) self.config.append_log(stderr_r.decode("latin-1"))
return (stdout_r, stderr_r) return (stdout_r, stderr_r)
@ -396,16 +396,16 @@ class executor(GObject.GObject):
self.set_pulse_mode(False) self.set_pulse_mode(False)
# stop any subclass progress polling timer # stop any subclass progress polling timer
try: if hasattr(self, "stop_progress_polling"):
self.stop_progress_polling() self.stop_progress_polling()
except AttributeError:
pass
# call, if it exists, the post-function # call the post-function if this process defines one
try: if hasattr(self, "post_function"):
self.post_function(retval, self.killed) try:
except: self.post_function(retval, self.killed)
pass except Exception as e:
self.config.append_static_log(
"post_function failed for %s: %s" % (self.text, e))
if self.killed: if self.killed:
retval = 0 retval = 0
@ -435,13 +435,13 @@ class executor(GObject.GObject):
if (pos == -1): if (pos == -1):
try: try:
return float(data) return float(data)
except: except (ValueError, TypeError):
return 0 return 0
else: else:
try: try:
data1 = float(data[:pos]) data1 = float(data[:pos])
data2 = float(data[pos + 1:]) data2 = float(data[pos + 1:])
except: except (ValueError, TypeError):
return 0 return 0
if (data2 == 0): if (data2 == 0):
return 0 return 0

View file

@ -106,7 +106,7 @@ class ffmpeg(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):
@ -124,7 +124,7 @@ class ffmpeg(devedeng.executor.executor):
if line.startswith("ffmpeg version "): if line.startswith("ffmpeg version "):
version_number = int(re.search(r"[0-9]+", line).group()) version_number = int(re.search(r"[0-9]+", line).group())
break break
except: except (OSError, subprocess.SubprocessError, ValueError, AttributeError):
version_number = 7 version_number = 7
pass pass
print(f"Detected ffmpeg version number: {version_number}") print(f"Detected ffmpeg version number: {version_number}")
@ -829,7 +829,7 @@ class ffmpeg(devedeng.executor.executor):
for e in parts: for e in parts:
try: try:
v = float(e) v = float(e)
except: except ValueError:
continue continue
t = t * 60.0 + v t = t * 60.0 + v
t /= self.final_length t /= self.final_length

View file

@ -43,7 +43,7 @@ class ffprobe(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):
@ -69,7 +69,7 @@ class ffprobe(devedeng.executor.executor):
(stdout, stderr) = self.launch_process(command_line, False) (stdout, stderr) = self.launch_process(command_line, False)
try: try:
stdout2 = stdout.decode("utf-8") stdout2 = stdout.decode("utf-8")
except: except UnicodeDecodeError:
stdout2 = stdout.decode("latin1") stdout2 = stdout.decode("latin1")
self.config.append_static_log("FFProbe JSON data: " + str(stdout2)) self.config.append_static_log("FFProbe JSON data: " + str(stdout2))
return self.process_json(file_name, stdout2) return self.process_json(file_name, stdout2)
@ -97,7 +97,9 @@ class ffprobe(devedeng.executor.executor):
"Getting data for {:s} with ffprobe".format(file_name)) "Getting data for {:s} with ffprobe".format(file_name))
try: try:
video_data = json.loads(stdout2) video_data = json.loads(stdout2)
except: except ValueError as e:
self.config.append_static_log(
"ffprobe returned unparseable JSON for %s: %s" % (file_name, e))
return True # There was an error reading the JSON data return True # There was an error reading the JSON data
if not("streams" in video_data): if not("streams" in video_data):
@ -108,7 +110,7 @@ class ffprobe(devedeng.executor.executor):
if (self.original_length == -1) and ("duration" in element): if (self.original_length == -1) and ("duration" in element):
try: try:
self.original_length = int(float(element["duration"])) self.original_length = int(float(element["duration"]))
except: except (ValueError, TypeError, KeyError):
self.original_length = -1 self.original_length = -1
if (element["codec_type"] == "video"): if (element["codec_type"] == "video"):
@ -176,7 +178,7 @@ class ffprobe(devedeng.executor.executor):
try: try:
stdout2 = stdout.decode("utf-8") + \ stdout2 = stdout.decode("utf-8") + \
"\n" + stderr.decode("utf-8") "\n" + stderr.decode("utf-8")
except: except UnicodeDecodeError:
stdout2 = stdout.decode("latin1") + \ stdout2 = stdout.decode("latin1") + \
"\n" + stderr.decode("latin1") "\n" + stderr.decode("latin1")
self.config.append_static_log("Using ffprobe human readable format: " + str(stdout2)) self.config.append_static_log("Using ffprobe human readable format: " + str(stdout2))

View file

@ -47,7 +47,7 @@ class file_copy(devedeng.executor.executor):
try: try:
copied = float(parts[0]) copied = float(parts[0])
except: except (ValueError, IndexError):
return return
p = copied / self.file_size p = copied / self.file_size

View file

@ -42,7 +42,7 @@ class genisoimage(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -39,7 +39,7 @@ class k3b(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -42,7 +42,7 @@ class mkisofs(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -42,7 +42,7 @@ class mplayer(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -41,7 +41,7 @@ class mpv(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -908,16 +908,16 @@ class devede_project:
if self.config.disc_type == "dvd": if self.config.disc_type == "dvd":
try: try:
os.unlink(os.path.join(data.path, data.name + ".iso")) os.unlink(os.path.join(data.path, data.name + ".iso"))
except: except OSError:
pass pass
if (self.config.disc_type == "vcd") or (self.config.disc_type == "svcd") or (self.config.disc_type == "cvd"): if (self.config.disc_type == "vcd") or (self.config.disc_type == "svcd") or (self.config.disc_type == "cvd"):
try: try:
os.unlink(os.path.join(data.path, data.name + ".bin")) os.unlink(os.path.join(data.path, data.name + ".bin"))
except: except OSError:
pass pass
try: try:
os.unlink(os.path.join(data.path, data.name + ".cue")) os.unlink(os.path.join(data.path, data.name + ".cue"))
except: except OSError:
pass pass
else: else:
return return
@ -968,7 +968,7 @@ class devede_project:
movie_folder = os.path.join(data.path, "movies") movie_folder = os.path.join(data.path, "movies")
try: try:
os.makedirs(movie_folder) os.makedirs(movie_folder)
except: except OSError:
pass pass
counter = 0 counter = 0
if self.disc_type == "divx": if self.disc_type == "divx":

View file

@ -22,11 +22,12 @@ class shutdown:
def __init__(self): def __init__(self):
# First, try with logind # First, try with logind
failure = False
try: try:
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
bus.call_sync("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", bus.call_sync("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager",
"PowerOff", GLib.Variant_boolean('(bb)', (False, False)), None, Gio.DBusCallFlags.NONE, -1, None) "PowerOff", GLib.Variant_boolean('(bb)', (False, False)), None, Gio.DBusCallFlags.NONE, -1, None)
except: except Exception:
failure = True failure = True
if (failure): if (failure):
@ -37,7 +38,7 @@ class shutdown:
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
bus.call_sync("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", bus.call_sync("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager",
"Stop", None, None, Gio.DBusCallFlags.NONE, -1, None) "Stop", None, None, Gio.DBusCallFlags.NONE, -1, None)
except: except Exception:
failure = True failure = True
if (failure): if (failure):
@ -48,5 +49,5 @@ class shutdown:
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
bus.call_sync("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer", "org.freedesktop.Hal.Device.SystemPowerManagement", bus.call_sync("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer", "org.freedesktop.Hal.Device.SystemPowerManagement",
"Shutdown", None, None, Gio.DBusCallFlags.NONE, -1, None) "Shutdown", None, None, Gio.DBusCallFlags.NONE, -1, None)
except: except Exception:
failure = True failure = True

View file

@ -39,7 +39,7 @@ class vlc(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):

View file

@ -38,7 +38,7 @@ class xfburn(devedeng.executor.executor):
return True return True
else: else:
return False return False
except: except (OSError, subprocess.SubprocessError):
return False return False
def __init__(self): def __init__(self):