Skip to content

Commit 6329ef4

Browse files
committed
fix(cli): quote py interpreter path when it contains whitespace
For the `py` script type, the resolved interpreter may be an absolute path containing spaces (notably `sys.executable` under Windows `Program Files`). Quote it when it contains whitespace so the `{SCRIPT}` invocation isn't split into multiple arguments. Add positive/negative tests for the quoting behavior.
1 parent e1534e4 commit 6329ef4

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

src/specify_cli/integrations/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,11 @@ def process_template(
626626
# executable on Windows).
627627
if script_type == "py":
628628
interpreter = IntegrationBase.resolve_python_interpreter(project_root)
629+
# Quote the interpreter if it contains whitespace (e.g. an
630+
# absolute ``sys.executable`` path under Windows
631+
# ``Program Files``) so it isn't split into multiple args.
632+
if any(ch.isspace() for ch in interpreter):
633+
interpreter = f'"{interpreter}"'
629634
script_command = f"{interpreter} {script_command}"
630635
content = content.replace("{SCRIPT}", script_command)
631636

tests/integrations/test_base.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,31 @@ def test_sh_does_not_prefix_interpreter(self):
403403
assert ".specify/scripts/bash/check-prerequisites.sh --json" in result
404404
assert "python" not in result
405405

406+
def test_py_quotes_interpreter_with_spaces(self, monkeypatch):
407+
# An interpreter path containing whitespace (e.g. Windows
408+
# ``Program Files``) must be quoted so it isn't split into args.
409+
monkeypatch.setattr(
410+
"specify_cli.integrations.base.shutil.which", lambda name: None
411+
)
412+
monkeypatch.setattr(
413+
"specify_cli.integrations.base.sys.executable",
414+
r"C:\Program Files\Python\python.exe",
415+
)
416+
result = IntegrationBase.process_template(self.CONTENT, "agent", "py")
417+
assert (
418+
'"C:\\Program Files\\Python\\python.exe" '
419+
".specify/scripts/python/check-prerequisites.py --json"
420+
) in result
421+
422+
def test_py_does_not_quote_interpreter_without_spaces(self, monkeypatch):
423+
# Negative: a whitespace-free interpreter is left unquoted.
424+
monkeypatch.setattr(
425+
"specify_cli.integrations.base.shutil.which",
426+
lambda name: "/usr/bin/python3" if name == "python3" else None,
427+
)
428+
result = IntegrationBase.process_template(self.CONTENT, "agent", "py")
429+
assert '"' not in result.split("check-prerequisites.py")[0]
430+
406431
def test_py_uses_project_venv(self, monkeypatch, tmp_path):
407432
venv_python = tmp_path / ".venv" / "bin" / "python"
408433
venv_python.parent.mkdir(parents=True)

0 commit comments

Comments
 (0)