##// END OF EJS Templates
fix types
M Bussonnier -
Show More
@@ -1,217 +1,218
1 """Common utilities for the various process_* implementations.
1 """Common utilities for the various process_* implementations.
2
2
3 This file is only meant to be imported by the platform-specific implementations
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.
4 of subprocess utilities, and it contains tools that are common to all of them.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2010-2011 The IPython Development Team
8 # Copyright (C) 2010-2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 import subprocess
17 import os
18 import shlex
18 import shlex
19 import subprocess
19 import sys
20 import sys
20 import os
21 from typing import IO, Any, Callable, List, Union
21 from typing import Callable, Optional, Union, List
22
22 from IPython.utils import py3compat
23 from IPython.utils import py3compat
23
24
24 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25 # Function definitions
26 # Function definitions
26 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
27
28
28 def read_no_interrupt(p: subprocess.Popen) -> str:
29 def read_no_interrupt(stream: IO[Any]) -> bytes:
29 """Read from a pipe ignoring EINTR errors.
30 """Read from a pipe ignoring EINTR errors.
30
31
31 This is necessary because when reading from pipes with GUI event loops
32 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 running in the background, often interrupts are raised that stop the
33 command from completing."""
34 command from completing."""
34 import errno
35 import errno
35
36
36 try:
37 try:
37 return p.read()
38 return stream.read()
38 except IOError as err:
39 except IOError as err:
39 if err.errno != errno.EINTR:
40 if err.errno != errno.EINTR:
40 raise
41 raise
41
42
42
43
43 def process_handler(
44 def process_handler(
44 cmd: Union[str, List[str]],
45 cmd: Union[str, List[str]],
45 callback: Callable[[subprocess.Popen], int],
46 callback: Callable[[subprocess.Popen], int],
46 stderr=subprocess.PIPE,
47 stderr=subprocess.PIPE,
47 ) -> int:
48 ) -> int:
48 """Open a command in a shell subprocess and execute a callback.
49 """Open a command in a shell subprocess and execute a callback.
49
50
50 This function provides common scaffolding for creating subprocess.Popen()
51 This function provides common scaffolding for creating subprocess.Popen()
51 calls. It creates a Popen object and then calls the callback with it.
52 calls. It creates a Popen object and then calls the callback with it.
52
53
53 Parameters
54 Parameters
54 ----------
55 ----------
55 cmd : str or list
56 cmd : str or list
56 A command to be executed by the system, using :class:`subprocess.Popen`.
57 A command to be executed by the system, using :class:`subprocess.Popen`.
57 If a string is passed, it will be run in the system shell. If a list is
58 If a string is passed, it will be run in the system shell. If a list is
58 passed, it will be used directly as arguments.
59 passed, it will be used directly as arguments.
59 callback : callable
60 callback : callable
60 A one-argument function that will be called with the Popen object.
61 A one-argument function that will be called with the Popen object.
61 stderr : file descriptor number, optional
62 stderr : file descriptor number, optional
62 By default this is set to ``subprocess.PIPE``, but you can also pass the
63 By default this is set to ``subprocess.PIPE``, but you can also pass the
63 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
64 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
64 the same file descriptor as its stdout. This is useful to read stdout
65 the same file descriptor as its stdout. This is useful to read stdout
65 and stderr combined in the order they are generated.
66 and stderr combined in the order they are generated.
66
67
67 Returns
68 Returns
68 -------
69 -------
69 The return value of the provided callback is returned.
70 The return value of the provided callback is returned.
70 """
71 """
71 sys.stdout.flush()
72 sys.stdout.flush()
72 sys.stderr.flush()
73 sys.stderr.flush()
73 # On win32, close_fds can't be true when using pipes for stdin/out/err
74 # On win32, close_fds can't be true when using pipes for stdin/out/err
74 if sys.platform == "win32" and stderr != subprocess.PIPE:
75 if sys.platform == "win32" and stderr != subprocess.PIPE:
75 close_fds = False
76 close_fds = False
76 else:
77 else:
77 close_fds = True
78 close_fds = True
78 # Determine if cmd should be run with system shell.
79 # Determine if cmd should be run with system shell.
79 shell = isinstance(cmd, str)
80 shell = isinstance(cmd, str)
80 # On POSIX systems run shell commands with user-preferred shell.
81 # On POSIX systems run shell commands with user-preferred shell.
81 executable = None
82 executable = None
82 if shell and os.name == 'posix' and 'SHELL' in os.environ:
83 if shell and os.name == 'posix' and 'SHELL' in os.environ:
83 executable = os.environ['SHELL']
84 executable = os.environ['SHELL']
84 p = subprocess.Popen(cmd, shell=shell,
85 p = subprocess.Popen(cmd, shell=shell,
85 executable=executable,
86 executable=executable,
86 stdin=subprocess.PIPE,
87 stdin=subprocess.PIPE,
87 stdout=subprocess.PIPE,
88 stdout=subprocess.PIPE,
88 stderr=stderr,
89 stderr=stderr,
89 close_fds=close_fds)
90 close_fds=close_fds)
90
91
91 try:
92 try:
92 out = callback(p)
93 out = callback(p)
93 except KeyboardInterrupt:
94 except KeyboardInterrupt:
94 print('^C')
95 print('^C')
95 sys.stdout.flush()
96 sys.stdout.flush()
96 sys.stderr.flush()
97 sys.stderr.flush()
97 out = None
98 out = None
98 finally:
99 finally:
99 # Make really sure that we don't leave processes behind, in case the
100 # Make really sure that we don't leave processes behind, in case the
100 # call above raises an exception
101 # call above raises an exception
101 # We start by assuming the subprocess finished (to avoid NameErrors
102 # We start by assuming the subprocess finished (to avoid NameErrors
102 # later depending on the path taken)
103 # later depending on the path taken)
103 if p.returncode is None:
104 if p.returncode is None:
104 try:
105 try:
105 p.terminate()
106 p.terminate()
106 p.poll()
107 p.poll()
107 except OSError:
108 except OSError:
108 pass
109 pass
109 # One last try on our way out
110 # One last try on our way out
110 if p.returncode is None:
111 if p.returncode is None:
111 try:
112 try:
112 p.kill()
113 p.kill()
113 except OSError:
114 except OSError:
114 pass
115 pass
115
116
116 return out
117 return out
117
118
118
119
119 def getoutput(cmd):
120 def getoutput(cmd):
120 """Run a command and return its stdout/stderr as a string.
121 """Run a command and return its stdout/stderr as a string.
121
122
122 Parameters
123 Parameters
123 ----------
124 ----------
124 cmd : str or list
125 cmd : str or list
125 A command to be executed in the system shell.
126 A command to be executed in the system shell.
126
127
127 Returns
128 Returns
128 -------
129 -------
129 output : str
130 output : str
130 A string containing the combination of stdout and stderr from the
131 A string containing the combination of stdout and stderr from the
131 subprocess, in whatever order the subprocess originally wrote to its
132 subprocess, in whatever order the subprocess originally wrote to its
132 file descriptors (so the order of the information in this string is the
133 file descriptors (so the order of the information in this string is the
133 correct order as would be seen if running the command in a terminal).
134 correct order as would be seen if running the command in a terminal).
134 """
135 """
135 out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT)
136 out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT)
136 if out is None:
137 if out is None:
137 return ''
138 return ''
138 return py3compat.decode(out)
139 return py3compat.decode(out)
139
140
140
141
141 def getoutputerror(cmd):
142 def getoutputerror(cmd):
142 """Return (standard output, standard error) of executing cmd in a shell.
143 """Return (standard output, standard error) of executing cmd in a shell.
143
144
144 Accepts the same arguments as os.system().
145 Accepts the same arguments as os.system().
145
146
146 Parameters
147 Parameters
147 ----------
148 ----------
148 cmd : str or list
149 cmd : str or list
149 A command to be executed in the system shell.
150 A command to be executed in the system shell.
150
151
151 Returns
152 Returns
152 -------
153 -------
153 stdout : str
154 stdout : str
154 stderr : str
155 stderr : str
155 """
156 """
156 return get_output_error_code(cmd)[:2]
157 return get_output_error_code(cmd)[:2]
157
158
158 def get_output_error_code(cmd):
159 def get_output_error_code(cmd):
159 """Return (standard output, standard error, return code) of executing cmd
160 """Return (standard output, standard error, return code) of executing cmd
160 in a shell.
161 in a shell.
161
162
162 Accepts the same arguments as os.system().
163 Accepts the same arguments as os.system().
163
164
164 Parameters
165 Parameters
165 ----------
166 ----------
166 cmd : str or list
167 cmd : str or list
167 A command to be executed in the system shell.
168 A command to be executed in the system shell.
168
169
169 Returns
170 Returns
170 -------
171 -------
171 stdout : str
172 stdout : str
172 stderr : str
173 stderr : str
173 returncode: int
174 returncode: int
174 """
175 """
175
176
176 out_err, p = process_handler(cmd, lambda p: (p.communicate(), p))
177 out_err, p = process_handler(cmd, lambda p: (p.communicate(), p))
177 if out_err is None:
178 if out_err is None:
178 return '', '', p.returncode
179 return '', '', p.returncode
179 out, err = out_err
180 out, err = out_err
180 return py3compat.decode(out), py3compat.decode(err), p.returncode
181 return py3compat.decode(out), py3compat.decode(err), p.returncode
181
182
182 def arg_split(s, posix=False, strict=True):
183 def arg_split(s, posix=False, strict=True):
183 """Split a command line's arguments in a shell-like manner.
184 """Split a command line's arguments in a shell-like manner.
184
185
185 This is a modified version of the standard library's shlex.split()
186 This is a modified version of the standard library's shlex.split()
186 function, but with a default of posix=False for splitting, so that quotes
187 function, but with a default of posix=False for splitting, so that quotes
187 in inputs are respected.
188 in inputs are respected.
188
189
189 if strict=False, then any errors shlex.split would raise will result in the
190 if strict=False, then any errors shlex.split would raise will result in the
190 unparsed remainder being the last element of the list, rather than raising.
191 unparsed remainder being the last element of the list, rather than raising.
191 This is because we sometimes use arg_split to parse things other than
192 This is because we sometimes use arg_split to parse things other than
192 command-line args.
193 command-line args.
193 """
194 """
194
195
195 lex = shlex.shlex(s, posix=posix)
196 lex = shlex.shlex(s, posix=posix)
196 lex.whitespace_split = True
197 lex.whitespace_split = True
197 # Extract tokens, ensuring that things like leaving open quotes
198 # Extract tokens, ensuring that things like leaving open quotes
198 # does not cause this to raise. This is important, because we
199 # does not cause this to raise. This is important, because we
199 # sometimes pass Python source through this (e.g. %timeit f(" ")),
200 # sometimes pass Python source through this (e.g. %timeit f(" ")),
200 # and it shouldn't raise an exception.
201 # and it shouldn't raise an exception.
201 # It may be a bad idea to parse things that are not command-line args
202 # It may be a bad idea to parse things that are not command-line args
202 # through this function, but we do, so let's be safe about it.
203 # through this function, but we do, so let's be safe about it.
203 lex.commenters='' #fix for GH-1269
204 lex.commenters='' #fix for GH-1269
204 tokens = []
205 tokens = []
205 while True:
206 while True:
206 try:
207 try:
207 tokens.append(next(lex))
208 tokens.append(next(lex))
208 except StopIteration:
209 except StopIteration:
209 break
210 break
210 except ValueError:
211 except ValueError:
211 if strict:
212 if strict:
212 raise
213 raise
213 # couldn't parse, get remaining blob as last token
214 # couldn't parse, get remaining blob as last token
214 tokens.append(lex.token)
215 tokens.append(lex.token)
215 break
216 break
216
217
217 return tokens
218 return tokens
@@ -1,215 +1,220
1 """Windows-specific implementation of process utilities.
1 """Windows-specific implementation of process utilities.
2
2
3 This file is only meant to be imported by process.py, not by end-users.
3 This file is only meant to be imported by process.py, not by end-users.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team
7 # Copyright (C) 2010-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 # stdlib
17 # stdlib
18 import ctypes
18 import os
19 import os
20 import subprocess
19 import sys
21 import sys
20 import ctypes
21 import time
22 import time
22
23 from ctypes import POINTER, c_int
23 from ctypes import c_int, POINTER
24 from ctypes.wintypes import HLOCAL, LPCWSTR
24 from ctypes.wintypes import LPCWSTR, HLOCAL
25 from subprocess import STDOUT
25 from subprocess import STDOUT, TimeoutExpired
26 from threading import Thread
26 from threading import Thread
27 import subprocess
27 from types import TracebackType
28 from typing import IO, Any, List, Optional
28
29
29 from typing import Optional, List
30 from . import py3compat
30 import traceback
31 from ._process_common import arg_split as py_arg_split
31
32
32 # our own imports
33 # our own imports
33 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
34 from ._process_common import process_handler, read_no_interrupt
34 from . import py3compat
35 from .encoding import DEFAULT_ENCODING
35 from .encoding import DEFAULT_ENCODING
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # Function definitions
38 # Function definitions
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41 class AvoidUNCPath:
41 class AvoidUNCPath:
42 """A context manager to protect command execution from UNC paths.
42 """A context manager to protect command execution from UNC paths.
43
43
44 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
44 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
45 This context manager temporarily changes directory to the 'C:' drive on
45 This context manager temporarily changes directory to the 'C:' drive on
46 entering, and restores the original working directory on exit.
46 entering, and restores the original working directory on exit.
47
47
48 The context manager returns the starting working directory *if* it made a
48 The context manager returns the starting working directory *if* it made a
49 change and None otherwise, so that users can apply the necessary adjustment
49 change and None otherwise, so that users can apply the necessary adjustment
50 to their system calls in the event of a change.
50 to their system calls in the event of a change.
51
51
52 Examples
52 Examples
53 --------
53 --------
54 ::
54 ::
55 cmd = 'dir'
55 cmd = 'dir'
56 with AvoidUNCPath() as path:
56 with AvoidUNCPath() as path:
57 if path is not None:
57 if path is not None:
58 cmd = '"pushd %s &&"%s' % (path, cmd)
58 cmd = '"pushd %s &&"%s' % (path, cmd)
59 os.system(cmd)
59 os.system(cmd)
60 """
60 """
61
61
62 def __enter__(self) -> Optional[str]:
62 def __enter__(self) -> Optional[str]:
63 self.path = os.getcwd()
63 self.path = os.getcwd()
64 self.is_unc_path = self.path.startswith(r"\\")
64 self.is_unc_path = self.path.startswith(r"\\")
65 if self.is_unc_path:
65 if self.is_unc_path:
66 # change to c drive (as cmd.exe cannot handle UNC addresses)
66 # change to c drive (as cmd.exe cannot handle UNC addresses)
67 os.chdir("C:")
67 os.chdir("C:")
68 return self.path
68 return self.path
69 else:
69 else:
70 # We return None to signal that there was no change in the working
70 # We return None to signal that there was no change in the working
71 # directory
71 # directory
72 return None
72 return None
73
73
74 def __exit__(
74 def __exit__(
75 self, exc_type: Optional[type], exc_value: Optional[BaseException], traceback
75 self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback:TracebackType
76 ) -> None:
76 ) -> None:
77 if self.is_unc_path:
77 if self.is_unc_path:
78 os.chdir(self.path)
78 os.chdir(self.path)
79
79
80
80
81 def _system_body(p: subprocess.Popen) -> int:
81 def _system_body(p: subprocess.Popen) -> int:
82 """Callback for _system."""
82 """Callback for _system."""
83 enc = DEFAULT_ENCODING
83 enc = DEFAULT_ENCODING
84
84
85 # Dec 2024: in both of these functions, I'm not sure why we .splitlines()
86 # the bytes and then decode each line individually instead of just decoding
87 # the whole thing at once.
85 def stdout_read() -> None:
88 def stdout_read() -> None:
86 try:
89 try:
87 for line in read_no_interrupt(p.stdout).splitlines():
90 assert p.stdout is not None
88 line = line.decode(enc, "replace")
91 for byte_line in read_no_interrupt(p.stdout).splitlines():
92 line = byte_line.decode(enc, "replace")
89 print(line, file=sys.stdout)
93 print(line, file=sys.stdout)
90 except Exception as e:
94 except Exception as e:
91 print(f"Error reading stdout: {e}", file=sys.stderr)
95 print(f"Error reading stdout: {e}", file=sys.stderr)
92
96
93 def stderr_read() -> None:
97 def stderr_read() -> None:
94 try:
98 try:
95 for line in read_no_interrupt(p.stderr).splitlines():
99 assert p.stderr is not None
96 line = line.decode(enc, "replace")
100 for byte_line in read_no_interrupt(p.stderr).splitlines():
101 line = byte_line.decode(enc, "replace")
97 print(line, file=sys.stderr)
102 print(line, file=sys.stderr)
98 except Exception as e:
103 except Exception as e:
99 print(f"Error reading stderr: {e}", file=sys.stderr)
104 print(f"Error reading stderr: {e}", file=sys.stderr)
100
105
101 stdout_thread = Thread(target=stdout_read)
106 stdout_thread = Thread(target=stdout_read)
102 stderr_thread = Thread(target=stderr_read)
107 stderr_thread = Thread(target=stderr_read)
103
108
104 stdout_thread.start()
109 stdout_thread.start()
105 stderr_thread.start()
110 stderr_thread.start()
106
111
107 # Wait to finish for returncode. Unfortunately, Python has a bug where
112 # Wait to finish for returncode. Unfortunately, Python has a bug where
108 # wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in
113 # wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in
109 # a loop instead of just doing `return p.wait()`
114 # a loop instead of just doing `return p.wait()`
110 while True:
115 while True:
111 result = p.poll()
116 result = p.poll()
112 if result is None:
117 if result is None:
113 time.sleep(0.01)
118 time.sleep(0.01)
114 else:
119 else:
115 break
120 break
116
121
117 # Join the threads to ensure they complete before returning
122 # Join the threads to ensure they complete before returning
118 stdout_thread.join()
123 stdout_thread.join()
119 stderr_thread.join()
124 stderr_thread.join()
120
125
121 return result
126 return result
122
127
123
128
124 def system(cmd: str) -> Optional[int]:
129 def system(cmd: str) -> Optional[int]:
125 """Win32 version of os.system() that works with network shares.
130 """Win32 version of os.system() that works with network shares.
126
131
127 Note that this implementation returns None, as meant for use in IPython.
132 Note that this implementation returns None, as meant for use in IPython.
128
133
129 Parameters
134 Parameters
130 ----------
135 ----------
131 cmd : str or list
136 cmd : str or list
132 A command to be executed in the system shell.
137 A command to be executed in the system shell.
133
138
134 Returns
139 Returns
135 -------
140 -------
136 int : child process' exit code.
141 int : child process' exit code.
137 """
142 """
138 # The controller provides interactivity with both
143 # The controller provides interactivity with both
139 # stdin and stdout
144 # stdin and stdout
140 #import _process_win32_controller
145 #import _process_win32_controller
141 #_process_win32_controller.system(cmd)
146 #_process_win32_controller.system(cmd)
142
147
143 with AvoidUNCPath() as path:
148 with AvoidUNCPath() as path:
144 if path is not None:
149 if path is not None:
145 cmd = '"pushd %s &&"%s' % (path, cmd)
150 cmd = '"pushd %s &&"%s' % (path, cmd)
146 return process_handler(cmd, _system_body)
151 return process_handler(cmd, _system_body)
147
152
148 def getoutput(cmd: str) -> str:
153 def getoutput(cmd: str) -> str:
149 """Return standard output of executing cmd in a shell.
154 """Return standard output of executing cmd in a shell.
150
155
151 Accepts the same arguments as os.system().
156 Accepts the same arguments as os.system().
152
157
153 Parameters
158 Parameters
154 ----------
159 ----------
155 cmd : str or list
160 cmd : str or list
156 A command to be executed in the system shell.
161 A command to be executed in the system shell.
157
162
158 Returns
163 Returns
159 -------
164 -------
160 stdout : str
165 stdout : str
161 """
166 """
162
167
163 with AvoidUNCPath() as path:
168 with AvoidUNCPath() as path:
164 if path is not None:
169 if path is not None:
165 cmd = '"pushd %s &&"%s' % (path, cmd)
170 cmd = '"pushd %s &&"%s' % (path, cmd)
166 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
171 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
167
172
168 if out is None:
173 if out is None:
169 out = b''
174 out = b''
170 return py3compat.decode(out)
175 return py3compat.decode(out)
171
176
172 try:
177 try:
173 windll = ctypes.windll # type: ignore [attr-defined]
178 windll = ctypes.windll # type: ignore [attr-defined]
174 CommandLineToArgvW = windll.shell32.CommandLineToArgvW
179 CommandLineToArgvW = windll.shell32.CommandLineToArgvW
175 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
180 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
176 CommandLineToArgvW.restype = POINTER(LPCWSTR)
181 CommandLineToArgvW.restype = POINTER(LPCWSTR)
177 LocalFree = windll.kernel32.LocalFree
182 LocalFree = windll.kernel32.LocalFree
178 LocalFree.res_type = HLOCAL
183 LocalFree.res_type = HLOCAL
179 LocalFree.arg_types = [HLOCAL]
184 LocalFree.arg_types = [HLOCAL]
180
185
181 def arg_split(
186 def arg_split(
182 commandline: str, posix: bool = False, strict: bool = True
187 commandline: str, posix: bool = False, strict: bool = True
183 ) -> List[str]:
188 ) -> List[str]:
184 """Split a command line's arguments in a shell-like manner.
189 """Split a command line's arguments in a shell-like manner.
185
190
186 This is a special version for windows that use a ctypes call to CommandLineToArgvW
191 This is a special version for windows that use a ctypes call to CommandLineToArgvW
187 to do the argv splitting. The posix parameter is ignored.
192 to do the argv splitting. The posix parameter is ignored.
188
193
189 If strict=False, process_common.arg_split(...strict=False) is used instead.
194 If strict=False, process_common.arg_split(...strict=False) is used instead.
190 """
195 """
191 #CommandLineToArgvW returns path to executable if called with empty string.
196 #CommandLineToArgvW returns path to executable if called with empty string.
192 if commandline.strip() == "":
197 if commandline.strip() == "":
193 return []
198 return []
194 if not strict:
199 if not strict:
195 # not really a cl-arg, fallback on _process_common
200 # not really a cl-arg, fallback on _process_common
196 return py_arg_split(commandline, posix=posix, strict=strict)
201 return py_arg_split(commandline, posix=posix, strict=strict)
197 argvn = c_int()
202 argvn = c_int()
198 result_pointer = CommandLineToArgvW(commandline.lstrip(), ctypes.byref(argvn))
203 result_pointer = CommandLineToArgvW(commandline.lstrip(), ctypes.byref(argvn))
199 result_array_type = LPCWSTR * argvn.value
204 result_array_type = LPCWSTR * argvn.value
200 result = [
205 result = [
201 arg
206 arg
202 for arg in result_array_type.from_address(
207 for arg in result_array_type.from_address(
203 ctypes.addressof(result_pointer.contents)
208 ctypes.addressof(result_pointer.contents)
204 )
209 )
205 if arg is not None
210 if arg is not None
206 ]
211 ]
207 retval = LocalFree(result_pointer)
212 LocalFree(result_pointer)
208 return result
213 return result
209 except AttributeError:
214 except AttributeError:
210 arg_split = py_arg_split
215 arg_split = py_arg_split
211
216
212 def check_pid(pid: int) -> bool:
217 def check_pid(pid: int) -> bool:
213 # OpenProcess returns 0 if no such process (of ours) exists
218 # OpenProcess returns 0 if no such process (of ours) exists
214 # positive int otherwise
219 # positive int otherwise
215 return bool(windll.kernel32.OpenProcess(1, 0, pid))
220 return bool(windll.kernel32.OpenProcess(1, 0, pid))
General Comments 0
You need to be logged in to leave comments. Login now