@@ -43,6 +43,14 @@ def yaml_dump(o: Any) -> str:
4343 )
4444
4545
46+ def force_bytes (exc : Any ) -> bytes :
47+ with contextlib .suppress (TypeError ):
48+ return bytes (exc )
49+ with contextlib .suppress (Exception ):
50+ return str (exc ).encode ()
51+ return f'<unprintable { type (exc ).__name__ } object>' .encode ()
52+
53+
4654@contextlib .contextmanager
4755def clean_path_on_failure (path : str ) -> Generator [None , None , None ]:
4856 """Cleans up the directory on an exceptional failure."""
@@ -120,6 +128,10 @@ def _setdefault_kwargs(kwargs: Dict[str, Any]) -> None:
120128 kwargs .setdefault (arg , subprocess .PIPE )
121129
122130
131+ def _oserror_to_output (e : OSError ) -> Tuple [int , bytes , None ]:
132+ return 1 , force_bytes (e ).rstrip (b'\n ' ) + b'\n ' , None
133+
134+
123135def cmd_output_b (
124136 * cmd : str ,
125137 retcode : Optional [int ] = 0 ,
@@ -132,9 +144,13 @@ def cmd_output_b(
132144 except parse_shebang .ExecutableNotFoundError as e :
133145 returncode , stdout_b , stderr_b = e .to_output ()
134146 else :
135- proc = subprocess .Popen (cmd , ** kwargs )
136- stdout_b , stderr_b = proc .communicate ()
137- returncode = proc .returncode
147+ try :
148+ proc = subprocess .Popen (cmd , ** kwargs )
149+ except OSError as e :
150+ returncode , stdout_b , stderr_b = _oserror_to_output (e )
151+ else :
152+ stdout_b , stderr_b = proc .communicate ()
153+ returncode = proc .returncode
138154
139155 if retcode is not None and retcode != returncode :
140156 raise CalledProcessError (returncode , cmd , retcode , stdout_b , stderr_b )
@@ -205,7 +221,11 @@ def cmd_output_p(
205221 with open (os .devnull ) as devnull , Pty () as pty :
206222 assert pty .r is not None
207223 kwargs .update ({'stdin' : devnull , 'stdout' : pty .w , 'stderr' : pty .w })
208- proc = subprocess .Popen (cmd , ** kwargs )
224+ try :
225+ proc = subprocess .Popen (cmd , ** kwargs )
226+ except OSError as e :
227+ return _oserror_to_output (e )
228+
209229 pty .close_w ()
210230
211231 buf = b''
0 commit comments