Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from fiber import Fiber
2from fiber import current
3from fiber import suspend
4from . import OsError
5from .utils import to_utf8
7c"""source-before-namespace
8#include "cpp/subprocess.hpp"
9"""
11c"""
12class RunData {
13public:
14 const char *command_p;
15 bool capture_output;
16 struct subprocess_result_t *result_p;
17 mys::shared_ptr<Fiber> fiber;
18};
20static void run_work(uv_work_t *request_p)
21{
22 RunData *run_data_p = (RunData *)request_p->data;
24 if (run_data_p->capture_output) {
25 run_data_p->result_p = subprocess_exec_output(run_data_p->command_p);
26 } else {
27 run_data_p->result_p = subprocess_exec(run_data_p->command_p);
28 }
29}
31static void run_completed(uv_work_t *request_p, int status)
32{
33 RunData *run_data_p = (RunData *)request_p->data;
35 resume(run_data_p->fiber);
36}
37"""
39class CompletedProcess:
40 exit_code: i64
41 stdout: bytes
42 stderr: bytes
44func run(command: string,
45 check: bool = True,
46 capture_output: bool = True) -> CompletedProcess:
47 """Run given command in a subprocess and return it's exit code and
48 output once completed.
50 Raises an error if ``check`` is ``True`` and the subprocess exit code is
51 not zero.
53 Captures stdout and stderr if ``capture_output`` is ``True``.
55 """
57 command_utf8 = to_utf8(command)
58 completed_process = CompletedProcess(1, b"", b"")
59 ok = True
61 c"""
62 struct subprocess_result_t *result_p;
63 uv_work_t request;
64 RunData run_data;
66 run_data.command_p = (const char *)command_utf8.m_bytes->data();
67 run_data.capture_output = capture_output;
68 run_data.result_p = NULL;
69 run_data.fiber = current();
70 request.data = &run_data;
72 uv_queue_work(uv_default_loop(), &request, run_work, run_completed);
73 suspend();
75 if (run_data.result_p != NULL) {
76 completed_process->exit_code = run_data.result_p->exit_code;
78 for (int i = 0; i < run_data.result_p->stdout.length; i++) {
79 completed_process->stdout += run_data.result_p->stdout.buf_p[i];
80 }
82 for (int i = 0; i < run_data.result_p->stderr.length; i++) {
83 completed_process->stderr += run_data.result_p->stderr.buf_p[i];
84 }
86 subprocess_result_free(run_data.result_p);
87 } else {
88 ok = false;
89 }
90 """
92 if not ok:
93 raise OsError("Subprocess could not be started.")
95 if check and completed_process.exit_code != 0:
96 raise OsError(
97 f"'{command}' failed with exit code {completed_process.exit_code}.")
99 return completed_process
101test run():
102 proc = run("echo hej")
103 assert proc.exit_code == 0
104 assert string(proc.stdout) == "hej\n"
105 assert string(proc.stderr) == ""
107test run_command_not_found():
108 proc = run("mys-999", check=False)
109 assert proc.exit_code != 0
110 assert string(proc.stdout) == ""
111 assert "not found" in string(proc.stderr)
113test run_command_not_found_no_check():
114 try:
115 run("mys-999")
116 assert False
117 except OsError as error:
118 assert "'mys-999' failed with exit code " in error.message
120test run_dont_capture_output():
121 proc = run("echo hej", capture_output=False)
122 assert proc.exit_code == 0
123 assert string(proc.stdout) == ""
124 assert string(proc.stderr) == ""