ptyprocess
repository·master·Indexed 42 days ago
https://github.com/pexpect/ptyprocessA library for launching subprocesses in a pseudo terminal (pty), enabling interaction with programs that require a terminal environment, such as those using curses or those that behave differently when detecting a pipe instead of a TTY. It provides the PtyProcess class for byte-based interaction and PtyProcessUnicode for string-based interaction, allowing developers to control terminal settings like echo, window size, and control signals.
What's inside ptyprocess
- ptyprocess allows you to launch a subprocess in a pseudo terminal (pty) and interact with both the process and its pty. This is useful for automating processes that require a terminal environment, such as those with password prompts that bypass stdin, output that changes when piped, or curses-style terminal interfaces.
Understand the difference between a PTY and a Pipe
masterA pseudo-terminal (pty) is a kernel-level object that allows bidirectional data flow through a single interface.
Key differences from standard pipes (like those used with
subprocess.Popen):- Bidirectional Communication: Unlike a pipe which typically requires separate streams for stdin, stdout, and stderr, a single pty connects all three standard streams of a child process.
- Terminal Awareness: Programs using
isatty()will detect a pty and switch to 'interactive' mode. This means they may format output differently (e.g.,lsusing columns) or prompt for user confirmation, whereas they would behave differently when connected to a standard pipe. - Window Management: A pty tracks window size (rows and columns) and sends a
SIGWINCHsignal to child processes when the size changes. - Cooked Mode: In cooked mode, the pty processes input data. For example, sending the byte
03(Ctrl-C) will cause the pty to send aSIGINTsignal to the child process.
Basic usage of PtyProcessUnicode
masterTo interact with a subprocess using a pseudo terminal, use
PtyProcessUnicode.spawn()to launch the process. You can then use.read(n)to read up toncharacters from the terminal and.write(string)to send input to the process.from ptyprocess import PtyProcessUnicode # Launch a subprocess (e.g., the python interpreter) p = PtyProcessUnicode.spawn(['python']) # Read characters from the pty p.read(20) # Write input to the process p.write('6+6\n') # Read the result p.read(20)Write data to a PtyProcess instance
masterUse
write(data)to send strings or bytes to the subprocess's standard input.process.write('command_to_run\n')Read data from a PtyProcess instance
masterUse the following methods to retrieve data from the subprocess's output:
read(n): Reads up toncharacters from the terminal.readline(): Reads a single line from the terminal.
Control terminal signals and EOF in PtyProcess
masterYou can send specific control signals to the subprocess using these methods:
sendcontrol(char): Sends a control character.sendeof(): Sends an End-Of-File (EOF) signal.sendintr(): Sends an interrupt signal (equivalent to Ctrl-C).
Manage terminal echo settings in PtyProcess
masterTo control whether characters sent to the terminal are echoed back, use:
getecho(): Returns the current echo state.setecho(bool): Sets the echo state.waitnoecho(): Waits for the terminal to stop echoing.
Monitor and manage process lifecycle in PtyProcess
masterUse these methods to check the status or terminate the subprocess:
eof(): ReturnsTrueif the EOF has been reached.isalive(): ReturnsTrueif the process is still running.wait(): Waits for the process to exit.kill(): Kills the process.terminate(): Terminates the process.close(): Closes the connection to the process.
Use the PtyProcess class to interact with subprocesses
masterThe
PtyProcessclass is the primary interface for launching a subprocess in a pseudo-terminal (pty) and interacting with its standard input and output. You can use it to spawn processes, read/write data, and control terminal settings like echo and window size.import ptyprocess # Example usage pattern process = ptyprocess.PtyProcess.spawn('/bin/bash') process.write('ls\n') output = process.read(1024) process.close()Manage terminal window size in PtyProcess
masterTo handle terminal dimensions, use:
getwinsize(): Returns the current window size (rows, columns).setwinsize(rows, cols): Sets the terminal window size.
Read and write data using PtyProcess
masterThe
PtyProcessclass provides methods to interact with the subprocess via its pseudo-terminal using bytes.write(s, flush=True): Writes bytessto the pseudoterminal. Returns the number of bytes written.read(size=1024): Reads up tosizebytes from the pty. This is a blocking call. RaisesEOFErrorif the terminal is closed.readline(): Reads one line from the pseudoterminal and returns it as bytes. This is a blocking call. RaisesEOFErrorif the terminal is closed.sendcontrol(char): Helper to send control characters (e.g.,child.sendcontrol('g')for Ctrl-G).sendeof(): Sends an EOF character (typically Ctrl-D).sendintr(): Sends an interrupt character (typically Ctrl-C).
# Writing bytes child.write(b'hello\n') # Reading bytes data = child.read(1024) line = child.readline() # Sending control characters child.sendcontrol('c') # Sends Ctrl-CSpawn a subprocess with PtyProcess.spawn()
masterThe
PtyProcess.spawn()class method is the primary way to start a command in a pseudo-terminal. It handles the fork/exec logic and returns an instance ofPtyProcessthat you can use to interact with the child process.Parameters:
argv: A list or tuple of strings representing the command and its arguments.cwd: (Optional) The directory to launch the process in.env: (Optional) A dictionary of environment variables.echo: (Optional) Boolean indicating whether to enable terminal echo. Defaults toTrue.preexec_fn: (Optional) A function to be called in the child process beforeexec().dimensions: (Optional) A tuple(rows, cols)for the terminal size. Defaults to(24, 80).pass_fds: (Optional) A list of file descriptors to keep open in the child process.
Note: If
exec()fails,spawn()will raise the correspondingOSErrorin the parent process.from ptyprocess import PtyProcess # Example: Spawning a simple shell child = PtyProcess.spawn(['/bin/bash']) # You can then interact with it child.write(b'ls\n') print(child.read(1024))