Prompt user whether to replace file contents

This commit is contained in:
Joscha 2025-11-03 02:23:05 +01:00
parent 8c12e26951
commit 208ccb0de2
6 changed files with 67 additions and 21 deletions

View file

@ -1,10 +1,10 @@
from . import cmd, file, modules
from . import file, modules, util
from .orchestrator import Module, Orchestrator
__all__: list[str] = [
"Module",
"Orchestrator",
"cmd",
"file",
"modules",
"util",
]

View file

@ -1,16 +0,0 @@
import shlex
import subprocess
from rich import print
from rich.markup import escape
def run_execute(*cmd: str) -> None:
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
subprocess.run(cmd, check=True)
def run_capture(*cmd: str) -> str:
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
result = subprocess.run(cmd, check=True, capture_output=True, encoding="utf-8")
return result.stdout

View file

@ -1,5 +1,5 @@
from pasch.cmd import run_execute
from pasch.orchestrator import Module, Orchestrator
from pasch.util import run_execute
class Echo(Module):

View file

@ -4,10 +4,12 @@ import random
import string
from pathlib import Path
from rich.console import Console
from rich.markup import escape
from pasch.file.file import File
from pasch.orchestrator import Module, Orchestrator
from pasch.util import fmt_diff, prompt
def random_tmp_path(path: Path) -> Path:
@ -42,6 +44,21 @@ def path_to_str(path: Path) -> str:
return str(path.resolve())
def diff_and_prompt(c: Console, path: Path, new_content_bytes: bytes) -> bool:
try:
new_content = new_content_bytes.decode("utf-8")
except:
return False
try:
old_content = path.read_text(encoding="utf-8")
except:
return False
c.print(fmt_diff(old_content, new_content))
return prompt("Replace file contents?", default=False)
class FileDb:
def __init__(self, path: Path) -> None:
self._path = path
@ -128,7 +145,8 @@ class Files(Module):
if reason := self._file_db.verify_hash(path, cur_hash):
self.c.print(f"[red]Error:[/] {escape(reason)}")
return
if not diff_and_prompt(self.c, path, content):
return
# We want to avoid scenarios where we fail to remember a file we've
# written. It is better to remember a file with an incorrect hash than

View file

@ -3,8 +3,8 @@ from subprocess import CalledProcessError
from rich.markup import escape
from pasch.cmd import run_capture, run_execute
from pasch.orchestrator import Module, Orchestrator
from pasch.util import run_capture, run_execute
@dataclass

44
pasch/util.py Normal file
View file

@ -0,0 +1,44 @@
import difflib
import shlex
import subprocess
from rich import print
from rich.markup import escape
from rich.syntax import Syntax
def run_execute(*cmd: str) -> None:
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
subprocess.run(cmd, check=True)
def run_capture(*cmd: str) -> str:
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
result = subprocess.run(cmd, check=True, capture_output=True, encoding="utf-8")
return result.stdout
def fmt_diff(old: str, new: str, old_name="old", new_name="new") -> Syntax:
diff_text = "".join(
difflib.unified_diff(
old.splitlines(keepends=True),
new.splitlines(keepends=True),
fromfile=old_name,
tofile=new_name,
lineterm="\n",
)
)
return Syntax(diff_text, "diff", line_numbers=False)
def prompt(question: str, default: bool | None = None) -> bool:
default_str = {True: "[Y/n]", False: "[y/N]", None: "[y/n]"}[default]
while True:
reply = input(f"{question} {default_str} ").strip().lower()
if not reply and default is not None:
return default
if reply in {"y", "yes"}:
return True
if reply in {"n", "no"}:
return False
print("Please enter y/yes or n/no.")