consolidate lock file handling

- adds config_file_lock context manager to re-use
- moves filelock import to module level
- fixes exit codes, stderr messages
This commit is contained in:
Min RK
2024-09-05 11:58:51 +02:00
parent f7118edf48
commit 13c8946d8c

View File

@@ -18,9 +18,11 @@ import re
import sys import sys
import time import time
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from contextlib import contextmanager
from copy import deepcopy from copy import deepcopy
import requests import requests
from filelock import FileLock, Timeout
from .yaml import yaml from .yaml import yaml
@@ -32,6 +34,22 @@ CONFIG_DIR = os.path.join(INSTALL_PREFIX, "config")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yaml") CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yaml")
@contextmanager
def config_file_lock(config_path, timeout=1):
"""Context manager to acquire the config file lock"""
lock_file = f"{config_path}.lock"
try:
with FileLock(lock_file).acquire(timeout=timeout):
yield
except Timeout:
print(
f"Another instance of tljh-config holds the lock {lock_file}.",
file=sys.stderr,
)
sys.exit(1)
def set_item_in_config(config, property_path, value): def set_item_in_config(config, property_path, value):
""" """
Set key at property_path to value in config & return new config. Set key at property_path to value in config & return new config.
@@ -169,9 +187,10 @@ def validate_config(config, validate):
print( print(
f"Config validation error: {e.message}.\n" f"Config validation error: {e.message}.\n"
"You can still apply this change without validation by re-running your command with the --no-validate flag.\n" "You can still apply this change without validation by re-running your command with the --no-validate flag.\n"
"If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues." "If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues.",
file=sys.stderr,
) )
exit() sys.exit(1)
def show_config(config_path): def show_config(config_path):
@@ -186,12 +205,7 @@ def set_config_value(config_path, key_path, value, validate=True):
""" """
Set key at key_path in config_path to value Set key at key_path in config_path to value
""" """
from filelock import FileLock, Timeout with config_file_lock(config_path):
lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path) config = get_current_config(config_path)
config = set_item_in_config(config, key_path, value) config = set_item_in_config(config, key_path, value)
validate_config(config, validate) validate_config(config, validate)
@@ -199,21 +213,12 @@ def set_config_value(config_path, key_path, value, validate=True):
with open(config_path, "w") as f: with open(config_path, "w") as f:
yaml.dump(config, f) yaml.dump(config, f)
except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
def unset_config_value(config_path, key_path, validate=True): def unset_config_value(config_path, key_path, validate=True):
""" """
Unset key at key_path in config_path Unset key at key_path in config_path
""" """
from filelock import FileLock, Timeout with config_file_lock(config_path):
lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path) config = get_current_config(config_path)
config = unset_item_from_config(config, key_path) config = unset_item_from_config(config, key_path)
validate_config(config, validate) validate_config(config, validate)
@@ -221,21 +226,12 @@ def unset_config_value(config_path, key_path, validate=True):
with open(config_path, "w") as f: with open(config_path, "w") as f:
yaml.dump(config, f) yaml.dump(config, f)
except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
def add_config_value(config_path, key_path, value, validate=True): def add_config_value(config_path, key_path, value, validate=True):
""" """
Add value to list at key_path Add value to list at key_path
""" """
from filelock import FileLock, Timeout with config_file_lock(config_path):
lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path) config = get_current_config(config_path)
config = add_item_to_config(config, key_path, value) config = add_item_to_config(config, key_path, value)
validate_config(config, validate) validate_config(config, validate)
@@ -243,21 +239,12 @@ def add_config_value(config_path, key_path, value, validate=True):
with open(config_path, "w") as f: with open(config_path, "w") as f:
yaml.dump(config, f) yaml.dump(config, f)
except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
def remove_config_value(config_path, key_path, value, validate=True): def remove_config_value(config_path, key_path, value, validate=True):
""" """
Remove value from list at key_path Remove value from list at key_path
""" """
from filelock import FileLock, Timeout with config_file_lock(config_path):
lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path) config = get_current_config(config_path)
config = remove_item_from_config(config, key_path, value) config = remove_item_from_config(config, key_path, value)
validate_config(config, validate) validate_config(config, validate)
@@ -265,10 +252,6 @@ def remove_config_value(config_path, key_path, value, validate=True):
with open(config_path, "w") as f: with open(config_path, "w") as f:
yaml.dump(config, f) yaml.dump(config, f)
except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
def get_current_config(config_path): def get_current_config(config_path):
""" """