Format: text/x-python
⬇ Download File (6.8 KB)
   1  """Common utilities for the various process_* implementations.
   2  
   3  This file is only meant to be imported by the platform-specific implementations
   4  of subprocess utilities, and it contains tools that are common to all of them.
   5  """
   6  
   7  #-----------------------------------------------------------------------------
   8  #  Copyright (C) 2010-2011  The IPython Development Team
   9  #
  10  #  Distributed under the terms of the BSD License.  The full license is in
  11  #  the file COPYING, distributed as part of this software.
  12  #-----------------------------------------------------------------------------
  13  
  14  #-----------------------------------------------------------------------------
  15  # Imports
  16  #-----------------------------------------------------------------------------
  17  import subprocess
  18  import shlex
  19  import sys
  20  import os
  21  
  22  from IPython.utils import py3compat
  23  
  24  #-----------------------------------------------------------------------------
  25  # Function definitions
  26  #-----------------------------------------------------------------------------
  27  
  28  def read_no_interrupt(p):
  29      """Read from a pipe ignoring EINTR errors.
  30  
  31      This is necessary because when reading from pipes with GUI event loops
  32      running in the background, often interrupts are raised that stop the
  33      command from completing."""
  34      import errno
  35  
  36      try:
  37          return p.read()
  38      except IOError as err:
  39          if err.errno != errno.EINTR:
  40              raise
  41  
  42  
  43  def process_handler(cmd, callback, stderr=subprocess.PIPE):
  44      """Open a command in a shell subprocess and execute a callback.
  45  
  46      This function provides common scaffolding for creating subprocess.Popen()
  47      calls.  It creates a Popen object and then calls the callback with it.
  48  
  49      Parameters
  50      ----------
  51      cmd : str or list
  52          A command to be executed by the system, using :class:`subprocess.Popen`.
  53          If a string is passed, it will be run in the system shell. If a list is
  54          passed, it will be used directly as arguments.
  55      callback : callable
  56          A one-argument function that will be called with the Popen object.
  57      stderr : file descriptor number, optional
  58          By default this is set to ``subprocess.PIPE``, but you can also pass the
  59          value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
  60          the same file descriptor as its stdout.  This is useful to read stdout
  61          and stderr combined in the order they are generated.
  62  
  63      Returns
  64      -------
  65      The return value of the provided callback is returned.
  66      """
  67      sys.stdout.flush()
  68      sys.stderr.flush()
  69      # On win32, close_fds can't be true when using pipes for stdin/out/err
  70      close_fds = sys.platform != 'win32'
  71      # Determine if cmd should be run with system shell.
  72      shell = isinstance(cmd, str)
  73      # On POSIX systems run shell commands with user-preferred shell.
  74      executable = None
  75      if shell and os.name == 'posix' and 'SHELL' in os.environ:
  76          executable = os.environ['SHELL']
  77      p = subprocess.Popen(cmd, shell=shell,
  78                           executable=executable,
  79                           stdin=subprocess.PIPE,
  80                           stdout=subprocess.PIPE,
  81                           stderr=stderr,
  82                           close_fds=close_fds)
  83  
  84      try:
  85          out = callback(p)
  86      except KeyboardInterrupt:
  87          print('^C')
  88          sys.stdout.flush()
  89          sys.stderr.flush()
  90          out = None
  91      finally:
  92          # Make really sure that we don't leave processes behind, in case the
  93          # call above raises an exception
  94          # We start by assuming the subprocess finished (to avoid NameErrors
  95          # later depending on the path taken)
  96          if p.returncode is None:
  97              try:
  98                  p.terminate()
  99                  p.poll()
 100              except OSError:
 101                  pass
 102          # One last try on our way out
 103          if p.returncode is None:
 104              try:
 105                  p.kill()
 106              except OSError:
 107                  pass
 108  
 109      return out
 110  
 111  
 112  def getoutput(cmd):
 113      """Run a command and return its stdout/stderr as a string.
 114  
 115      Parameters
 116      ----------
 117      cmd : str or list
 118          A command to be executed in the system shell.
 119  
 120      Returns
 121      -------
 122      output : str
 123          A string containing the combination of stdout and stderr from the
 124      subprocess, in whatever order the subprocess originally wrote to its
 125      file descriptors (so the order of the information in this string is the
 126      correct order as would be seen if running the command in a terminal).
 127      """
 128      out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT)
 129      if out is None:
 130          return ''
 131      return py3compat.decode(out)
 132  
 133  
 134  def getoutputerror(cmd):
 135      """Return (standard output, standard error) of executing cmd in a shell.
 136  
 137      Accepts the same arguments as os.system().
 138  
 139      Parameters
 140      ----------
 141      cmd : str or list
 142          A command to be executed in the system shell.
 143  
 144      Returns
 145      -------
 146      stdout : str
 147      stderr : str
 148      """
 149      return get_output_error_code(cmd)[:2]
 150  
 151  def get_output_error_code(cmd):
 152      """Return (standard output, standard error, return code) of executing cmd
 153      in a shell.
 154  
 155      Accepts the same arguments as os.system().
 156  
 157      Parameters
 158      ----------
 159      cmd : str or list
 160          A command to be executed in the system shell.
 161  
 162      Returns
 163      -------
 164      stdout : str
 165      stderr : str
 166      returncode: int
 167      """
 168  
 169      out_err, p = process_handler(cmd, lambda p: (p.communicate(), p))
 170      if out_err is None:
 171          return '', '', p.returncode
 172      out, err = out_err
 173      return py3compat.decode(out), py3compat.decode(err), p.returncode
 174  
 175  def arg_split(s, posix=False, strict=True):
 176      """Split a command line's arguments in a shell-like manner.
 177  
 178      This is a modified version of the standard library's shlex.split()
 179      function, but with a default of posix=False for splitting, so that quotes
 180      in inputs are respected.
 181  
 182      if strict=False, then any errors shlex.split would raise will result in the
 183      unparsed remainder being the last element of the list, rather than raising.
 184      This is because we sometimes use arg_split to parse things other than
 185      command-line args.
 186      """
 187  
 188      lex = shlex.shlex(s, posix=posix)
 189      lex.whitespace_split = True
 190      # Extract tokens, ensuring that things like leaving open quotes
 191      # does not cause this to raise.  This is important, because we
 192      # sometimes pass Python source through this (e.g. %timeit f(" ")),
 193      # and it shouldn't raise an exception.
 194      # It may be a bad idea to parse things that are not command-line args
 195      # through this function, but we do, so let's be safe about it.
 196      lex.commenters='' #fix for GH-1269
 197      tokens = []
 198      while True:
 199          try:
 200              tokens.append(next(lex))
 201          except StopIteration:
 202              break
 203          except ValueError:
 204              if strict:
 205                  raise
 206              # couldn't parse, get remaining blob as last token
 207              tokens.append(lex.token)
 208              break
 209  
 210      return tokens
 211