diff --git a/.serena/memories/adding_new_language_support_guide.md b/.serena/memories/adding_new_language_support_guide.md index e80f1cf8..03b5ed06 100644 --- a/.serena/memories/adding_new_language_support_guide.md +++ b/.serena/memories/adding_new_language_support_guide.md @@ -61,6 +61,7 @@ To implement a new language server using the DependencyProvider pattern: **Implementation Pointers::** - Override `create_launch_command_env` if the launch command needs environment variables to be set (defaults to `{}` in the base implementation) + - When calling subprocesses, e.g. to install dependencies, do not use `subprocess.run` directly; instead, use the `subprocess_run` helper method from `solidlsp.utils.subprocess_utils` You should look at at least one existing implementation of each base class to understand how they work. diff --git a/src/solidlsp/util/subprocess_util.py b/src/solidlsp/util/subprocess_util.py index bf994e45..e88b9a4f 100644 --- a/src/solidlsp/util/subprocess_util.py +++ b/src/solidlsp/util/subprocess_util.py @@ -19,6 +19,35 @@ def subprocess_kwargs() -> dict: return kwargs +def subprocess_run( + cmd: list[str] | str, timeout: int | None = None, check: bool = False, capture_output: bool = True, text: bool = True, **kwargs +) -> subprocess.CompletedProcess: + """ + Runs a command in a subprocess, applying safe default settings. + + The stdin of the subprocess is set to DEVNULL to avoid interference with the parent process' stdin; + this cannot be overridden by passing a different value for stdin in kwargs. + + :param cmd: the command to run, specified as a list of arguments or a string + :param timeout: the timeout in seconds for the command to complete; if None, no timeout is applied + :param check: if True, raises CalledProcessError if the command exits with a non-zero status + :param capture_output: if True, captures stdout and stderr; otherwise, they are not captured + :param text: if True, captures output as text (str); otherwise, captures as bytes + :return: a CompletedProcess instance containing information about the completed process + """ + kwargs = dict(kwargs) + kwargs.update(subprocess_kwargs()) + kwargs.update( + { + "timeout": timeout, + "capture_output": capture_output, + "text": text, + "stdin": subprocess.DEVNULL, # important to avoid interference with parent process' stdin + } + ) + return subprocess.run(cmd, check=check, **kwargs) + + def convert_shell_cmd(cmd: str | list[str]) -> str: """ Converts a command (specified as a list or string) to a format supported by subprocess calls with shell=True on the current platform,