cli(windows): pass sys.stdio handles explicitly to powershell.exe
The previous Write-Host capture attempts (47432b0b-Command + *>&1 andf2c2b3f3[Console]::Out mirror in setup.ps1) still produced an empty update.log on windows-latest because the powershell.exe child had no stdio handles at all to write to. Root cause: subprocess.run on Windows with the default close_fds=True (Python 3.7+ default) sets bInheritHandles=False on CreateProcess. Combined with CREATE_NO_WINDOW (added by _windows_hidden_subprocess_ kwargs in non-TTY runs), the child gets: - no console (CREATE_NO_WINDOW) - no inherited std handles (bInheritHandles=False) GetStdHandle in the child returns INVALID_HANDLE_VALUE, so even [Console]::Out.WriteLine and Write-Output -- not just Write-Host -- write into the void. Fix: pass stdout=sys.stdout, stderr=sys.stderr (and stdin) when running the setup script on Windows. With explicit handles, Python's subprocess sets up PROC_THREAD_ATTRIBUTE_HANDLE_LIST containing the std handles + bInheritHandles=True, so the child inherits exactly the three std handles regardless of close_fds=True. CREATE_NO_WINDOW still applies (no transient console window), but the child can now write to the inherited stdout file handle, which lands on bash's `tee logs/update.log` in CI. A small _stream_for_subprocess helper guards against test harnesses that swap sys.stdout for a stream without a real fileno (pytest capsys, in-memory IO buffers, etc) -- those fall back to None so subprocess uses its default. Verified locally on PowerShell 7.4.6 / Linux that the explicit stdout handoff doesn't regress the existing direct-inherit path, and the marker line "prebuilt up to date and validated" reaches both the child's stdout and a parent `tee` consumer.
This commit is contained in:
parent
f2c2b3f327
commit
2453134084
1 changed files with 37 additions and 0 deletions
|
|
@ -133,6 +133,26 @@ def _windows_hidden_subprocess_kwargs() -> dict[str, object]:
|
|||
return kwargs
|
||||
|
||||
|
||||
def _stream_for_subprocess(stream):
|
||||
"""Return *stream* if it has a real OS file descriptor, else None.
|
||||
|
||||
subprocess.run on Windows refuses to inherit std handles unless
|
||||
they're passed explicitly (otherwise close_fds=True forces
|
||||
bInheritHandles=False, and a CREATE_NO_WINDOW child ends up with
|
||||
no stdio at all). When sys.stdout / sys.stderr is a real fd-backed
|
||||
stream we want to hand it through; when it's been captured by a
|
||||
test harness (pytest's capsys, an in-memory wrapper, etc) we fall
|
||||
back to None so subprocess uses its default.
|
||||
"""
|
||||
if stream is None:
|
||||
return None
|
||||
try:
|
||||
stream.fileno()
|
||||
except (AttributeError, OSError, ValueError):
|
||||
return None
|
||||
return stream
|
||||
|
||||
|
||||
def _studio_venv_python() -> Optional[Path]:
|
||||
"""Return the studio venv Python binary, or None if not set up."""
|
||||
if platform.system() == "Windows":
|
||||
|
|
@ -1015,9 +1035,26 @@ def _run_setup_script(*, verbose: bool = False) -> None:
|
|||
f"& '{script_pwsh_literal}' *>&1",
|
||||
]
|
||||
)
|
||||
# Explicitly hand stdin/stdout/stderr to the child so the
|
||||
# CI tee actually sees setup.ps1's output. Without this,
|
||||
# subprocess.run on Windows uses close_fds=True (default,
|
||||
# since Python 3.7) which sets bInheritHandles=False on
|
||||
# CreateProcess. With CREATE_NO_WINDOW also set (via
|
||||
# _windows_hidden_subprocess_kwargs in non-TTY runs), the
|
||||
# child has neither a console nor any inherited std
|
||||
# handles, so PowerShell's Write-Host -- and even
|
||||
# [Console]::Out.WriteLine -- writes to nothing. Passing
|
||||
# stdout=sys.stdout / stderr=sys.stderr makes Python set up
|
||||
# PROC_THREAD_ATTRIBUTE_HANDLE_LIST with the std handles
|
||||
# explicitly inheritable, which works alongside
|
||||
# CREATE_NO_WINDOW. Empty update.log on the windows-latest
|
||||
# CI was the smoking gun (run 25533694490 and 25534292239).
|
||||
result = subprocess.run(
|
||||
powershell_args,
|
||||
env = env,
|
||||
stdin = _stream_for_subprocess(sys.stdin),
|
||||
stdout = _stream_for_subprocess(sys.stdout),
|
||||
stderr = _stream_for_subprocess(sys.stderr),
|
||||
**_windows_hidden_subprocess_kwargs(),
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue