-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsubprocess.py
More file actions
77 lines (65 loc) · 2.43 KB
/
Copy pathsubprocess.py
File metadata and controls
77 lines (65 loc) · 2.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Unified subprocess execution for CPPython plugins.
All plugin subprocess calls should go through :func:`run_subprocess` so that
stdout/stderr is captured, logged to the session log file, and hidden from
the terminal unless verbose mode is enabled.
"""
import subprocess
from logging import Logger
from pathlib import Path
def run_subprocess(
cmd: list[str],
*,
cwd: Path | str | None = None,
logger: Logger,
**kwargs,
) -> subprocess.CompletedProcess[str]:
"""Run a subprocess with captured output that is routed to the logger.
Stdout and stderr are always captured (never printed to the terminal
directly). Each non-empty line is logged at ``DEBUG`` level so it
appears in the session log file. On failure the full output is logged
at ``ERROR`` level and the :class:`subprocess.CalledProcessError` is
re-raised.
Args:
cmd: The command and arguments to execute.
cwd: Working directory for the subprocess.
logger: Logger instance used to record output.
**kwargs: Additional keyword arguments forwarded to
:func:`subprocess.run`. ``capture_output``, ``text``, and
``check`` are always overridden.
Returns:
The completed process result.
Raises:
subprocess.CalledProcessError: If the process exits with a non-zero
return code.
"""
# Force capture so output never leaks to the terminal
kwargs.pop('capture_output', None)
kwargs.pop('text', None)
kwargs.pop('check', None)
logger.debug('Running: %s', ' '.join(cmd))
try:
result = subprocess.run(
cmd,
cwd=cwd,
capture_output=True,
text=True,
check=True,
**kwargs,
)
except subprocess.CalledProcessError as exc:
# Log everything we have so the session log file is useful
if exc.stdout:
for line in exc.stdout.splitlines():
logger.error('%s', line)
if exc.stderr:
for line in exc.stderr.splitlines():
logger.error('%s', line)
raise
# Log successful output at debug level
if result.stdout:
for line in result.stdout.splitlines():
logger.debug('%s', line)
if result.stderr:
for line in result.stderr.splitlines():
logger.debug('%s', line)
return result