pty.spawn() and friends
James T. Dennis
jadestar at idiom.com
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
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
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