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
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1"""Common utilities for the various process_* implementations.
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"""
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#-----------------------------------------------------------------------------
14#-----------------------------------------------------------------------------
15# Imports
16#-----------------------------------------------------------------------------
17import subprocess
18import shlex
19import sys
20import os
22from IPython.utils import py3compat
24#-----------------------------------------------------------------------------
25# Function definitions
26#-----------------------------------------------------------------------------
28def read_no_interrupt(p):
29 """Read from a pipe ignoring EINTR errors.
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
36 try:
37 return p.read()
38 except IOError as err:
39 if err.errno != errno.EINTR:
40 raise
43def process_handler(cmd, callback, stderr=subprocess.PIPE):
44 """Open a command in a shell subprocess and execute a callback.
46 This function provides common scaffolding for creating subprocess.Popen()
47 calls. It creates a Popen object and then calls the callback with it.
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.
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)
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
109 return out
112def getoutput(cmd):
113 """Run a command and return its stdout/stderr as a string.
115 Parameters
116 ----------
117 cmd : str or list
118 A command to be executed in the system shell.
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)
134def getoutputerror(cmd):
135 """Return (standard output, standard error) of executing cmd in a shell.
137 Accepts the same arguments as os.system().
139 Parameters
140 ----------
141 cmd : str or list
142 A command to be executed in the system shell.
144 Returns
145 -------
146 stdout : str
147 stderr : str
148 """
149 return get_output_error_code(cmd)[:2]
151def get_output_error_code(cmd):
152 """Return (standard output, standard error, return code) of executing cmd
153 in a shell.
155 Accepts the same arguments as os.system().
157 Parameters
158 ----------
159 cmd : str or list
160 A command to be executed in the system shell.
162 Returns
163 -------
164 stdout : str
165 stderr : str
166 returncode: int
167 """
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
175def arg_split(s, posix=False, strict=True):
176 """Split a command line's arguments in a shell-like manner.
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.
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 """
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
210 return tokens