def _run_binary(self, kwargs): if not self.cmd[0]: raise xt.XonshError("xonsh: subprocess mode: command is empty") bufsize = 1 try: if xp.ON_WINDOWS and self.binary_loc is not None: # launch process using full paths (https://bugs.python.org/issue8557) cmd = [self.binary_loc] + self.cmd[1:] else: cmd = self.cmd p = self.cls(cmd, bufsize=bufsize, **kwargs) except PermissionError: e = "xonsh: subprocess mode: permission denied: {0}" raise xt.XonshError(e.format(self.cmd[0])) except FileNotFoundError: cmd0 = self.cmd[0] if len(self.cmd) == 1 and cmd0.endswith("?"): with contextlib.suppress(OSError): return self.cls(["man", cmd0.rstrip("?")], bufsize=bufsize, **kwargs) e = "xonsh: subprocess mode: command not found: {0}".format(cmd0) env = XSH.env sug = xt.suggest_commands(cmd0, env) if len(sug.strip()) > 0: e += "\n" + xt.suggest_commands(cmd0, env) raise xt.XonshError(e) return p
def _redirect_streams(r, loc=None): """Returns stdin, stdout, stderr tuple of redirections.""" stdin = stdout = stderr = None no_ampersand = r.replace("&", "") # special case of redirecting stderr to stdout if no_ampersand in _E2O_MAP: stderr = subprocess.STDOUT return stdin, stdout, stderr elif no_ampersand in _O2E_MAP: stdout = 2 # using 2 as a flag, rather than using a file object return stdin, stdout, stderr # get streams orig, mode, dest = _parse_redirects(r) if mode == "r": stdin = safe_open(loc, mode) elif mode in _WRITE_MODES: if orig in _REDIR_ALL: stdout = stderr = safe_open(loc, mode) elif orig in _REDIR_OUT: stdout = safe_open(loc, mode) elif orig in _REDIR_ERR: stderr = safe_open(loc, mode) else: raise xt.XonshError(f"Unrecognized redirection command: {r}") else: raise xt.XonshError(f"Unrecognized redirection command: {r}") return stdin, stdout, stderr
def safe_open(fname, mode, buffering=-1): """Safely attempts to open a file in for xonsh subprocs.""" # file descriptors try: return io.open(fname, mode, buffering=buffering) except PermissionError: raise xt.XonshError(f"xonsh: {fname}: permission denied") except FileNotFoundError: raise xt.XonshError(f"xonsh: {fname}: no such file or directory") except Exception: raise xt.XonshError(f"xonsh: {fname}: unable to open file")
def cmds_to_specs(cmds, captured=False, envs=None): """Converts a list of cmds to a list of SubprocSpec objects that are ready to be executed. """ # first build the subprocs independently and separate from the redirects i = 0 specs = [] redirects = [] for i, cmd in enumerate(cmds): if isinstance(cmd, str): redirects.append(cmd) else: env = envs[i] if envs is not None else None spec = SubprocSpec.build(cmd, captured=captured, env=env) spec.pipeline_index = i specs.append(spec) i += 1 # now modify the subprocs based on the redirects. for i, redirect in enumerate(redirects): if redirect == "|": # these should remain integer file descriptors, and not Python # file objects since they connect processes. r, w = os.pipe() specs[i].stdout = w specs[i + 1].stdin = r elif redirect == "&" and i == len(redirects) - 1: specs[i].background = True else: raise xt.XonshError(f"unrecognized redirect {redirect!r}") # Apply boundary conditions _update_last_spec(specs[-1]) return specs
def stderr(self, value): if self._stderr is None: self._stderr = value elif value is None: pass else: safe_close(value) msg = "Multiple redirections for stderr for {0!r}" msg = msg.format(" ".join(self.args)) raise xt.XonshError(msg)
def stdin(self, value): if self._stdin is None: self._stdin = value elif value is None: pass else: safe_close(value) msg = "Multiple inputs for stdin for {0!r}" msg = msg.format(" ".join(self.args)) raise xt.XonshError(msg)
def _run_binary(self, kwargs): try: bufsize = 1 p = self.cls(self.cmd, bufsize=bufsize, **kwargs) except PermissionError: e = "xonsh: subprocess mode: permission denied: {0}" raise xt.XonshError(e.format(self.cmd[0])) except FileNotFoundError: cmd0 = self.cmd[0] if len(self.cmd) == 1 and cmd0.endswith("?"): with contextlib.suppress(OSError): return self.cls(["man", cmd0.rstrip("?")], bufsize=bufsize, **kwargs) e = "xonsh: subprocess mode: command not found: {0}".format(cmd0) env = builtins.__xonsh__.env sug = xt.suggest_commands(cmd0, env, builtins.aliases) if len(sug.strip()) > 0: e += "\n" + xt.suggest_commands(cmd0, env, builtins.aliases) raise xt.XonshError(e) return p
def _parse_redirects(r, loc=None): """returns origin, mode, destination tuple""" orig, mode, dest = _REDIR_REGEX.match(r).groups() # redirect to fd if dest.startswith("&"): try: dest = int(dest[1:]) if loc is None: loc, dest = dest, "" # NOQA else: e = f"Unrecognized redirection command: {r}" raise xt.XonshError(e) except (ValueError, xt.XonshError): raise except Exception: pass mode = _MODES.get(mode, None) if mode == "r" and (len(orig) > 0 or len(dest) > 0): raise xt.XonshError(f"Unrecognized redirection command: {r}") elif mode in _WRITE_MODES and len(dest) > 0: raise xt.XonshError(f"Unrecognized redirection command: {r}") return orig, mode, dest
def partial_proxy(f): """Dispatches the appropriate proxy function based on the number of args.""" numargs = 0 for name, param in inspect.signature(f).parameters.items(): if (param.kind == param.POSITIONAL_ONLY or param.kind == param.POSITIONAL_OR_KEYWORD): numargs += 1 elif name in xt.ALIAS_KWARG_NAMES and param.kind == param.KEYWORD_ONLY: numargs += 1 if numargs < 6: return functools.partial(PROXIES[numargs], f) elif numargs == 6: # don't need to partial. return f else: e = "Expected proxy with 6 or fewer arguments for {}, not {}" raise xt.XonshError(e.format(", ".join(xt.ALIAS_KWARG_NAMES), numargs))
def cmds_to_specs(cmds, captured=False, envs=None): """Converts a list of cmds to a list of SubprocSpec objects that are ready to be executed. """ # first build the subprocs independently and separate from the redirects i = 0 specs = [] redirects = [] for i, cmd in enumerate(cmds): if isinstance(cmd, str): redirects.append(cmd) else: env = envs[i] if envs is not None else None spec = SubprocSpec.build(cmd, captured=captured, env=env) spec.pipeline_index = i specs.append(spec) i += 1 # now modify the subprocs based on the redirects. for i, redirect in enumerate(redirects): if redirect == "|": # these should remain integer file descriptors, and not Python # file objects since they connect processes. r, w = os.pipe() specs[i].stdout = w specs[i + 1].stdin = r elif redirect == "&" and i == len(redirects) - 1: specs[i].background = True else: raise xt.XonshError(f"unrecognized redirect {redirect!r}") # Apply boundary conditions if not XSH.env.get("XONSH_CAPTURE_ALWAYS"): # Make sure sub-specs are always captured. # I.e. ![some_alias | grep x] $(some_alias) specs_to_capture = specs if captured in STDOUT_CAPTURE_KINDS else specs[: -1] for spec in specs_to_capture: if spec.env is None: spec.env = {"XONSH_CAPTURE_ALWAYS": True} else: spec.env.setdefault("XONSH_CAPTURE_ALWAYS", True) _update_last_spec(specs[-1]) return specs
def build(kls, cmd, *, cls=subprocess.Popen, **kwargs): """Creates an instance of the subprocess command, with any modifications and adjustments based on the actual cmd that was received. """ if not cmd: raise xt.XonshError("xonsh: subprocess mode: command is empty") # modifications that do not alter cmds may come before creating instance spec = kls(cmd, cls=cls, **kwargs) # modifications that alter cmds must come after creating instance # perform initial redirects spec.redirect_leading() spec.redirect_trailing() # apply aliases spec.resolve_alias() spec.resolve_binary_loc() spec.resolve_auto_cd() spec.resolve_executable_commands() spec.resolve_alias_cls() spec.resolve_stack() return spec
def resolve_executable_commands(self): """Resolve command executables, if applicable.""" alias = self.alias if alias is None: pass elif callable(alias): self.cmd.pop(0) return else: self.cmd = alias + self.cmd[1:] # resolve any redirects the aliases may have applied self.redirect_leading() self.redirect_trailing() if self.binary_loc is None: return try: scriptcmd = get_script_subproc_command(self.binary_loc, self.cmd[1:]) if scriptcmd is not None: self.cmd = scriptcmd except PermissionError: e = "xonsh: subprocess mode: permission denied: {0}" raise xt.XonshError(e.format(self.cmd[0]))