PyWinPTY
PyWinPTY provides pseudo-terminal support for Windows, allowing Python applications to create and communicate with console processes via input and output pipes. It leverages both the native ConPTY interface and the fallback winpty library. The current version is 3.0.3, with minor releases for dependency updates and bug fixes, and a major release (3.0.0) focusing on performance and async compatibility.
Warnings
- breaking The `read()` method on `PtyProcess` no longer accepts a `num_bytes` parameter as of v3.0.0. It now returns all available bytes in the stream, requiring downstream consumers to handle buffering.
- gotcha Direct `pip install pywinpty` may fail if the appropriate wheel for your Python version and Windows architecture (e.g., 32-bit Python or a pre-release Python 3.14) is not available on PyPI, leading to an attempt to build from source. Building from source requires a Rust toolchain, MSVC, and potentially `nuget.exe` to be correctly configured and in your system PATH.
- gotcha When running Jupyter Notebooks on Windows, `pywinpty` is often a dependency. Issues with `pywinpty` installation can manifest as 'Terminals not available' errors or `ModuleNotFoundError: No module named 'winpty.cywinpty'`.
Install
-
pip install pywinpty
Imports
- PtyProcess
from winpty import PtyProcess
- PTY
from winpty import PTY
Quickstart
import os
import time
from winpty import PtyProcess
# Spawn a Python interpreter in a pseudo-terminal
# For demonstration, we use 'python', but it could be 'cmd.exe' or 'powershell.exe'
proc = PtyProcess.spawn('python')
# Write some commands to the pseudo-terminal
proc.write('import sys\r\n')
proc.write('print("Hello from pywinpty!")\r\n')
proc.write('print(sys.version)\r\n')
proc.write('exit()\r\n')
# Read output until the process exits
output = []
while proc.isalive():
try:
# read() in v3.0.0+ does not accept num_bytes; reads all available.
chunk = proc.read().decode('utf-8', errors='ignore')
if chunk:
output.append(chunk)
# Give the process a moment to produce more output, prevent busy-waiting
time.sleep(0.01)
except EOFError:
break
# Ensure all remaining output is read after process might have died but before stream closes
# (though read() should ideally handle this)
try:
final_chunk = proc.read().decode('utf-8', errors='ignore')
if final_chunk:
output.append(final_chunk)
except EOFError:
pass
proc.close()
print("\n--- Captured Output ---")
print(''.join(output))
# Example of sending a control character (e.g., Ctrl+C)
# Uncomment to test, but will interrupt the spawned process
# proc = PtyProcess.spawn('cmd.exe') # or 'python'
# proc.sendcontrol('c') # Sends Ctrl+C
# proc.close()