From 10d5df6689749b7036c4fcca209c2c4060e1d103 Mon Sep 17 00:00:00 2001 From: Jonas Haag Date: Fri, 22 Dec 2023 14:45:19 +0100 Subject: [PATCH] Don't use shell by default. Fixes #100, #103 --- .pre-commit-config.yaml | 2 +- src/subprocess_tee/__init__.py | 7 +++++-- test/test_rich.py | 4 ++-- test/test_unit.py | 18 ++++++++++++------ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d30d44c..3a5e746 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,7 +53,7 @@ repos: - pytest>=6.1.2 - enrich>=1.2.5 - repo: https://github.com/PyCQA/pylint - rev: v3.0.0a6 + rev: v3.0.3 hooks: - id: pylint additional_dependencies: diff --git a/src/subprocess_tee/__init__.py b/src/subprocess_tee/__init__.py index c13109e..7b3094b 100644 --- a/src/subprocess_tee/__init__.py +++ b/src/subprocess_tee/__init__.py @@ -6,7 +6,7 @@ import sys from asyncio import StreamReader from importlib.metadata import PackageNotFoundError, version # type: ignore -from shlex import join +from shlex import join, quote from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union try: @@ -127,7 +127,10 @@ def run(args: Union[str, List[str]], **kwargs: Any) -> CompletedProcess: quiet: False - Avoid printing output """ if isinstance(args, str): - cmd = args + if kwargs.get("shell"): + cmd = args + else: + cmd = quote(args) else: # run was called with a list instead of a single item but asyncio # create_subprocess_shell requires command as a single string, so diff --git a/test/test_rich.py b/test/test_rich.py index 2b2c7ce..8d6efa2 100644 --- a/test/test_rich.py +++ b/test/test_rich.py @@ -19,7 +19,7 @@ def test_rich_console_ex() -> None: # an exception. Some libraries may still sometimes send bytes to the # streams, notable example being click. # sys.stdout.write(b"epsilon\n") # type: ignore - proc = run("echo 123") + proc = run("echo 123", shell=True) assert proc.stdout == "123\n" text = console.export_text() assert text == "alpha\nbeta\ngamma\ndelta\n123\n" @@ -30,7 +30,7 @@ def test_rich_console_ex_ansi() -> None: print() console = Console(force_terminal=True, record=True, redirect=True) console.print("[green]this from Console.print()[/green]", style="red") - proc = run(r'echo -e "\033[31mred\033[0m"') + proc = run(r'echo -e "\033[31mred\033[0m"', shell=True) assert proc.returncode == 0 assert "red" in proc.stdout diff --git a/test/test_unit.py b/test/test_unit.py index 652fce9..6a93288 100644 --- a/test/test_unit.py +++ b/test/test_unit.py @@ -11,7 +11,7 @@ def test_run_string() -> None: """Valida run() called with a single string command.""" - cmd = "echo 111 && >&2 echo 222" + cmd = "echo 111 && echo 222 >&2" old_result = subprocess.run( cmd, shell=True, @@ -20,7 +20,7 @@ def test_run_string() -> None: stderr=subprocess.PIPE, check=False, ) - result = run(cmd) + result = run(cmd, shell=True) assert result.returncode == old_result.returncode assert result.stdout == old_result.stdout assert result.stderr == old_result.stderr @@ -74,7 +74,7 @@ def test_run_echo(capsys: CaptureFixture[str]) -> None: def test_run_with_env(env: Dict[str, str]) -> None: """Validate that passing custom env to run() works.""" env["FOO"] = "BAR" - result = run("echo $FOO", env=env, echo=True) + result = run("echo $FOO", env=env, echo=True, shell=True) assert result.stdout == "BAR\n" @@ -82,16 +82,22 @@ def test_run_shell() -> None: """Validate run call with multiple shell commands works.""" cmd = "echo a && echo b && false || exit 4" # "python --version" - result = run(cmd, echo=True) + result = run(cmd, echo=True, shell=True) assert result.returncode == 4 assert result.stdout == "a\nb\n" +def test_run_shell_false() -> None: + """Shell commands should not work if 'shell=False'.""" + with pytest.raises(subprocess.CalledProcessError): + run("echo 42", check=True) + + def test_run_shell_undefined() -> None: """Validate run call with multiple shell commands works.""" cmd = "echo a && echo b && false || exit 4" # "python --version" - result = run(cmd, echo=True, env={}) + result = run(cmd, echo=True, env={}, shell=True) assert result.returncode == 4 assert result.stdout == "a\nb\n" @@ -99,7 +105,7 @@ def test_run_shell_undefined() -> None: def test_run_cwd() -> None: """Validate that run accepts cwd and respects it.""" cmd = "pwd" - result = run(cmd, echo=True, cwd="/") + result = run(cmd, echo=True, cwd="/", shell=True) assert result.returncode == 0 assert result.stdout == "/\n"