flake history: make operations atomic

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

View File

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