Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/utils/_process_common.py: 16%

68 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

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 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 

112def 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 

134def 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 

151def 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 

175def 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