Skip to content

Commit

Permalink
Short in the dark windows issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau committed Dec 17, 2024
1 parent a0c65d1 commit b63a5a5
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
11 changes: 7 additions & 4 deletions IPython/utils/_process_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import shlex
import sys
import os

from typing import Callable, Optional, Union, List
from IPython.utils import py3compat

#-----------------------------------------------------------------------------
# Function definitions
#-----------------------------------------------------------------------------

def read_no_interrupt(p):
def read_no_interrupt(p: subprocess.Popen):
"""Read from a pipe ignoring EINTR errors.
This is necessary because when reading from pipes with GUI event loops
Expand All @@ -40,7 +40,7 @@ def read_no_interrupt(p):
raise


def process_handler(cmd, callback, stderr=subprocess.PIPE):
def process_handler(cmd:Union[str, List[str]], callback:Callable[[subprocess.Popen], int], stderr=subprocess.PIPE) -> Optional[int]:
"""Open a command in a shell subprocess and execute a callback.
This function provides common scaffolding for creating subprocess.Popen()
Expand All @@ -67,7 +67,10 @@ def process_handler(cmd, callback, stderr=subprocess.PIPE):
sys.stdout.flush()
sys.stderr.flush()
# On win32, close_fds can't be true when using pipes for stdin/out/err
close_fds = sys.platform != 'win32'
if sys.platform == "win32" and stderr != subprocess.PIPE:
close_fds = False
else:
close_fds = True
# Determine if cmd should be run with system shell.
shell = isinstance(cmd, str)
# On POSIX systems run shell commands with user-preferred shell.
Expand Down
42 changes: 29 additions & 13 deletions IPython/utils/_process_win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ctypes.wintypes import LPCWSTR, HLOCAL
from subprocess import STDOUT, TimeoutExpired
from threading import Thread
import subprocess

# our own imports
from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
Expand All @@ -34,7 +35,7 @@
# Function definitions
#-----------------------------------------------------------------------------

class AvoidUNCPath(object):
class AvoidUNCPath:
"""A context manager to protect command execution from UNC paths.
In the Win32 API, commands can't be invoked with the cwd being a UNC path.
Expand Down Expand Up @@ -71,35 +72,50 @@ def __exit__(self, exc_type, exc_value, traceback):
os.chdir(self.path)


def _system_body(p):
def _system_body(p: subprocess.Popen) -> int:
"""Callback for _system."""
enc = DEFAULT_ENCODING

def stdout_read():
for line in read_no_interrupt(p.stdout).splitlines():
line = line.decode(enc, 'replace')
print(line, file=sys.stdout)
try:
for line in read_no_interrupt(p.stdout).splitlines():
line = line.decode(enc, 'replace')
print(line, file=sys.stdout)
except Exception as e:
print(f"Error reading stdout: {e}", file=sys.stderr)

def stderr_read():
for line in read_no_interrupt(p.stderr).splitlines():
line = line.decode(enc, 'replace')
print(line, file=sys.stderr)
try:
for line in read_no_interrupt(p.stderr).splitlines():
line = line.decode(enc, 'replace')
print(line, file=sys.stderr)
except Exception as e:
print(f"Error reading stderr: {e}", file=sys.stderr)

Thread(target=stdout_read).start()
Thread(target=stderr_read).start()
stdout_thread = Thread(target=stdout_read)
stderr_thread = Thread(target=stderr_read)

stdout_thread.start()
stderr_thread.start()

# Wait to finish for returncode. Unfortunately, Python has a bug where
# wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in
# a loop instead of just doing `return p.wait()`.
# a loop instead of just doing `return p.wait()`
while True:
result = p.poll()
if result is None:
time.sleep(0.01)
else:
return result
break

# Join the threads to ensure they complete before returning
stdout_thread.join()
stderr_thread.join()

return result


def system(cmd):
def system(cmd: str):
"""Win32 version of os.system() that works with network shares.
Note that this implementation returns None, as meant for use in IPython.
Expand Down

0 comments on commit b63a5a5

Please sign in to comment.