Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ def __exit__(self, exc_type, exc_val, exc_tb):


class _TestProcess:
@unittest.skipUnless(sys.platform == "win32", "Windows startupinfo")
def test_process_startupinfo(self):
async def test():
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE

proc = await asyncio.create_subprocess_exec(
sys.executable,
"-c",
"import sys; sys.stdout.write(sys.stdin.read())",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
startupinfo=startupinfo,
)

out, err = await proc.communicate(b"startupinfo")
self.assertEqual(out, b"startupinfo")
self.assertEqual(err, b"")
self.assertEqual(proc.returncode, 0)

self.loop.run_until_complete(test())

def get_num_fds(self):
# Winloop comment: use num_handles() as a meaningful
# substitute for num_fds() on Windows.
Expand Down Expand Up @@ -940,6 +964,29 @@ async def test():


class Test_UV_Process(_TestProcess, tb.UVTestCase):
@unittest.skipUnless(sys.platform == "win32", "Windows startupinfo")
def test_process_startupinfo_unsupported_flags(self):
async def test():
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESTDHANDLES

with self.assertRaisesRegex(ValueError, "startupinfo.dwFlags"):
await asyncio.create_subprocess_exec(
sys.executable, "-c", "pass", startupinfo=startupinfo,
)

self.loop.run_until_complete(test())

@unittest.skipIf(sys.platform == "win32", "POSIX startupinfo validation")
def test_process_startupinfo_posix(self):
async def test():
with self.assertRaisesRegex(ValueError, "startupinfo"):
await asyncio.create_subprocess_exec(
sys.executable, "-c", "pass", startupinfo=object(),
)

self.loop.run_until_complete(test())

@unittest.skipIf(sys.platform != "win32", "problem is windows related only")
def test_windows_issue_153(self):
"""Winloop issue 153 is related to bringing back the shlex parser
Expand Down
8 changes: 5 additions & 3 deletions winloop/handles/process.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ cdef class UVProcess(UVHandle):
cdef _init(self, Loop loop, list args, dict env, cwd,
start_new_session,
_stdin, _stdout, _stderr, pass_fds,
debug_flags, preexec_fn, restore_signals)
debug_flags, preexec_fn, restore_signals, startupinfo)

cdef _after_fork(self)

cdef char** __to_cstring_array(self, list arr)
cdef _init_args(self, list args)
cdef _init_env(self, dict env)
cdef _init_files(self, _stdin, _stdout, _stderr)
cdef _init_startupinfo(self, startupinfo)
cdef _init_options(self, list args, dict env, cwd, start_new_session,
_stdin, _stdout, _stderr, bint force_fork)
_stdin, _stdout, _stderr, bint force_fork,
startupinfo)

cdef _close_after_spawn(self, int fd)

Expand Down Expand Up @@ -77,4 +79,4 @@ cdef class UVProcessTransport(UVProcess):
_stdin, _stdout, _stderr, pass_fds,
waiter,
debug_flags,
preexec_fn, restore_signals)
preexec_fn, restore_signals, startupinfo)
40 changes: 35 additions & 5 deletions winloop/handles/process.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ cdef class UVProcess(UVHandle):
cdef _init(self, Loop loop, list args, dict env,
cwd, start_new_session,
_stdin, _stdout, _stderr, # std* can be defined as macros in C
pass_fds, debug_flags, preexec_fn, restore_signals):
pass_fds, debug_flags, preexec_fn, restore_signals,
startupinfo):

global __forking

Expand Down Expand Up @@ -58,7 +59,8 @@ cdef class UVProcess(UVHandle):

try:
self._init_options(args, env, cwd, start_new_session,
_stdin, _stdout, _stderr, force_fork)
_stdin, _stdout, _stderr, force_fork,
startupinfo)

restore_inheritable = set()
if pass_fds:
Expand Down Expand Up @@ -254,10 +256,12 @@ cdef class UVProcess(UVHandle):
return ret

cdef _init_options(self, list args, dict env, cwd, start_new_session,
_stdin, _stdout, _stderr, bint force_fork):
_stdin, _stdout, _stderr, bint force_fork,
startupinfo):

memset(&self.options, 0, sizeof(uv.uv_process_options_t))

self._init_startupinfo(startupinfo)
self._init_env(env)
self.options.env = self.uv_opt_env

Expand Down Expand Up @@ -307,6 +311,30 @@ cdef class UVProcess(UVHandle):
self.options.exit_cb = &__uvprocess_on_exit_callback

self._init_files(_stdin, _stdout, _stderr)
# Winloop needs startupinfo in order to work with playwright
# SEE: https://github.com/Vizonex/Winloop/issues/34
cdef _init_startupinfo(self, startupinfo):
Comment thread
Vizonex marked this conversation as resolved.
if startupinfo is None:
return
if not system.PLATFORM_IS_WINDOWS:
raise ValueError('startupinfo is not supported')
if startupinfo.dwFlags & ~subprocess_STARTF_USESHOWWINDOW:
raise ValueError(
'startupinfo.dwFlags only supports STARTF_USESHOWWINDOW')
attribute_list = startupinfo.lpAttributeList
if attribute_list and (
attribute_list.get('handle_list')
or set(attribute_list) != {'handle_list'}
):
raise ValueError(
'non-empty startupinfo.lpAttributeList is not supported')
if startupinfo.dwFlags & subprocess_STARTF_USESHOWWINDOW:
if startupinfo.wShowWindow != subprocess_SW_HIDE:
raise ValueError(
'startupinfo.wShowWindow only supports SW_HIDE')
# Match STARTF_USESHOWWINDOW with SW_HIDE, without changing
# whether the child has a console.
self.options.flags |= uv.UV_PROCESS_WINDOWS_HIDE_GUI

cdef _init_args(self, list args):
cdef:
Expand Down Expand Up @@ -644,7 +672,8 @@ cdef class UVProcessTransport(UVProcess):
waiter,
debug_flags,
preexec_fn,
restore_signals):
restore_signals,
startupinfo):

cdef UVProcessTransport handle
handle = UVProcessTransport.__new__(UVProcessTransport)
Expand All @@ -656,7 +685,8 @@ cdef class UVProcessTransport(UVProcess):
pass_fds,
debug_flags,
preexec_fn,
restore_signals)
restore_signals,
startupinfo)

if handle._init_futs:
handle._stdio_ready = 0
Expand Down
2 changes: 2 additions & 0 deletions winloop/includes/stdlib.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ cdef threading_main_thread = threading.main_thread
cdef int subprocess_PIPE = subprocess.PIPE
cdef int subprocess_STDOUT = subprocess.STDOUT
cdef int subprocess_DEVNULL = subprocess.DEVNULL
cdef int subprocess_STARTF_USESHOWWINDOW = getattr(subprocess, "STARTF_USESHOWWINDOW", 0)
cdef int subprocess_SW_HIDE = getattr(subprocess, "SW_HIDE", 0)
cdef subprocess_SubprocessError = subprocess.SubprocessError

cdef int signal_SIGABRT = signal.SIGABRT
Expand Down
3 changes: 2 additions & 1 deletion winloop/includes/uv.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ cdef extern from "uv.h" nogil:
UV_PROCESS_SETGID = 2,
UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = 4,
UV_PROCESS_DETACHED = 8,
UV_PROCESS_WINDOWS_HIDE = 16
UV_PROCESS_WINDOWS_HIDE = 16,
UV_PROCESS_WINDOWS_HIDE_GUI = 64

ctypedef enum uv_stdio_flags:
UV_IGNORE = 0x00,
Expand Down
5 changes: 2 additions & 3 deletions winloop/loop.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2825,8 +2825,6 @@ cdef class Loop:
raise ValueError("universal_newlines must be False")
if bufsize != 0:
raise ValueError("bufsize must be 0")
if startupinfo is not None:
raise ValueError('startupinfo is not supported')
if creationflags != 0:
raise ValueError('creationflags is not supported')

Expand All @@ -2848,7 +2846,8 @@ cdef class Loop:
waiter,
debug_flags,
preexec_fn,
restore_signals)
restore_signals,
startupinfo)

try:
await waiter
Expand Down
Loading