| """Common utilities for the various process_* implementations. |
| |
| This file is only meant to be imported by the platform-specific implementations |
| of subprocess utilities, and it contains tools that are common to all of them. |
| """ |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| import subprocess |
| import shlex |
| import sys |
| import os |
|
|
| from IPython.utils import py3compat |
|
|
| |
| |
| |
|
|
| def read_no_interrupt(p): |
| """Read from a pipe ignoring EINTR errors. |
| |
| This is necessary because when reading from pipes with GUI event loops |
| running in the background, often interrupts are raised that stop the |
| command from completing.""" |
| import errno |
|
|
| try: |
| return p.read() |
| except IOError as err: |
| if err.errno != errno.EINTR: |
| raise |
|
|
|
|
| def process_handler(cmd, callback, stderr=subprocess.PIPE): |
| """Open a command in a shell subprocess and execute a callback. |
| |
| This function provides common scaffolding for creating subprocess.Popen() |
| calls. It creates a Popen object and then calls the callback with it. |
| |
| Parameters |
| ---------- |
| cmd : str or list |
| A command to be executed by the system, using :class:`subprocess.Popen`. |
| If a string is passed, it will be run in the system shell. If a list is |
| passed, it will be used directly as arguments. |
| callback : callable |
| A one-argument function that will be called with the Popen object. |
| stderr : file descriptor number, optional |
| By default this is set to ``subprocess.PIPE``, but you can also pass the |
| value ``subprocess.STDOUT`` to force the subprocess' stderr to go into |
| the same file descriptor as its stdout. This is useful to read stdout |
| and stderr combined in the order they are generated. |
| |
| Returns |
| ------- |
| The return value of the provided callback is returned. |
| """ |
| sys.stdout.flush() |
| sys.stderr.flush() |
| |
| close_fds = sys.platform != 'win32' |
| |
| shell = isinstance(cmd, str) |
| |
| executable = None |
| if shell and os.name == 'posix' and 'SHELL' in os.environ: |
| executable = os.environ['SHELL'] |
| p = subprocess.Popen(cmd, shell=shell, |
| executable=executable, |
| stdin=subprocess.PIPE, |
| stdout=subprocess.PIPE, |
| stderr=stderr, |
| close_fds=close_fds) |
|
|
| try: |
| out = callback(p) |
| except KeyboardInterrupt: |
| print('^C') |
| sys.stdout.flush() |
| sys.stderr.flush() |
| out = None |
| finally: |
| |
| |
| |
| |
| if p.returncode is None: |
| try: |
| p.terminate() |
| p.poll() |
| except OSError: |
| pass |
| |
| if p.returncode is None: |
| try: |
| p.kill() |
| except OSError: |
| pass |
|
|
| return out |
|
|
|
|
| def getoutput(cmd): |
| """Run a command and return its stdout/stderr as a string. |
| |
| Parameters |
| ---------- |
| cmd : str or list |
| A command to be executed in the system shell. |
| |
| Returns |
| ------- |
| output : str |
| A string containing the combination of stdout and stderr from the |
| subprocess, in whatever order the subprocess originally wrote to its |
| file descriptors (so the order of the information in this string is the |
| correct order as would be seen if running the command in a terminal). |
| """ |
| out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT) |
| if out is None: |
| return '' |
| return py3compat.decode(out) |
|
|
|
|
| def getoutputerror(cmd): |
| """Return (standard output, standard error) of executing cmd in a shell. |
| |
| Accepts the same arguments as os.system(). |
| |
| Parameters |
| ---------- |
| cmd : str or list |
| A command to be executed in the system shell. |
| |
| Returns |
| ------- |
| stdout : str |
| stderr : str |
| """ |
| return get_output_error_code(cmd)[:2] |
|
|
| def get_output_error_code(cmd): |
| """Return (standard output, standard error, return code) of executing cmd |
| in a shell. |
| |
| Accepts the same arguments as os.system(). |
| |
| Parameters |
| ---------- |
| cmd : str or list |
| A command to be executed in the system shell. |
| |
| Returns |
| ------- |
| stdout : str |
| stderr : str |
| returncode: int |
| """ |
|
|
| out_err, p = process_handler(cmd, lambda p: (p.communicate(), p)) |
| if out_err is None: |
| return '', '', p.returncode |
| out, err = out_err |
| return py3compat.decode(out), py3compat.decode(err), p.returncode |
|
|
| def arg_split(s, posix=False, strict=True): |
| """Split a command line's arguments in a shell-like manner. |
| |
| This is a modified version of the standard library's shlex.split() |
| function, but with a default of posix=False for splitting, so that quotes |
| in inputs are respected. |
| |
| if strict=False, then any errors shlex.split would raise will result in the |
| unparsed remainder being the last element of the list, rather than raising. |
| This is because we sometimes use arg_split to parse things other than |
| command-line args. |
| """ |
|
|
| lex = shlex.shlex(s, posix=posix) |
| lex.whitespace_split = True |
| |
| |
| |
| |
| |
| |
| lex.commenters='' |
| tokens = [] |
| while True: |
| try: |
| tokens.append(next(lex)) |
| except StopIteration: |
| break |
| except ValueError: |
| if strict: |
| raise |
| |
| tokens.append(lex.token) |
| break |
|
|
| return tokens |
|
|