Skip to content

Commit e908f47

Browse files
authored
feat: Add user friendly message when the exectuable isn't found (#219)
Signed-off-by: Justin Blagden <[email protected]>
1 parent cffc1fb commit e908f47

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

src/openjd/adaptor_runtime/process/_logging_subprocess.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from __future__ import annotations
55

66
import logging
7+
import shutil
78
import signal
89
import subprocess
910
import uuid
@@ -21,7 +22,11 @@
2122

2223

2324
class LoggingSubprocess(object):
24-
"""A process whose stdout/stderr lines are sent to a configurable logger"""
25+
"""A process whose stdout/stderr lines are sent to a configurable logger
26+
27+
Raises:
28+
FileNotFoundError: When the executable the adaptor is set to run cannot be found.
29+
"""
2530

2631
_logger: logging.Logger
2732
_process: subprocess.Popen
@@ -64,7 +69,27 @@ def __init__(
6469
# In Windows, this is required for signal. SIGBREAK will be sent to the entire process group.
6570
# Without this one, current process will also get the SIGBREAK and may react incorrectly.
6671
popen_params.update(creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # type: ignore[attr-defined]
67-
self._process = subprocess.Popen(**popen_params)
72+
73+
try:
74+
self._process = subprocess.Popen(**popen_params)
75+
76+
except FileNotFoundError as fnf_error:
77+
# In ManagedProcess we prepend the executable to the list of arguments before creating a LoggingSubprocess
78+
executable = args[0]
79+
exe_path = shutil.which(executable)
80+
81+
# If we didn't find the executable found by which
82+
if exe_path is not None:
83+
raise FileNotFoundError(
84+
f"Could not find executable at: {exe_path} using alias {executable}\n"
85+
f"Error:{fnf_error}"
86+
)
87+
88+
raise FileNotFoundError(
89+
f"Could not find the specified command: {executable}\n"
90+
f"Ensure the command is available from the PATH environment variable.\n"
91+
f"Error:{fnf_error}"
92+
)
6893

6994
if not self._process.stdout: # pragma: no cover
7095
raise RuntimeError("process stdout not set")

test/openjd/adaptor_runtime/integ/process/test_integration_logging_subprocess.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ def test_startup_directory(self, startup_dir: str | None, caplog):
113113
def test_startup_directory_empty_posix(self):
114114
"""When calling LoggingSubprocess with an empty cwd, FileNotFoundError will be raised."""
115115
args = ["pwd"]
116-
with pytest.raises(FileNotFoundError) as excinfo:
116+
with pytest.raises(FileNotFoundError) as exc_info:
117117
LoggingSubprocess(args=args, startup_directory="")
118-
assert "[Errno 2] No such file or directory: ''" in str(excinfo.value)
118+
assert "[Errno 2] No such file or directory: ''" in str(exc_info.value)
119119

120120
@pytest.mark.skipif(not OSName.is_windows(), reason="Only run this test in Windows.")
121121
def test_startup_directory_empty_windows(self):
@@ -151,6 +151,13 @@ def test_log_levels(self, log_level: int, caplog):
151151

152152
assert any(r.message == message and r.levelno == _STDERR_LEVEL for r in records)
153153

154+
def test_executable_not_found(self):
155+
"""When calling LoggingSubprocess with a missing executable, FileNotFoundError will be raised"""
156+
args = ["missing_executable"]
157+
with pytest.raises(FileNotFoundError) as exc_info:
158+
LoggingSubprocess(args=args)
159+
assert "Could not find the specified command" in str(exc_info.value)
160+
154161

155162
class TestIntegrationRegexHandler(object):
156163
"""Integration tests for LoggingSubprocess"""

0 commit comments

Comments
 (0)