From 7e07f5165375394b4770fff396846ca9bca35379 Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Mon, 20 Apr 2026 15:50:24 +0200 Subject: [PATCH] local: gracefully fail if the lock is already taken An EACCES or EAGAIN error on a non-blocking lockf is not actually an error, but rather the program working as intended. At this stage, any other error is unexpected and should probably be left alone. The tailor-made exception can be caught in our main function to display a short error string and use a specific error code for the user to act accordingly. This is much more user-friendly (and machine-friendly) than the standard python uncaught exception handler. --- lieer/gmailieer.py | 6 +++++- lieer/local.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lieer/gmailieer.py b/lieer/gmailieer.py index 8430cfb..234c862 100755 --- a/lieer/gmailieer.py +++ b/lieer/gmailieer.py @@ -416,7 +416,11 @@ def main(self): if args.quiet: args.no_progress = True - args.func(args) + try: + args.func(args) + except Local.LockingException as e: + print(e, file=sys.stderr) + sys.exit(7) def initialize(self, args): self.setup(args, False) diff --git a/lieer/local.py b/lieer/local.py index a9e5f79..966794b 100644 --- a/lieer/local.py +++ b/lieer/local.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import base64 +import errno import fcntl import json import os @@ -116,6 +117,9 @@ def update_translation_list_with_overlay(self, translation_list_overlay): class RepositoryException(Exception): pass + class LockingException(RepositoryException): + pass + class Config: replace_slash_with_dot = False account = None @@ -382,10 +386,14 @@ def load_repository(self, block=False): fcntl.lockf(self.lckf, fcntl.LOCK_EX) else: fcntl.lockf(self.lckf, fcntl.LOCK_EX | fcntl.LOCK_NB) - except OSError: - raise Local.RepositoryException( - "failed to lock repository (probably in use by another gmi instance)" - ) + except OSError as e: + if e.errno in (errno.EACCES, errno.EAGAIN): + # Lock already taken, works as intended + raise Local.LockingException( + "failed to lock repository (probably in use by another gmi instance)" + ) from None + # otherwise probably irrecoverable, keep the raw exception to help debugging + raise self.config = Local.Config(self.config_f) self.state = Local.State(self.state_f, self.config)