Skip to content

Commit

Permalink
feat: run commands to get settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Ned Batchelder committed Oct 9, 2023
1 parent 8cc5926 commit 1f6ec11
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/scriv/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .exceptions import ScrivException
from .literals import find_literal
from .optional import tomllib
from .shell import run_shell_command

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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_shell_command(cmd)
if not ok:
raise ScrivException(f"Command {cmd!r} failed:\n{out}")
if out.count("\n") == 1:
out = out.rstrip("\r\n")
value = out
return value

def read_file_value(self, file_name: str) -> str:
Expand Down
19 changes: 19 additions & 0 deletions src/scriv/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,22 @@ def run_simple_command(cmd: Union[str, List[str]]) -> str:
if not ok:
return ""
return out.strip()


def run_shell_command(cmd: str) -> CmdResult:
"""
Run a command line with a shell.
"""
logger.debug(f"Running shell command {cmd!r}")
proc = subprocess.run(
cmd,
shell=True,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
output = proc.stdout.decode("utf-8")
logger.debug(
f"Command exited with {proc.returncode} status. Output: {output!r}"
)
return proc.returncode == 0, output
40 changes: 40 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,43 @@ def test_no_toml_installed_no_settings(self, temp_dir):
with without_module(scriv.config, "tomllib"):
config = Config.read()
assert config.categories[0] == "Removed"


@pytest.mark.parametrize(
"cmd_output, result",
[
("Xyzzy 2 3\n", "Xyzzy 2 3"),
("Xyzzy 2 3\nAnother line\n", "Xyzzy 2 3\nAnother line\n"),
],
)
def test_command_running(mocker, cmd_output, result):
# Any setting can be the output of a command.
mocker.patch(
"scriv.config.run_shell_command", lambda cmd: (True, cmd_output)
)
text = Config(output_file="command: doesnt-matter").output_file
assert text == result


def test_real_command_running():
text = Config(output_file="command: echo Xyzzy 2 3").output_file
assert text == "Xyzzy 2 3"


@pytest.mark.parametrize(
"bad_cmd, msg_rx",
[
(
"xyzzyplugh",
"Couldn't read 'output_file' setting: Command 'xyzzyplugh' failed:",
),
(
"'hi!2><",
"Couldn't read 'output_file' setting: Command \"'hi!2><\" failed:",
),
],
)
def test_bad_command(fake_run_command, bad_cmd, msg_rx):
# Any setting can be the output of a command.
with pytest.raises(ScrivException, match=msg_rx):
_ = Config(output_file=f"command: {bad_cmd}").output_file

0 comments on commit 1f6ec11

Please sign in to comment.