diff --git a/src/scriv/config.py b/src/scriv/config.py index e388d8d..681a604 100644 --- a/src/scriv/config.py +++ b/src/scriv/config.py @@ -13,6 +13,7 @@ from .exceptions import ScrivException from .literals import find_literal from .optional import tomllib +from .shell import run_command logger = logging.getLogger(__name__) @@ -363,6 +364,7 @@ def resolve_value(self, value: str) -> str: Prefixes: "file:" read the content from a file. "literal:" read a literal string from a file. + "command:" read the output of a shell command. """ value = value.replace("${config:format}", self._options.format) @@ -392,6 +394,14 @@ def resolve_value(self, value: str) -> str: + f"{value!r}" ) value = found + elif value.startswith("command:"): + cmd = value.partition(":")[2].strip() + ok, out = run_command(cmd) + if not ok: + raise ScrivException(f"Command {cmd!r} failed:\n{out}") + if out.count("\n") == 1: + out = out.rstrip("\n") + value = out return value def read_file_value(self, file_name: str) -> str: diff --git a/tests/test_config.py b/tests/test_config.py index 5fb1fd6..ba624e8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -344,3 +344,19 @@ def test_no_toml_installed_no_settings(self, temp_dir): with without_module(scriv.config, "tomllib"): config = Config.read() assert config.categories[0] == "Removed" + + +def test_command_running(fake_run_command): + # Any setting can be the output of a command. + fake_run_command.patch_module("scriv.config") + fake_run_command.add_handler( + "showtext", lambda argv: (True, " ".join(argv[1:])) + ) + text = Config(output_file="command: showtext Xyzzy 2 3").output_file + assert text == "Xyzzy 2 3" + + +def test_nosuch_command(fake_run_command): + # Any setting can be the output of a command. + text = Config(output_file="command: xyzzyplugh").output_file + assert text == "Xyzzy 2 3"