flake history: make operations atomic
All checks were successful
checks-impure / test (pull_request) Successful in 1m6s
checks / test (pull_request) Successful in 1m38s

This commit is contained in:
DavHau 2023-12-01 14:00:15 +07:00
parent 6d6460ffca
commit 261322fae8
3 changed files with 28 additions and 8 deletions

View File

@ -5,19 +5,22 @@ from pathlib import Path
from clan_cli.dirs import user_history_file
from ..async_cmd import CmdOut, runforcli
from ..locked_open import locked_open
async def add_flake(path: Path) -> dict[str, CmdOut]:
user_history_file().parent.mkdir(parents=True, exist_ok=True)
# append line to history file
# TODO: Make this atomic
lines: set[str] = set()
if user_history_file().exists():
with open(user_history_file()) as f:
lines = set(f.readlines())
lines.add(str(path))
with open(user_history_file(), "w") as f:
f.writelines(lines)
lines: set = set()
old_lines = set()
with locked_open(user_history_file(), "w+") as f:
old_lines = set(f.readlines())
lines = old_lines | {str(path)}
if old_lines != lines:
f.seek(0)
f.writelines(lines)
f.truncate()
return {}

View File

@ -4,12 +4,14 @@ from pathlib import Path
from clan_cli.dirs import user_history_file
from ..locked_open import locked_open
def list_history() -> list[Path]:
if not user_history_file().exists():
return []
# read path lines from history file
with open(user_history_file()) as f:
with locked_open(user_history_file()) as f:
lines = f.readlines()
return [Path(line.strip()) for line in lines]

View File

@ -0,0 +1,15 @@
import fcntl
from collections.abc import Generator
from contextlib import contextmanager
from pathlib import Path
@contextmanager
def locked_open(filename: str | Path, mode: str = "r") -> Generator:
"""
This is a context manager that provides an advisory write lock on the file specified by `filename` when entering the context, and releases the lock when leaving the context. The lock is acquired using the `fcntl` module's `LOCK_EX` flag, which applies an exclusive write lock to the file.
"""
with open(filename, mode) as fd:
fcntl.flock(fd, fcntl.LOCK_EX)
yield fd
fcntl.flock(fd, fcntl.LOCK_UN)