From ceae28d286b5d647d4e07b9e31407534c86e71eb Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sun, 22 Jun 2025 01:09:40 +0200 Subject: [PATCH] Fix hanging of tool executions that use shell execution #212 Affected ExecuteShellCommandTool and GetCurrentConfigTool (because of git status). Solution: Adjust parameters for subprocess creation --- src/serena/__init__.py | 12 +++++++----- src/serena/util/git.py | 21 +++++++++++++++++++++ src/serena/util/shell.py | 20 +++++++++++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 src/serena/util/git.py diff --git a/src/serena/__init__.py b/src/serena/__init__.py index 1a6b238..9ee9fb1 100644 --- a/src/serena/__init__.py +++ b/src/serena/__init__.py @@ -1,17 +1,19 @@ __version__ = "2025-05-21" +import logging + +log = logging.getLogger(__name__) + def serena_version() -> str: """ :return: the version of the package, including git status if available. """ + from serena.util.git import get_git_status + version = __version__ try: - from sensai.util.git import git_status - from sensai.util.logging import LoggingDisabledContext - - with LoggingDisabledContext(): - git_status = git_status() + git_status = get_git_status() version += f"-{git_status.commit[:8]}" if not git_status.is_clean: version += "-dirty" diff --git a/src/serena/util/git.py b/src/serena/util/git.py new file mode 100644 index 0000000..b46d17e --- /dev/null +++ b/src/serena/util/git.py @@ -0,0 +1,21 @@ +import logging + +from sensai.util.git import GitStatus + +from .shell import subprocess_check_output + +log = logging.getLogger(__name__) + + +def get_git_status() -> GitStatus | None: + try: + commit_hash = subprocess_check_output(["git", "rev-parse", "HEAD"]) + unstaged = bool(subprocess_check_output(["git", "diff", "--name-only"])) + staged = bool(subprocess_check_output(["git", "diff", "--staged", "--name-only"])) + untracked = bool(subprocess_check_output(["git", "ls-files", "--others", "--exclude-standard"])) + return GitStatus( + commit=commit_hash, has_unstaged_changes=unstaged, has_staged_uncommitted_changes=staged, has_untracked_files=untracked + ) + except Exception as e: + log.error("Error determining Git status", exc_info=e) + return None diff --git a/src/serena/util/shell.py b/src/serena/util/shell.py index d940384..faef516 100644 --- a/src/serena/util/shell.py +++ b/src/serena/util/shell.py @@ -24,11 +24,14 @@ def execute_shell_command(command: str, cwd: str | None = None, capture_stderr: if cwd is None: cwd = os.getcwd() + is_windows = platform.system() == "Windows" process = subprocess.Popen( command, - shell=platform.system() != "Windows", + shell=not is_windows, + stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE if capture_stderr else None, + creationflags=subprocess.CREATE_NO_WINDOW if is_windows else 0, text=True, encoding="utf-8", errors="replace", @@ -37,3 +40,18 @@ def execute_shell_command(command: str, cwd: str | None = None, capture_stderr: stdout, stderr = process.communicate() return ShellCommandResult(stdout=stdout, stderr=stderr, return_code=process.returncode, cwd=cwd) + + +def subprocess_check_output(args: list[str], encoding: str = "utf-8", strip: bool = True, timeout: float | None = None) -> str: + kwargs = { + "stdin": subprocess.DEVNULL, + "stderr": subprocess.PIPE, + "timeout": timeout, + "env": os.environ.copy(), + } + if platform.system() == "Windows": + kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW + output = subprocess.check_output(args, **kwargs).decode(encoding) + if strip: + output = output.strip() + return output