def run_subproc(cmds, captured=False): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ env = builtins.__xonsh_env__ background = False procinfo = {} if cmds[-1] == '&': background = True cmds = cmds[:-1] _pipeline_group = None write_target = None last_cmd = len(cmds) - 1 procs = [] prev_proc = None _capture_streams = captured in {'stdout', 'object'} for ix, cmd in enumerate(cmds): starttime = time.time() procinfo['args'] = list(cmd) stdin = None stderr = None if isinstance(cmd, str): continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'][-1] procinfo['stdin_redirect'] = streams['stdin'][:-1] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output _stdout_name = None _stderr_name = None if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'][-1] procinfo['stdout_redirect'] = streams['stdout'][:-1] elif ix != last_cmd: stdout = subprocess.PIPE elif _capture_streams: _nstdout = stdout = tempfile.NamedTemporaryFile(delete=False) _stdout_name = stdout.name elif builtins.__xonsh_stdout_uncaptured__ is not None: stdout = builtins.__xonsh_stdout_uncaptured__ else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'][-1] procinfo['stderr_redirect'] = streams['stderr'][:-1] elif captured == 'object' and ix == last_cmd: _nstderr = stderr = tempfile.NamedTemporaryFile(delete=False) _stderr_name = stderr.name elif builtins.__xonsh_stderr_uncaptured__ is not None: stderr = builtins.__xonsh_stderr_uncaptured__ uninew = (ix == last_cmd) and (not _capture_streams) # find alias if callable(cmd[0]): alias = cmd[0] else: alias = builtins.aliases.get(cmd[0], None) procinfo['alias'] = alias # find binary location, if not callable if alias is None: binary_loc = locate_binary(cmd[0]) elif not callable(alias): binary_loc = locate_binary(alias[0]) # implement AUTO_CD if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmd) == 1 and os.path.isdir(cmd[0]) and binary_loc is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) if callable(alias): aliased_cmd = alias else: if alias is not None: aliased_cmd = alias + cmd[1:] else: aliased_cmd = cmd if binary_loc is not None: try: aliased_cmd = get_script_subproc_command(binary_loc, aliased_cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) _stdin_file = None if (stdin is not None and env.get('XONSH_STORE_STDIN') and captured == 'object' and __xonsh_commands_cache__.lazy_locate_binary('cat') and __xonsh_commands_cache__.lazy_locate_binary('tee')): _stdin_file = tempfile.NamedTemporaryFile() cproc = subprocess.Popen(['cat'], stdin=stdin, stdout=subprocess.PIPE) tproc = subprocess.Popen(['tee', _stdin_file.name], stdin=cproc.stdout, stdout=subprocess.PIPE) stdin = tproc.stdout if callable(aliased_cmd): prev_is_proxy = True bgable = getattr(aliased_cmd, '__xonsh_backgroundable__', True) numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy if bgable else SimpleForegroundProcProxy elif numargs == 4: cls = ProcProxy if bgable else ForegroundProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False usetee = ((stdout is None) and (not background) and env.get('XONSH_STORE_STDOUT', False)) cls = TeePTYProc if usetee else subprocess.Popen subproc_kwargs = {} if ON_POSIX and cls is subprocess.Popen: def _subproc_pre(): if _pipeline_group is None: os.setpgrp() else: os.setpgid(0, _pipeline_group) signal.signal(signal.SIGTSTP, lambda n, f: signal.pause()) subproc_kwargs['preexec_fn'] = _subproc_pre denv = env.detype() if ON_WINDOWS: # Over write prompt variable as xonsh's $PROMPT does # not make much sense for other subprocs denv['PROMPT'] = '$P$G' try: proc = cls(aliased_cmd, universal_newlines=uninew, env=denv, stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) sug = suggest_commands(cmd, env, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, env, builtins.aliases) raise XonshError(e) procs.append(proc) prev_proc = proc if ON_POSIX and cls is subprocess.Popen and _pipeline_group is None: _pipeline_group = prev_proc.pid if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if (env.get('XONSH_INTERACTIVE') and not env.get('XONSH_STORE_STDOUT') and not _capture_streams): # set title here to get current command running try: builtins.__xonsh_shell__.settitle() except AttributeError: pass if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass hist = builtins.__xonsh_history__ hist.last_cmd_rtn = prev_proc.returncode # get output output = b'' if write_target is None: if _stdout_name is not None: with open(_stdout_name, 'rb') as stdoutfile: output = stdoutfile.read() try: _nstdout.close() except Exception: pass os.unlink(_stdout_name) elif prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if _capture_streams: # to get proper encoding from Popen, we have to # use a byte stream and then implement universal_newlines here output = output.decode(encoding=env.get('XONSH_ENCODING'), errors=env.get('XONSH_ENCODING_ERRORS')) output = output.replace('\r\n', '\n') else: hist.last_cmd_out = output if captured == 'object': # get stderr as well named = _stderr_name is not None unnamed = prev_proc.stderr not in {None, sys.stderr} if named: with open(_stderr_name, 'rb') as stderrfile: errout = stderrfile.read() try: _nstderr.close() except Exception: pass os.unlink(_stderr_name) elif unnamed: errout = prev_proc.stderr.read() if named or unnamed: errout = errout.decode(encoding=env.get('XONSH_ENCODING'), errors=env.get('XONSH_ENCODING_ERRORS')) errout = errout.replace('\r\n', '\n') procinfo['stderr'] = errout if getattr(prev_proc, 'signal', None): sig, core = prev_proc.signal sig_str = SIGNAL_MESSAGES.get(sig) if sig_str: if core: sig_str += ' (core dumped)' print(sig_str, file=sys.stderr) if (not prev_is_proxy and hist.last_cmd_rtn is not None and hist.last_cmd_rtn > 0 and env.get('RAISE_SUBPROC_ERROR')): raise subprocess.CalledProcessError(hist.last_cmd_rtn, aliased_cmd, output=output) if captured == 'stdout': return output elif captured is not False: procinfo['executed_cmd'] = aliased_cmd procinfo['pid'] = prev_proc.pid procinfo['returncode'] = prev_proc.returncode procinfo['timestamp'] = (starttime, time.time()) if captured == 'object': procinfo['stdout'] = output if _stdin_file is not None: _stdin_file.seek(0) procinfo['stdin'] = _stdin_file.read().decode() _stdin_file.close() return CompletedCommand(**procinfo) else: return HiddenCompletedCommand(**procinfo)
def run_subproc(cmds, captured=True): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV background = False if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None last_cmd = len(cmds) - 1 prev = None procs = [] prev_proc = None for ix, cmd in enumerate(cmds): stdin = None stdout = None stderr = None if isinstance(cmd, string_types): prev = cmd continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'] elif captured or ix != last_cmd: stdout = PIPE else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'] uninew = ix == last_cmd alias = builtins.aliases.get(cmd[0], None) if callable(alias): aliased_cmd = alias else: if alias is not None: cmd = alias + cmd[1:] n = _get_runnable_name(cmd[0]) if n is None: aliased_cmd = cmd else: try: aliased_cmd = get_script_subproc_command(n, cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) if callable(aliased_cmd): prev_is_proxy = True numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy elif numargs == 4: cls = ProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False subproc_kwargs = {} if ON_POSIX: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = Popen(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format( cmd) sug = suggest_commands(cmd, ENV, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev = None prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() if write_target is None: # get output output = '' if prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if captured: return output elif last_stdout not in (PIPE, None, sys.stdout): last_stdout.close()
def run_subproc(cmds, captured=True): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV background = False if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None last_cmd = len(cmds) - 1 procs = [] prev_proc = None for ix, cmd in enumerate(cmds): stdin = None stderr = None if isinstance(cmd, string_types): continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'] elif captured or ix != last_cmd: stdout = PIPE else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'] uninew = (ix == last_cmd) and (not captured) alias = builtins.aliases.get(cmd[0], None) if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmd) == 1 and os.path.isdir(cmd[0]) and locate_binary(cmd[0], cwd=None) is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) if callable(alias): aliased_cmd = alias else: if alias is not None: cmd = alias + cmd[1:] n = _get_runnable_name(cmd[0]) if n is None: aliased_cmd = cmd else: try: aliased_cmd = get_script_subproc_command(n, cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) if callable(aliased_cmd): prev_is_proxy = True numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy elif numargs == 4: cls = ProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False usetee = (stdout is None) and (not background) and \ ENV.get('XONSH_STORE_STDOUT', False) cls = TeePTYProc if usetee else Popen subproc_kwargs = {} if ON_POSIX and cls is Popen: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = cls(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) sug = suggest_commands(cmd, ENV, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if ENV.get('XONSH_INTERACTIVE') and not ENV.get('XONSH_STORE_STDOUT'): # set title here to get current command running try: builtins.__xonsh_shell__.settitle() except AttributeError: pass if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() hist = builtins.__xonsh_history__ hist.last_cmd_rtn = prev_proc.returncode if write_target is None: # get output output = b'' if prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if captured: # to get proper encoding from Popen, we have to # use a byte stream and then implement universal_newlines here output = output.decode(encoding=ENV.get('XONSH_ENCODING'), errors=ENV.get('XONSH_ENCODING_ERRORS')) output = output.replace('\r\n', '\n') return output else: hist.last_cmd_out = output
def iterraw(self): """Iterates through the last stdout, and returns the lines exactly as found. """ # get appropriate handles spec = self.spec proc = self.proc if proc is None: return timeout = builtins.__xonsh__.env.get("XONSH_PROC_FREQUENCY") # get the correct stdout stdout = proc.stdout if (stdout is None or spec.stdout is None or not safe_readable(stdout) ) and spec.captured_stdout is not None: stdout = spec.captured_stdout if hasattr(stdout, "buffer"): stdout = stdout.buffer if stdout is not None and not isinstance(stdout, self.nonblocking): stdout = NonBlockingFDReader(stdout.fileno(), timeout=timeout) if (not stdout or self.captured == "stdout" or not safe_readable(stdout) or not spec.threadable): # we get here if the process is not threadable or the # class is the real Popen PrevProcCloser(pipeline=self) task = xj.wait_for_active_job() if task is None or task["status"] != "stopped": proc.wait() self._endtime() if self.captured == "object": self.end(tee_output=False) elif self.captured == "hiddenobject" and stdout: b = stdout.read() lines = b.splitlines(keepends=True) yield from lines self.end(tee_output=False) elif self.captured == "stdout": b = stdout.read() s = self._decode_uninew(b, universal_newlines=True) self.lines = s.splitlines(keepends=True) return # get the correct stderr stderr = proc.stderr if (stderr is None or spec.stderr is None or not safe_readable(stderr) ) and spec.captured_stderr is not None: stderr = spec.captured_stderr if hasattr(stderr, "buffer"): stderr = stderr.buffer if stderr is not None and not isinstance(stderr, self.nonblocking): stderr = NonBlockingFDReader(stderr.fileno(), timeout=timeout) # read from process while it is running check_prev_done = len(self.procs) == 1 prev_end_time = None i = j = cnt = 1 while proc.poll() is None: if getattr(proc, "suspended", False): return elif getattr(proc, "in_alt_mode", False): time.sleep(0.1) # probably not leaving any time soon continue elif not check_prev_done: # In the case of pipelines with more than one command # we should give the commands a little time # to start up fully. This is particularly true for # GNU Parallel, which has a long startup time. pass elif self._prev_procs_done(): self._close_prev_procs() proc.prevs_are_closed = True break stdout_lines = safe_readlines(stdout, 1024) i = len(stdout_lines) if i != 0: yield from stdout_lines stderr_lines = safe_readlines(stderr, 1024) j = len(stderr_lines) if j != 0: self.stream_stderr(stderr_lines) if not check_prev_done: # if we are piping... if stdout_lines or stderr_lines: # see if we have some output. check_prev_done = True elif prev_end_time is None: # or see if we already know that the next-to-last # proc in the pipeline has ended. if self._prev_procs_done(): # if it has, record the time prev_end_time = time.time() elif time.time() - prev_end_time >= 0.1: # if we still don't have any output, even though the # next-to-last proc has finished, wait a bit to make # sure we have fully started up, etc. check_prev_done = True # this is for CPU usage if i + j == 0: cnt = min(cnt + 1, 1000) else: cnt = 1 time.sleep(timeout * cnt) # read from process now that it is over yield from safe_readlines(stdout) self.stream_stderr(safe_readlines(stderr)) proc.wait() self._endtime() yield from safe_readlines(stdout) self.stream_stderr(safe_readlines(stderr)) if self.captured == "object": self.end(tee_output=False)
def run_subproc(cmds, captured=False): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV background = False procinfo = {} if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None last_cmd = len(cmds) - 1 procs = [] prev_proc = None _capture_streams = captured in {'stdout', 'object'} for ix, cmd in enumerate(cmds): starttime = time.time() procinfo['args'] = list(cmd) stdin = None stderr = None if isinstance(cmd, str): continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'][-1] procinfo['stdin_redirect'] = streams['stdin'][:-1] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output _stdout_name = None _stderr_name = None if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'][-1] procinfo['stdout_redirect'] = streams['stdout'][:-1] elif ix != last_cmd: stdout = PIPE elif _capture_streams: _nstdout = stdout = tempfile.NamedTemporaryFile(delete=False) _stdout_name = stdout.name elif builtins.__xonsh_stdout_uncaptured__ is not None: stdout = builtins.__xonsh_stdout_uncaptured__ else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'][-1] procinfo['stderr_redirect'] = streams['stderr'][:-1] elif captured == 'object' and ix == last_cmd: _nstderr = stderr = tempfile.NamedTemporaryFile(delete=False) _stderr_name = stderr.name elif builtins.__xonsh_stderr_uncaptured__ is not None: stderr = builtins.__xonsh_stderr_uncaptured__ uninew = (ix == last_cmd) and (not _capture_streams) alias = builtins.aliases.get(cmd[0], None) procinfo['alias'] = alias if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmd) == 1 and os.path.isdir(cmd[0]) and locate_binary(cmd[0]) is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) if callable(alias): aliased_cmd = alias else: if alias is not None: cmd = alias + cmd[1:] n = locate_binary(cmd[0]) if n is None: aliased_cmd = cmd else: try: aliased_cmd = get_script_subproc_command(n, cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) _stdin_file = None if (stdin is not None and ENV.get('XONSH_STORE_STDIN') and captured == 'object' and 'cat' in __xonsh_commands_cache__ and 'tee' in __xonsh_commands_cache__): _stdin_file = tempfile.NamedTemporaryFile() cproc = Popen(['cat'], stdin=stdin, stdout=PIPE) tproc = Popen(['tee', _stdin_file.name], stdin=cproc.stdout, stdout=PIPE) stdin = tproc.stdout if callable(aliased_cmd): prev_is_proxy = True bgable = getattr(aliased_cmd, '__xonsh_backgroundable__', True) numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy if bgable else SimpleForegroundProcProxy elif numargs == 4: cls = ProcProxy if bgable else ForegroundProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False usetee = ((stdout is None) and (not background) and ENV.get('XONSH_STORE_STDOUT', False)) cls = TeePTYProc if usetee else Popen subproc_kwargs = {} if ON_POSIX and cls is Popen: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = cls(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) sug = suggest_commands(cmd, ENV, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if (ENV.get('XONSH_INTERACTIVE') and not ENV.get('XONSH_STORE_STDOUT') and not _capture_streams): # set title here to get current command running try: builtins.__xonsh_shell__.settitle() except AttributeError: pass if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() hist = builtins.__xonsh_history__ hist.last_cmd_rtn = prev_proc.returncode # get output output = b'' if write_target is None: if _stdout_name is not None: with open(_stdout_name, 'rb') as stdoutfile: output = stdoutfile.read() try: _nstdout.close() except: pass os.unlink(_stdout_name) elif prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if _capture_streams: # to get proper encoding from Popen, we have to # use a byte stream and then implement universal_newlines here output = output.decode(encoding=ENV.get('XONSH_ENCODING'), errors=ENV.get('XONSH_ENCODING_ERRORS')) output = output.replace('\r\n', '\n') else: hist.last_cmd_out = output if captured == 'object': # get stderr as well named = _stderr_name is not None unnamed = prev_proc.stderr not in {None, sys.stderr} if named: with open(_stderr_name, 'rb') as stderrfile: errout = stderrfile.read() try: _nstderr.close() except: pass os.unlink(_stderr_name) elif unnamed: errout = prev_proc.stderr.read() if named or unnamed: errout = errout.decode(encoding=ENV.get('XONSH_ENCODING'), errors=ENV.get('XONSH_ENCODING_ERRORS')) errout = errout.replace('\r\n', '\n') procinfo['stderr'] = errout if getattr(prev_proc, 'signal', None): sig, core = prev_proc.signal sig_str = SIGNAL_MESSAGES.get(sig) if sig_str: if core: sig_str += ' (core dumped)' print(sig_str, file=sys.stderr) if (not prev_is_proxy and hist.last_cmd_rtn is not None and hist.last_cmd_rtn > 0 and ENV.get('RAISE_SUBPROC_ERROR')): raise CalledProcessError(hist.last_cmd_rtn, aliased_cmd, output=output) if captured == 'stdout': return output elif captured is not False: procinfo['executed_cmd'] = aliased_cmd procinfo['pid'] = prev_proc.pid procinfo['returncode'] = prev_proc.returncode procinfo['timestamp'] = (starttime, time.time()) if captured == 'object': procinfo['stdout'] = output if _stdin_file is not None: _stdin_file.seek(0) procinfo['stdin'] = _stdin_file.read().decode() _stdin_file.close() return CompletedCommand(**procinfo) else: return HiddenCompletedCommand(**procinfo)
def run_subproc(cmds, captured=True): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV background = False if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None last_cmd = len(cmds) - 1 prev = None procs = [] prev_proc = None for ix, cmd in enumerate(cmds): stdin = None stdout = None stderr = None if isinstance(cmd, string_types): prev = cmd continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'] elif captured or ix != last_cmd: stdout = PIPE else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'] uninew = ix == last_cmd alias = builtins.aliases.get(cmd[0], None) if callable(alias): aliased_cmd = alias else: if alias is not None: cmd = alias + cmd[1:] n = _get_runnable_name(cmd[0]) if n is None: aliased_cmd = cmd else: try: aliased_cmd = get_script_subproc_command(n, cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) if callable(aliased_cmd): prev_is_proxy = True numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy elif numargs == 4: cls = ProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False subproc_kwargs = {} if ON_POSIX: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = Popen(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) sug = suggest_commands(cmd, ENV, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev = None prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() if write_target is None: # get output output = '' if prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if captured: return output elif last_stdout not in (PIPE, None, sys.stdout): last_stdout.close()
def run_subproc(cmds, captured=True): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV background = False if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None last_cmd = len(cmds) - 1 prev = None procs = [] prev_proc = None for ix, cmd in enumerate(cmds): stdin = None stdout = None stderr = None if isinstance(cmd, string_types): prev = cmd continue streams = {} while True: if len(cmd) >= 3 and _is_redirect(cmd[-2]): _redirect_io(streams, cmd[-2], cmd[-1]) cmd = cmd[:-2] elif len(cmd) >= 2 and _is_redirect(cmd[-1]): _redirect_io(streams, cmd[-1]) cmd = cmd[:-1] elif len(cmd) >= 3 and cmd[0] == '<': _redirect_io(streams, cmd[0], cmd[1]) cmd = cmd[2:] else: break # set standard input if 'stdin' in streams: if prev_proc is not None: raise XonshError('Multiple inputs for stdin') stdin = streams['stdin'] elif prev_proc is not None: stdin = prev_proc.stdout # set standard output if 'stdout' in streams: if ix != last_cmd: raise XonshError('Multiple redirects for stdout') stdout = streams['stdout'] elif captured or ix != last_cmd: stdout = PIPE else: stdout = None # set standard error if 'stderr' in streams: stderr = streams['stderr'] uninew = (ix == last_cmd) and (not captured) alias = builtins.aliases.get(cmd[0], None) if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmds)==1 and os.path.isdir(cmd[0]) and locate_binary(cmd[0], cwd=None) is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) if callable(alias): aliased_cmd = alias else: if alias is not None: cmd = alias + cmd[1:] n = _get_runnable_name(cmd[0]) if n is None: aliased_cmd = cmd else: try: aliased_cmd = get_script_subproc_command(n, cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) if callable(aliased_cmd): prev_is_proxy = True numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy elif numargs == 4: cls = ProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, stderr, universal_newlines=uninew) else: prev_is_proxy = False usetee = (stdout is None) and (not background) and \ ENV.get('XONSH_STORE_STDOUT', False) cls = TeePTYProc if usetee else Popen subproc_kwargs = {} if ON_POSIX and cls is Popen: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = cls(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, stderr=stderr, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) sug = suggest_commands(cmd, ENV, builtins.aliases) if len(sug.strip()) > 0: e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev = None prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() hist = builtins.__xonsh_history__ hist.last_cmd_rtn = prev_proc.returncode if write_target is None: # get output output = '' if prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if captured: # to get proper encoding from Popen, we have to # use a byte stream and then implement universal_newlines here output = output.decode(encoding=ENV.get('XONSH_ENCODING'), errors=ENV.get('XONSH_ENCODING_ERRORS')) output = output.replace('\r\n', '\n') return output else: hist.last_cmd_out = output
def run_subproc(cmds, captured=True): """Runs a subprocess, in its many forms. This takes a list of 'commands,' which may be a list of command line arguments or a string, representing a special connecting character. For example:: $ ls | grep wakka is represented by the following cmds:: [['ls'], '|', ['grep', 'wakka']] Lastly, the captured argument affects only the last real command. """ global ENV last_stdout = PIPE if captured else None background = False if cmds[-1] == '&': background = True cmds = cmds[:-1] write_target = None if len(cmds) >= 3 and cmds[-2] in WRITER_MODES: write_target = cmds[-1][0] write_mode = WRITER_MODES[cmds[-2]] cmds = cmds[:-2] if write_target is not None: try: last_stdout = open(write_target, write_mode) except FileNotFoundError: e = 'xonsh: {0}: no such file or directory' raise XonshError(e.format(write_target)) else: last_stdout = PIPE last_cmd = cmds[-1] prev = None procs = [] prev_proc = None for cmd in cmds: if isinstance(cmd, string_types): prev = cmd continue stdout = last_stdout if cmd is last_cmd else PIPE uninew = cmd is last_cmd alias = builtins.aliases.get(cmd[0], None) if _is_runnable_name(cmd[0]): try: aliased_cmd = get_script_subproc_command(cmd[0], cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) elif alias is None: aliased_cmd = cmd elif callable(alias): aliased_cmd = alias else: aliased_cmd = alias + cmd[1:] # compute stdin for subprocess if prev_proc is None: stdin = None else: stdin = prev_proc.stdout if callable(aliased_cmd): prev_is_proxy = True numargs = len(inspect.signature(aliased_cmd).parameters) if numargs == 2: cls = SimpleProcProxy elif numargs == 4: cls = ProcProxy else: e = 'Expected callable with 2 or 4 arguments, not {}' raise XonshError(e.format(numargs)) proc = cls(aliased_cmd, cmd[1:], stdin, stdout, None, universal_newlines=uninew) else: prev_is_proxy = False subproc_kwargs = {} if ON_POSIX: subproc_kwargs['preexec_fn'] = _subproc_pre try: proc = Popen(aliased_cmd, universal_newlines=uninew, env=ENV.detype(), stdin=stdin, stdout=stdout, **subproc_kwargs) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(aliased_cmd[0])) except FileNotFoundError: cmd = aliased_cmd[0] e = 'xonsh: subprocess mode: command not found: {0}'.format(cmd) e += '\n' + suggest_commands(cmd, ENV, builtins.aliases) raise XonshError(e) procs.append(proc) prev = None prev_proc = proc for proc in procs[:-1]: try: proc.stdout.close() except OSError: pass if not prev_is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': prev_proc, 'bg': background }) if background: return if prev_is_proxy: prev_proc.wait() wait_for_active_job() if write_target is None: # get output output = '' if prev_proc.stdout not in (None, sys.stdout): output = prev_proc.stdout.read() if captured: return output elif last_stdout not in (PIPE, None, sys.stdout): last_stdout.close()