22
22
23
23
24
24
def has_errors (output ):
25
- if isinstance (output , str ):
26
- output = output .encode (get_default_encoding ())
27
- return any (marker in output for marker in error_markers )
25
+ if output :
26
+ if isinstance (output , str ):
27
+ output = output .encode (get_default_encoding ())
28
+ return any (marker in output for marker in error_markers )
29
+ return False
28
30
29
31
30
32
class LocalOperations (OsOperations ):
@@ -38,32 +40,6 @@ def __init__(self, conn_params=None):
38
40
self .remote = False
39
41
self .username = conn_params .username or self .get_user ()
40
42
41
- @staticmethod
42
- def _run_command (cmd , shell , input , stdin , stdout , stderr , timeout , encoding , temp_file = None , get_process = None ):
43
- """Execute a command and return the process."""
44
- if temp_file is not None :
45
- stdout = stdout or temp_file
46
- stderr = stderr or subprocess .STDOUT
47
- else :
48
- stdout = stdout or subprocess .PIPE
49
- stderr = stderr or subprocess .PIPE
50
-
51
- process = subprocess .Popen (
52
- cmd ,
53
- shell = shell ,
54
- stdin = stdin or subprocess .PIPE if input is not None else None ,
55
- stdout = stdout ,
56
- stderr = stderr ,
57
- )
58
-
59
- if get_process :
60
- return None , process
61
- try :
62
- return process .communicate (input = input .encode (encoding ) if input else None , timeout = timeout ), process
63
- except subprocess .TimeoutExpired :
64
- process .kill ()
65
- raise ExecUtilException ("Command timed out after {} seconds." .format (timeout ))
66
-
67
43
@staticmethod
68
44
def _raise_exec_exception (message , command , exit_code , output ):
69
45
"""Raise an ExecUtilException."""
@@ -72,105 +48,72 @@ def _raise_exec_exception(message, command, exit_code, output):
72
48
exit_code = exit_code ,
73
49
out = output )
74
50
75
- def exec_command (self , cmd , wait_exit = False , verbose = False ,
76
- expect_error = False , encoding = None , shell = False , text = False ,
77
- input = None , stdin = None , stdout = None , stderr = None ,
78
- get_process = None , timeout = None ):
79
- """
80
- Execute a command in a subprocess.
81
-
82
- Args:
83
- - cmd: The command to execute.
84
- - wait_exit: Whether to wait for the subprocess to exit before returning.
85
- - verbose: Whether to return verbose output.
86
- - expect_error: Whether to raise an error if the subprocess exits with an error status.
87
- - encoding: The encoding to use for decoding the subprocess output.
88
- - shell: Whether to use shell when executing the subprocess.
89
- - text: Whether to return str instead of bytes for the subprocess output.
90
- - input: The input to pass to the subprocess.
91
- - stdout: The stdout to use for the subprocess.
92
- - stderr: The stderr to use for the subprocess.
93
- - proc: The process to use for subprocess creation.
94
- :return: The output of the subprocess.
95
- """
96
- if os .name == 'nt' :
97
- return self ._exec_command_windows (cmd , wait_exit = wait_exit , verbose = verbose ,
98
- expect_error = expect_error , encoding = encoding , shell = shell , text = text ,
99
- input = input , stdin = stdin , stdout = stdout , stderr = stderr ,
100
- get_process = get_process , timeout = timeout )
101
- else :
51
+ @staticmethod
52
+ def _process_output (encoding , temp_file_path ):
53
+ """Process the output of a command from a temporary file."""
54
+ with open (temp_file_path , 'rb' ) as temp_file :
55
+ output = temp_file .read ()
56
+ if encoding :
57
+ output = output .decode (encoding )
58
+ return output , None # In Windows stderr writing in stdout
59
+
60
+ def _run_command (self , cmd , shell , input , stdin , stdout , stderr , get_process , timeout , encoding ):
61
+ """Execute a command and return the process and its output."""
62
+ if os .name == 'nt' and stdout is None : # Windows
63
+ with tempfile .NamedTemporaryFile (mode = 'w+b' , delete = False ) as temp_file :
64
+ stdout = temp_file
65
+ stderr = subprocess .STDOUT
66
+ process = subprocess .Popen (
67
+ cmd ,
68
+ shell = shell ,
69
+ stdin = stdin or subprocess .PIPE if input is not None else None ,
70
+ stdout = stdout ,
71
+ stderr = stderr ,
72
+ )
73
+ if get_process :
74
+ return process , None , None
75
+ temp_file_path = temp_file .name
76
+
77
+ # Wait process finished
78
+ process .wait ()
79
+
80
+ output , error = self ._process_output (encoding , temp_file_path )
81
+ return process , output , error
82
+ else : # Other OS
102
83
process = subprocess .Popen (
103
84
cmd ,
104
85
shell = shell ,
105
- stdin = stdin ,
106
- stdout = stdout ,
107
- stderr = stderr ,
86
+ stdin = stdin or subprocess . PIPE if input is not None else None ,
87
+ stdout = stdout or subprocess . PIPE ,
88
+ stderr = stderr or subprocess . PIPE ,
108
89
)
109
90
if get_process :
110
- return process
111
-
91
+ return process , None , None
112
92
try :
113
- result , error = process .communicate (input , timeout = timeout )
93
+ output , error = process .communicate (input = input .encode (encoding ) if input else None , timeout = timeout )
94
+ if encoding :
95
+ output = output .decode (encoding )
96
+ error = error .decode (encoding )
97
+ return process , output , error
114
98
except subprocess .TimeoutExpired :
115
99
process .kill ()
116
100
raise ExecUtilException ("Command timed out after {} seconds." .format (timeout ))
117
- exit_status = process .returncode
118
101
119
- error_found = exit_status != 0 or has_errors (error )
120
-
121
- if encoding :
122
- result = result .decode (encoding )
123
- error = error .decode (encoding )
124
-
125
- if expect_error :
126
- raise Exception (result , error )
127
-
128
- if exit_status != 0 or error_found :
129
- if exit_status == 0 :
130
- exit_status = 1
131
- self ._raise_exec_exception ('Utility exited with non-zero code. Error `{}`' , cmd , exit_status , result )
132
- if verbose :
133
- return exit_status , result , error
134
- else :
135
- return result
102
+ def exec_command (self , cmd , wait_exit = False , verbose = False , expect_error = False , encoding = None , shell = False ,
103
+ text = False , input = None , stdin = None , stdout = None , stderr = None , get_process = False , timeout = None ):
104
+ """
105
+ Execute a command in a subprocess and handle the output based on the provided parameters.
106
+ """
107
+ process , output , error = self ._run_command (cmd , shell , input , stdin , stdout , stderr , get_process , timeout , encoding )
108
+ if get_process :
109
+ return process
110
+ if process .returncode != 0 or (has_errors (error ) and not expect_error ):
111
+ self ._raise_exec_exception ('Utility exited with non-zero code. Error `{}`' , cmd , process .returncode , error )
136
112
137
- @staticmethod
138
- def _process_output (process , encoding , temp_file = None ):
139
- """Process the output of a command."""
140
- if temp_file is not None :
141
- temp_file .seek (0 )
142
- output = temp_file .read ()
113
+ if verbose :
114
+ return process .returncode , output , error
143
115
else :
144
- output = process .stdout .read ()
145
-
146
- if encoding :
147
- output = output .decode (encoding )
148
-
149
- return output
150
-
151
- def _exec_command_windows (self , cmd , wait_exit = False , verbose = False ,
152
- expect_error = False , encoding = None , shell = False , text = False ,
153
- input = None , stdin = None , stdout = None , stderr = None ,
154
- get_process = None , timeout = None ):
155
- with tempfile .NamedTemporaryFile (mode = 'w+b' ) as temp_file :
156
- _ , process = self ._run_command (cmd , shell , input , stdin , stdout , stderr , timeout , encoding , temp_file , get_process )
157
- if get_process :
158
- return process
159
- result = self ._process_output (process , encoding , temp_file )
160
-
161
- if process .returncode != 0 or has_errors (result ):
162
- if process .returncode == 0 :
163
- process .returncode = 1
164
- if expect_error :
165
- if verbose :
166
- return process .returncode , result , result
167
- else :
168
- return result
169
- else :
170
- self ._raise_exec_exception ('Utility exited with non-zero code. Error `{}`' , cmd , process .returncode ,
171
- result )
172
-
173
- return (process .returncode , result , result ) if verbose else result
116
+ return output
174
117
175
118
# Environment setup
176
119
def environ (self , var_name ):
0 commit comments