pty.spawn() and friends

James T. Dennis jadestar at
Sat Dec 29 17:56:18 CET 2001

 I've been trying to learn more Python and I've been sticking with 
 the somewhat obscure kinds of things that I've routinely needed to
 do as a system administrator.  I came across the pty module as a
 primitive alternative to using expect and it seems like I should
 be able to so simple work with it.

 So naturally I tried to implement the simplext expect script
 (autopasswd) as a Python script using the pty.spawn() function.

 This doesn't work as I'd expect.  First the documentation states
 that pty.spawn():

	spawn a process, and connect its controlling terminal with 
	the current process's [sic] io [sic].  This is often used to 
	baffle programs with insist on reading from the controlling

 ... well it certainly baffles the programmer that tries to use it.

 My expectation was that this would spawn the program in question
 ("/usr/bin/passwd" for my tests) and allow me to print to its input
 and read from its output; under programmatic control.  I would thus
 have to cross-wire the inputs and outputs of the child process with
 some sort of handle, object, or file descriptor on my (parent) process.

 However it seem like pty.spawn() doesn't do any cross-wiring.  It
 seems to simply start a sub-process that interacts with my terminal,
 basically like the os.system() function.  (The difference is that
 pty.spawn() takes a tuple of arguments while os.system() takes a 
 string and passes it to a shell for parsing and execution).

 That seems pretty lame.  It's like the author of the pty module
 didn't know what the author of the os module was doing and provided a
 gratuitously similar function with no actual relevance to psuedo-ttys
 and no additional functionality.  In so doing, they've raised 
 (my) expectations and failed to satisfy them.

 What pty.spawn() *should* do (to meet with reasonable expectations
 that it would have something to do with spawning subprocesses *UNDER
 THE CONTROL OF A psudo-tty*) is return a file-like object to which
 one can write (commands or "keystrokes") and from which one could read
 (responses, prompts, potentially screen updates).  

 Ideally this would come with some convenience methods, and 
 members/constants (possibly inherited from a common module with 
 curses, and/or tty, to allow one to send keystrokes by their symbol 
 names (rather than having to manually fuss with escape sequences) and 
 receive data as structures of character/attribute data).  (Imagine 
 being able to run a child ncurses monitoring process and automatically
 programmatically respond to a specific bit of text "turning red" or
 "going on the blink").

 None of this would be nearly so irritating if USAGE EXAMPLES had been 
 provide to explain what the programmer should expect of the program.
 Naturally I had to fuss with it for some time before figure out that
 it really was a lame and redundant function rather than my own 
 lack of skill.  The fact that the reference docs give a couple of
 optional arguments: [,master_read[,stdin_read]] which are supposed 
 to be:

		functions which read from a file-descriptor

 ... complicates it further.  What kind of function could I write 
 that "reads from a file-descriptor" but which takes no arguments 
 telling it *which* file-descriptor! 
 At first I thought this was trying to say to call pty.spawn() with
 one argument to act sort of like os.system(), or two arguments
 (a tuple and a function invocation) to spawn a process under the
 control of that function.  (Envisioning something where *that function's*
 stdin and stdout where wired up to the spawned process' stdout and 
 stdin respectively).  (If that was the case I'd expect that the optional
 third argument would be another controlling function for handling 
 the child process' stderr; communications between the two would be
 "merely a matter of programming").

 Ugh!  Between this and the equally frustrating Python curses docs
 I'm pretty grumpy.

More information about the Python-list mailing list