Skip to content

Arguments to the WinPTY backend don't work #518

@ofek

Description

@ofek

Script to reproduce:

import io
import os
import shutil
import signal
import sys
import tempfile
import threading
import time
from subprocess import list2cmdline

from winpty import PTY, Backend, WinptyError

READ_INTERVAL_SECONDS = 0.05


def capture(pty, writers, stop_event) -> None:
    while not stop_event.is_set():
        try:
            output = pty.read(io.DEFAULT_BUFFER_SIZE)
            if not output:
                time.sleep(READ_INTERVAL_SECONDS)
                continue

            for writer in writers:
                writer.write(output)
                writer.flush()
        except WinptyError:
            if pty.iseof():
                break

            continue


def main():
    command = list(sys.argv[1:])
    executable = command[0]
    if not os.path.isabs(executable):
        executable = shutil.which(executable)
        if not executable:
            raise FileNotFoundError(f"Executable not found: {executable}")

    args = command[1:]
    if args:
        # args.insert(0, "")
        args = list2cmdline(args)
    else:
        args = None

    width, height = shutil.get_terminal_size()
    pty = PTY(width, height, backend=Backend.WinPTY)
    pty.spawn(list2cmdline([executable]), cmdline=args)

    with tempfile.SpooledTemporaryFile(mode="w+", encoding="utf-8", newline="") as out:
        event = threading.Event()
        thread = threading.Thread(target=capture, args=(pty, [sys.stdout, out], event), daemon=True)
        thread.start()
        try:
            while not pty.iseof():
                time.sleep(0.1)
        except KeyboardInterrupt:
            os.kill(pty.pid, signal.SIGTERM)
            raise
        finally:
            # time.sleep(3)
            event.set()
            thread.join()

            if (exit_code := pty.get_exitstatus()) is None:
                exit_code = 1

            # out.seek(0)
            # output = out.read()
            # print(repr(output))

    return exit_code


if __name__ == "__main__":
    sys.exit(main())

Notice that the first argument somehow becomes malformed based on the current directory:

❯ python script.py python -c "import sys;print(sys.executable)"
-c: can't open file 'C:\\Users\\ofek\\Desktop\\code\\pywinpty\\import sys;print(sys.executable)': [Errno 2] No such file or directory

Now uncomment the args.insert(0, "") to erroneously add an extra preceding argument and watch it work:

❯ python script.py python -c "import sys;print(sys.executable)"
C:\Users\ofek\AppData\Local\Programs\Python\Python312\python.EXE

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions