diff --git a/unsloth_cli/commands/studio.py b/unsloth_cli/commands/studio.py index d0d6e308c1..74f52feecc 100644 --- a/unsloth_cli/commands/studio.py +++ b/unsloth_cli/commands/studio.py @@ -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: