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. """ specs = cmds_to_specs(cmds, captured=captured) captured = specs[-1].captured procs = [] proc = pipeline_group = None for spec in specs: starttime = time.time() proc = spec.run(pipeline_group=pipeline_group) procs.append(proc) if ON_POSIX and pipeline_group is None and \ spec.cls is subprocess.Popen: pipeline_group = proc.pid if not spec.is_proxy: add_job({ 'cmds': cmds, 'pids': [i.pid for i in procs], 'obj': proc, 'bg': spec.background, }) if _should_set_title(captured=captured): # set title here to get currently executing command pause_call_resume(proc, builtins.__xonsh_shell__.settitle) # create command or return if backgrounding. if spec.background: return if captured == 'hiddenobject': command = HiddenCommandPipeline(specs, procs, starttime=starttime, captured=captured) else: command = CommandPipeline(specs, procs, starttime=starttime, captured=captured) # now figure out what we should return. if captured == 'stdout': command.end() return command.output elif captured == 'object': return command elif captured == 'hiddenobject': command.end() return command else: command.end() return
def run_subproc(cmds, captured=False, envs=None): """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. """ if builtins.__xonsh__.env.get("XONSH_TRACE_SUBPROC"): print("TRACE SUBPROC: %s" % str(cmds), file=sys.stderr) specs = cmds_to_specs(cmds, captured=captured, envs=envs) captured = specs[-1].captured if captured == "hiddenobject": command = HiddenCommandPipeline(specs) else: command = CommandPipeline(specs) proc = command.proc background = command.spec.background if not all(x.is_proxy for x in specs): add_job( { "cmds": cmds, "pids": [i.pid for i in command.procs], "obj": proc, "bg": background, "pipeline": command, "pgrp": command.term_pgid, } ) if _should_set_title(captured=captured): # set title here to get currently executing command pause_call_resume(proc, builtins.__xonsh__.shell.settitle) else: # for some reason, some programs are in a stopped state when the flow # reaches this point, hence a SIGCONT should be sent to `proc` to make # sure that the shell doesn't hang. This `pause_call_resume` invocation # does this pause_call_resume(proc, int) # create command or return if backgrounding. if background: return # now figure out what we should return. if captured == "stdout": command.end() return command.output elif captured == "object": return command elif captured == "hiddenobject": command.end() return command else: command.end() return
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. """ specs = cmds_to_specs(cmds, captured=captured) captured = specs[-1].captured if captured == 'hiddenobject': command = HiddenCommandPipeline(specs) else: command = CommandPipeline(specs) proc = command.proc background = command.spec.background if not all(x.is_proxy for x in specs): add_job({ 'cmds': cmds, 'pids': [i.pid for i in command.procs], 'obj': proc, 'bg': background, 'pipeline': command, 'pgrp': command.term_pgid, }) if _should_set_title(captured=captured): # set title here to get currently executing command pause_call_resume(proc, builtins.__xonsh_shell__.settitle) # create command or return if backgrounding. if background: return # now figure out what we should return. if captured == 'stdout': command.end() return command.output elif captured == 'object': return command elif captured == 'hiddenobject': command.end() return command else: command.end() return
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 and hasattr(builtins, '__xonsh_shell__')): # set title here to get current command running pause_call_resume(prev_proc, builtins.__xonsh_shell__.settitle) 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)