##// END OF EJS Templates
Short in the dark windows issue
M Bussonnier -
Show More
@@ -18,14 +18,14 import subprocess
18 18 import shlex
19 19 import sys
20 20 import os
21
21 from typing import Callable, Optional, Union, List
22 22 from IPython.utils import py3compat
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Function definitions
26 26 #-----------------------------------------------------------------------------
27 27
28 def read_no_interrupt(p):
28 def read_no_interrupt(p: subprocess.Popen):
29 29 """Read from a pipe ignoring EINTR errors.
30 30
31 31 This is necessary because when reading from pipes with GUI event loops
@@ -40,7 +40,7 def read_no_interrupt(p):
40 40 raise
41 41
42 42
43 def process_handler(cmd, callback, stderr=subprocess.PIPE):
43 def process_handler(cmd:Union[str, List[str]], callback:Callable[[subprocess.Popen], int], stderr=subprocess.PIPE) -> Optional[int]:
44 44 """Open a command in a shell subprocess and execute a callback.
45 45
46 46 This function provides common scaffolding for creating subprocess.Popen()
@@ -67,7 +67,10 def process_handler(cmd, callback, stderr=subprocess.PIPE):
67 67 sys.stdout.flush()
68 68 sys.stderr.flush()
69 69 # On win32, close_fds can't be true when using pipes for stdin/out/err
70 close_fds = sys.platform != 'win32'
70 if sys.platform == "win32" and stderr != subprocess.PIPE:
71 close_fds = False
72 else:
73 close_fds = True
71 74 # Determine if cmd should be run with system shell.
72 75 shell = isinstance(cmd, str)
73 76 # On POSIX systems run shell commands with user-preferred shell.
@@ -24,6 +24,7 from ctypes import c_int, POINTER
24 24 from ctypes.wintypes import LPCWSTR, HLOCAL
25 25 from subprocess import STDOUT, TimeoutExpired
26 26 from threading import Thread
27 import subprocess
27 28
28 29 # our own imports
29 30 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
@@ -34,7 +35,7 from .encoding import DEFAULT_ENCODING
34 35 # Function definitions
35 36 #-----------------------------------------------------------------------------
36 37
37 class AvoidUNCPath(object):
38 class AvoidUNCPath:
38 39 """A context manager to protect command execution from UNC paths.
39 40
40 41 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
@@ -71,35 +72,50 class AvoidUNCPath(object):
71 72 os.chdir(self.path)
72 73
73 74
74 def _system_body(p):
75 def _system_body(p: subprocess.Popen) -> int:
75 76 """Callback for _system."""
76 77 enc = DEFAULT_ENCODING
77 78
78 79 def stdout_read():
79 for line in read_no_interrupt(p.stdout).splitlines():
80 line = line.decode(enc, 'replace')
81 print(line, file=sys.stdout)
80 try:
81 for line in read_no_interrupt(p.stdout).splitlines():
82 line = line.decode(enc, 'replace')
83 print(line, file=sys.stdout)
84 except Exception as e:
85 print(f"Error reading stdout: {e}", file=sys.stderr)
82 86
83 87 def stderr_read():
84 for line in read_no_interrupt(p.stderr).splitlines():
85 line = line.decode(enc, 'replace')
86 print(line, file=sys.stderr)
88 try:
89 for line in read_no_interrupt(p.stderr).splitlines():
90 line = line.decode(enc, 'replace')
91 print(line, file=sys.stderr)
92 except Exception as e:
93 print(f"Error reading stderr: {e}", file=sys.stderr)
87 94
88 Thread(target=stdout_read).start()
89 Thread(target=stderr_read).start()
95 stdout_thread = Thread(target=stdout_read)
96 stderr_thread = Thread(target=stderr_read)
97
98 stdout_thread.start()
99 stderr_thread.start()
90 100
91 101 # Wait to finish for returncode. Unfortunately, Python has a bug where
92 102 # wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in
93 # a loop instead of just doing `return p.wait()`.
103 # a loop instead of just doing `return p.wait()`
94 104 while True:
95 105 result = p.poll()
96 106 if result is None:
97 107 time.sleep(0.01)
98 108 else:
99 return result
109 break
110
111 # Join the threads to ensure they complete before returning
112 stdout_thread.join()
113 stderr_thread.join()
114
115 return result
100 116
101 117
102 def system(cmd):
118 def system(cmd: str):
103 119 """Win32 version of os.system() that works with network shares.
104 120
105 121 Note that this implementation returns None, as meant for use in IPython.
General Comments 0
You need to be logged in to leave comments. Login now