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