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