Skip to content

Add Windows support#75

Open
itzrnvr wants to merge 1 commit into
googlecolab:mainfrom
itzrnvr:windows-support
Open

Add Windows support#75
itzrnvr wants to merge 1 commit into
googlecolab:mainfrom
itzrnvr:windows-support

Conversation

@itzrnvr

@itzrnvr itzrnvr commented Jul 5, 2026

Copy link
Copy Markdown

@## What

The CLI was entirely broken on Windows. src/colab_cli/console.py did unguarded module-level import termios / import tty, and since commands/execution.py imports connect_console from it at load time (cli.py:26), import colab_cli.cli raised ModuleNotFoundError: No module named termios on Windows. This killed every command (colab --help crashed) and made the entire pytest suite fail at collection (every test file does from colab_cli.cli import app). The README explicitly stated "Windows is not supported at this time."

Changes

  • console.py — platform-guard the termios/tty imports (termios = tty = None on win32). connect_console now dispatches: POSIX keeps the existing termios/tty/SIGWINCH path; Windows uses a new ctypes raw-console path. The piped-stdin (isatty() == False) path is unchanged and cross-platform.
  • _winconsole.py (new) — stdlib-ctypes Windows console helpers: a raw_mode() context manager that saves/restores CONIN$/CONOUT$ modes, enables ENABLE_VIRTUAL_TERMINAL_INPUT (disabling echo/line/processed input) and ENABLE_VIRTUAL_TERMINAL_PROCESSING on output, plus get_console_size() via GetConsoleScreenBufferInfo. No pywin32 dependency.
  • console.py resize handling — Windows has no SIGWINCH; a daemon thread polls GetConsoleScreenBufferInfo every 0.25s and calls send_terminal_size(ws) on change.
  • repl.py — defer PromptSession construction from ColabREPL.__init__ to run() (lazy, if self.session is None). prompt_toolkit's Win32Output calls GetConsoleScreenBufferInfo at PromptSession init and raised ctypes.ArgumentError under captured/non-tty stdout on Windows; constructing eagerly broke colab repl and 11 unit tests. Interactive use on a real Windows console is unaffected (run() builds the session against the real console).
  • commands/files.pyedit() switched from NamedTemporaryFile(delete=True) (held an exclusive handle on Windows → open(path,"rb") for hashing and the editor raised PermissionError) to delete=False + close + manual os.unlink in finally.
  • commands/automation.pydrivemount's open("/dev/tty") (FileNotFoundError on Windows) replaced with _read_line_from_controlling_tty(), which tries /dev/tty and falls back to sys.stdin.readline() on Windows.
  • Tests — guarded POSIX termios/tty console tests with skipif(sys.platform == "win32"); added Windows console-mode set/restore tests (against the real CONIN$/CONOUT$); added cross-platform /dev/tty fallback tests; made _extract_command_names robust to both rounded (/) and square (/) Rich help boxes and to wrapped description continuation lines (Windows renders square boxes and wraps VM/available onto their own lines).
  • README.md / docs/02_execution_and_interactive.md — document Windows support and the Unix-only shebang (#!/usr/bin/env -S colab run / chmod +x) limitation.

Already Windows-handled (intentionally not touched)

spawn_keep_alive (DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP), state.py (filelock.ReadWriteLock, not fcntl), ~/.config/colab-cli paths (functional on Windows via ~C:\\Users\\<user>), common.kill_process (SIGTERMTerminateProcess), auto_update.is_self_install_supported() (deliberately gated).

Verification (on Windows 11, Python 3.12.10)

  • uv run python -c "import colab_cli.cli"IMPORT OK (was: ModuleNotFoundError: No module named termios)
  • uv run colab --help / version / repl --help / console --help / edit --help / drivemount --help / update → all run, exit 0
  • uv run pytest tests/ -q237 passed, 1 skipped (was: 15 collection errors). The 1 skip is the POSIX termios-based console test on Windows.
  • uv run ruff check src/colab_cli tests → clean
  • _read_line_from_controlling_tty() verified on Windows: open("/dev/tty") raises OSError → falls back to sys.stdin.readline().
  • _winconsole.raw_mode() verified against the real Windows console: input mode has ENABLE_VIRTUAL_TERMINAL_INPUT set and ENABLE_LINE_INPUT cleared mid-flight, restored on exit.

Not verified end-to-end

Interactive colab console / colab repl against a live Colab VM require OAuth + a billable VM allocation, which I can't provision autonomously. The raw-console ctypes path and command wiring are unit-tested and --help-verified; a live interactive session should be run by a reviewer on Windows Terminal to confirm end-to-end. Piped colab console/repl (non-VM) reach the websocket/prompt layer correctly.

Review with git diff main..windows-support.
@

The CLI was entirely broken on Windows: console.py did unguarded
module-level /, which crashed at CLI
startup (ModuleNotFoundError) since execution.py imports connect_console
at load time. Every test file doing
failed at pytest collection.

Fixes:
- console.py: platform-guard the termios/tty imports; on win32 dispatch
  to a new ctypes-based raw-console path in _winconsole.py (CONIN$/CONOUT$
  mode save/restore, ENABLE_VIRTUAL_TERMINAL_INPUT raw input, VT output
  processing, daemon resize-poll thread replacing SIGWINCH). Piped-stdin
  path unchanged and cross-platform.
- _winconsole.py: new stdlib-ctypes Windows console helpers.
- repl.py: defer PromptSession construction from __init__ to run() so
  ColabREPL does not require a real console at construction time
  (prompt_toolkit Win32Output raised under captured stdout on Windows).
- commands/files.py: edit() now uses NamedTemporaryFile(delete=False) +
  close + manual unlink so hashing/reopen/edit don't hit PermissionError
  from Windows' exclusive temp-file handle.
- commands/automation.py: replace open('/dev/tty') in drivemount with a
  cross-platform helper that falls back to sys.stdin on Windows.
- tests: guard POSIX termios/tty console tests with skipif(win32); add
  Windows console-mode tests; add cross-platform /dev/tty fallback tests;
  make the help-command parser robust to square Rich boxes and wrapped
  description continuation lines.
- README.md / docs/02: document Windows support and the Unix-only
  shebang limitation.

Verified on Windows 11 / Python 3.12.10: , ,
, , Usage: edit [OPTIONS] [FILE[:LINE[:COLUMN]]]
Options:
    -h, --help       Print this help message
    -v, --version    Print the version number

Arguments:
    FILE[:LINE[:COLUMN]]    The file to open, optionally with line and column (e.g., foo.txt:123:45)
, ,
Usage: Squirrel.exe command [OPTS]
Manages Squirrel packages

Commands
      --install=VALUE        Install the app whose package is in the specified
                               directory
      --uninstall            Uninstall the app the same dir as Update.exe
      --download=VALUE       Download the releases specified by the URL and
                               write new results to stdout as JSON
      --checkForUpdate=VALUE Check for one available update and writes new
                               results to stdout as JSON
      --update=VALUE         Update the application to the latest remote
                               version specified by URL
      --releasify=VALUE      Update or generate a releases directory with a
                               given NuGet package
      --createShortcut=VALUE Create a shortcut for the given executable name
      --removeShortcut=VALUE Remove a shortcut for the given executable name
      --updateSelf=VALUE     Copy the currently executing Update.exe into the
                               default location

Options:
  -h, -?, --help             Display Help and exit
  -r, --releaseDir=VALUE     Path to a release directory to use with releasify
  -p, --packagesDir=VALUE    Path to the NuGet Packages directory for C# apps
      --bootstrapperExe=VALUE
                             Path to the Setup.exe to use as a template
  -g, --loadingGif=VALUE     Path to an animated GIF to be displayed during
                               installation
  -i, --icon=VALUE           Path to an ICO file that will be used for icon
                               shortcuts
      --setupIcon=VALUE      Path to an ICO file that will be used for the
                               Setup executable's icon
  -n, --signWithParams=VALUE Sign the installer via SignTool.exe with the
                               parameters given
  -s, --silent               Silent install
  -l, --shortcut-locations=VALUE
                             Comma-separated string of shortcut locations, e.g.
                               'Desktop,StartMenu'
      --no-msi               Don't generate an MSI package
      --no-delta             Don't generate delta packages to save time
      --framework-version=VALUE
                             Set the required .NET framework version, e.g.
                               net461
      --msi-win64            Mark the MSI as 64-bit, which is useful in
                               Enterprise deployment scenarios
 all run; full suite 237 passed, 1 skipped (POSIX termios test).
@google-cla

google-cla Bot commented Jul 5, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@itzrnvr

itzrnvr commented Jul 5, 2026

Copy link
Copy Markdown
Author

Live end-to-end verification on Windows

Verified against real Colab VMs (free tier) on Windows 11, after uv tool install of this branch (0.6.1.dev3+g354692959):

Auth (oauth2 remote copy-paste flow)colab sessions → URL printed, code pasted, token cached at ~/.config/colab-cli/token.json. ✅

CPU VM

  • colab new -s wincpu → Session READY (~16s) ✅
  • colab exec -s wincpu -f examples/_win_smoke_cpu.pyhello from colab-cli windows e2e / python: 3.12.13 | host os: Linux / cwd: /content
  • colab log -s wincpu -n 3 → recorded EXEC + console_started events ✅
  • Piped echo "print(1+1)" | colab repl -s wincpu2 (lazy PromptSession fix) ✅
  • Piped printf 'uname -a\r\n...' | colab console -s wincpu → connected via the Windows websocket TTY path, remote uname -a returned Linux e38600fadf01 6.6.122+ ... x86_64 GNU/Linux, tmux status bar rendered, clean close ✅
  • colab stop -s wincpu → terminated ✅

Free-tier T4 GPU VM

  • colab new -s wingpu --gpu T4 → Session READY (~21s) ✅
  • colab exec -s wingpu -f examples/_win_smoke_gpu.pytorch: 2.11.0+cu128 / cuda available: True / device: Tesla T4 / matmul sum ok: True
  • colab stop -s wingpu → terminated ✅

Ephemeral job

  • colab run --gpu T4 examples/_win_smoke_gpu.py → provisioned fresh T4 (run-e061d2), ran the script (CUDA True, Tesla T4, matmul ok), auto-tore-down ✅

Cleanupcolab sessionsNo active sessions found on server. (no orphans) ✅

Not covered: the interactive (raw-TTY, human-at-keyboard) colab console/repl variants can'''t be automated, but the piped variants above exercise the same Windows websocket/PTY code paths against a live VM, and _winconsole.raw_mode() is unit-tested against the real Windows console (mode set/restore).

DGJK2301 added a commit to DGJK2301/google-colab-cli that referenced this pull request Jul 19, 2026
Windows automation support based on upstream PR googlecolab#75
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant