6fb9c41b5fbcf3005aa779a046be3945975fef32 — JA Viljoen 9 months ago 7396b34
Enable running commands needing quoting with utils.run

This function now accepts a command as either a single string
or a list of strings.
If it's a list, the elements are quoted before sending it to the shell.

utils.mkdir and utils.cp can now accept paths that need to be quoted,
such as path names containing spaces.
2 files changed, 23 insertions(+), 4 deletions(-)

M tests/test_utils.py
M yatte/utils.py
M tests/test_utils.py => tests/test_utils.py +14 -0
@@ 10,6 10,20 @@ def test_run_command_successfully(capfd):
    assert std.out == "ok\n"

def test_run_command_with_shell_quoting(capfd, tmp_path):
    d = tmp_path / "a sub>dir"  # special chars, so must be quoted
    std = capfd.readouterr()
    assert std.err == f"$ mkdir -p '{d}'\n"
    assert d.is_dir()

    f = d / "pyproject.toml"
    yatte.utils.cp("pyproject.toml", f)
    std = capfd.readouterr()
    assert std.err == f"$ cp -p pyproject.toml '{f}'\n"
    assert f.is_file()

def test_run_command_that_errors_out(capfd):
    with pytest.raises(SystemExit) as e:
        yatte.utils.run("echo failure imminent; exit 2")

M yatte/utils.py => yatte/utils.py +9 -4
@@ 5,7 5,8 @@ import sys
from concurrent.futures import ProcessPoolExecutor
from logging import error
from pathlib import Path
from typing import Iterable
from shlex import quote
from typing import Iterable, List, Union

def stderr(s: str):

@@ 13,8 14,12 @@ def stderr(s: str):
    print(s, file=sys.stderr)

def run(cmd: str):
def run(cmd: Union[str, List[str]]):
    """Run a shell command."""
    if isinstance(cmd, list):
        cmd = " ".join(map(quote, cmd))
        # equivalent to shlex.join() in python >= 3.8

    stderr(f"$ {cmd}")
        subprocess.run(cmd, shell=True, check=True)

@@ 51,14 56,14 @@ def _run(cmd: str) -> int:
def mkdir(d: Path):
    """Create directory d if it doesn't already exist."""
    if not d.is_dir():
        run(f"mkdir -p {d}")
        run(["mkdir", "-p", str(d)])

def cp(src: Path, dest: Path):
    """Copy src to dest if dest doesn't already exist."""
    if not dest.is_file():
        run(f"cp -p {src} {dest}")
        run(["cp", "-p", str(src), str(dest)])

def is_newer(f: Path, than: Path) -> bool: