123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- """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.
- """
- #-----------------------------------------------------------------------------
- # Copyright (C) 2010-2011 The IPython Development Team
- #
- # Distributed under the terms of the BSD License. The full license is in
- # the file COPYING, distributed as part of this software.
- #-----------------------------------------------------------------------------
- #-----------------------------------------------------------------------------
- # Imports
- #-----------------------------------------------------------------------------
- import subprocess
- import shlex
- import sys
- import os
- from IPython.utils import py3compat
- #-----------------------------------------------------------------------------
- # Function definitions
- #-----------------------------------------------------------------------------
- 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()
- # On win32, close_fds can't be true when using pipes for stdin/out/err
- close_fds = sys.platform != 'win32'
- # Determine if cmd should be run with system shell.
- shell = isinstance(cmd, str)
- # On POSIX systems run shell commands with user-preferred shell.
- 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:
- # Make really sure that we don't leave processes behind, in case the
- # call above raises an exception
- # We start by assuming the subprocess finished (to avoid NameErrors
- # later depending on the path taken)
- if p.returncode is None:
- try:
- p.terminate()
- p.poll()
- except OSError:
- pass
- # One last try on our way out
- 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
- # Extract tokens, ensuring that things like leaving open quotes
- # does not cause this to raise. This is important, because we
- # sometimes pass Python source through this (e.g. %timeit f(" ")),
- # and it shouldn't raise an exception.
- # It may be a bad idea to parse things that are not command-line args
- # through this function, but we do, so let's be safe about it.
- lex.commenters='' #fix for GH-1269
- tokens = []
- while True:
- try:
- tokens.append(next(lex))
- except StopIteration:
- break
- except ValueError:
- if strict:
- raise
- # couldn't parse, get remaining blob as last token
- tokens.append(lex.token)
- break
- return tokens
|