Hide keyboard shortcuts

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 

6 

7c"""source-before-namespace 

8#include "cpp/subprocess.hpp" 

9""" 

10 

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}; 

19 

20static void run_work(uv_work_t *request_p) 

21{ 

22 RunData *run_data_p = (RunData *)request_p->data; 

23 

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} 

30 

31static void run_completed(uv_work_t *request_p, int status) 

32{ 

33 RunData *run_data_p = (RunData *)request_p->data; 

34 

35 resume(run_data_p->fiber); 

36} 

37""" 

38 

39class CompletedProcess: 

40 exit_code: i64 

41 stdout: bytes 

42 stderr: bytes 

43 

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. 

49 

50 Raises an error if ``check`` is ``True`` and the subprocess exit code is 

51 not zero. 

52 

53 Captures stdout and stderr if ``capture_output`` is ``True``. 

54 

55 """ 

56 

57 command_utf8 = to_utf8(command) 

58 completed_process = CompletedProcess(1, b"", b"") 

59 ok = True 

60 

61 c""" 

62 struct subprocess_result_t *result_p; 

63 uv_work_t request; 

64 RunData run_data; 

65 

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; 

71 

72 uv_queue_work(uv_default_loop(), &request, run_work, run_completed); 

73 suspend(); 

74 

75 if (run_data.result_p != NULL) { 

76 completed_process->exit_code = run_data.result_p->exit_code; 

77 

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 } 

81 

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 } 

85 

86 subprocess_result_free(run_data.result_p); 

87 } else { 

88 ok = false; 

89 } 

90 """ 

91 

92 if not ok: 

93 raise OsError("Subprocess could not be started.") 

94 

95 if check and completed_process.exit_code != 0: 

96 raise OsError( 

97 f"'{command}' failed with exit code {completed_process.exit_code}.") 

98 

99 return completed_process 

100 

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) == "" 

106 

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) 

112 

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 

119 

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) == ""