Expect Module for Python ------------------------ This is Python module that does most of what you would normally need from Tcl's expect, but it works like a standard popen(1) call but with BOTH reading AND writing. FTP: ---- ftp://ftp.obsidian.co.za/pub/expect/ Example: -------- #!/usr/bin/python import sys from expect import * f = popen2 ("passwd " + sys.argv[1]) print f.read ("word: ") f.write (sys.argv[2]) print f.read ("word: ") f.write (sys.argv[2]) print f.read () or #!/usr/bin/python import sys, time from expect import * f = popen2 ("passwd " + sys.argv[1], "p") print f.read (": ") time.sleep (0.1) f.write (sys.argv[2] + "\r\n") print f.read (":") time.sleep (0.1) f.write (sys.argv[2] + "\r\n") print f.read() Classes: -------- There are three classes defined by this module: popen - this is like a standard UNIX popen command the difference being that you can both read and write to the result file object. The stderr stream of the process is NOT redirected and will hence appear on the terminal exactly like with popen or back-quotes under sh. popen2 - like popen, but both stderr AND stdout of the process can be read through the same pipe. i.e. f.read() calls return ALL output of the process. popen3 - like popen2, but stderr and stdout are read through two separate functions: f.read() returns stdout of the process. f.read_error() returns stderr. How it works: ------------- This is a completely standalone implementation of popen. It does not rely on the existing popen in any way. The problem with reading and writing to a process is that it is easy to write code that blocks indefinitely waiting for IO. This library works by queueing reads when doing either a read or a write call. Read operations are buffered so that incoming data (the stderr and stdout of the process) are queued in anticipation of any future read(). write() operations are unbuffered. This allows you to arbitrarily read to and write from a process without worrying about an indefinite block. It is hence very similar to Tcl's expect program. Invocation: ----------- f = popen (command, options) f = popen2 (command, options) f = popen3 (command, options) command is a string. command is a shell program passed as to /bin/sh -c options is a string. If options contains the character `b' then the blocking is turned off. read() and write() will then not block waiting for data. This is untested. If options contains the character `p', then a pseudo tty is opened for the process. The process will still inherit all environment variables, hence for consistent behaviour, the caller may want to explicitly set the TERM environment variable. Note that many programs follow different behaviour depending on whether they are attached to a tty or not. In the passwd examples above, passwd is happy to read from a pipe. If you use a terminal (`p') then you are required to send a '\n' at the end of the line, and pause a small amount while the getpass C library function switches into ICANON mode (whatever that is) causing passwd to raw read separately from the normal read queue. All other letters passed to options are ignored. Methods: -------- f.read (integer) * Can take a normal integer to read a finite amount of data in the same way as usual file object's read does. f.read () * If no arguments are passed, then this reads as much data as is outputted by the process, blocking until it reads nothing (i.e. the process has exited). f.read (string) * If a single string is passed, then read() reads up to and including the matching string. f.read ((string, string, ...)) * If a tuple of strings are passed, then read() reads up to and including the first matching string. f.read_error () * works just like read. Typically you would check for errors with read_error() after calling read(). f.close () * closes all pipes and returns the exit code of the process. If the process has been killed, then this raises an error giving the terminating signal. f.flush () * does nothing f.isatty () * returns 0 f.readline () * reads up to an including a newline character. takes no arguments f.readlines () * analogous to file objects readline(). takes no arguments f.seek (offset, whence) * reads and discards offset amount of data. whence must be 1. returns nothing f.tell () * returns the total amount of data read from stdout (or in the case of popen2) from stdout and stderr. f.writelines (list) * analogous to file objects writelines (). returns nothing. f.pid () * get the pid of the child process. Returns -1 if the child has died. f.setblocking (block) * takes an integer. 1 turns blocking on, 0 turns blocking off. This overrides the options string. f.command * a copy of the command. Backquote notation: ------------------- Backquotes work the same as in sh. It does this by binding the the __repr__ method. Hence a=`popen ("echo hello")` Works just like a=`echo hello` under a sh or perl. Intricacies: ------------ - To save invocation of /bin/sh, popen checks if the command contains any shell special characters i.e. > ` ! < & $ \n ; ( ) { } and then, failing this, tries to interpret the command-line by itself, following the exact conventions of bash. If you are not convinced, add in a ; at the end of your command to force use of /bin/sh. - If you try to close a process that has stopped or still executing, close blocks waiting for the process to die. - del f is just like f.close, but sends the process the SIGTERM signal before blocking waiting for the child to die. Since a process can intercept the SIGTERM signal, its is still possible for Python to block at an arbitrary location. For instance: def func (): f = popen2 ("prog") time.sleep (0.1) func () If prog were to ignore SIGTERM, then func would block until prog had exited because python dereferences f before returning from func. (The reason you need to sleep(0.1) is to give prog a chance to set its signal handlers.) - f.p is the internal object that you would normally not be interested in. However `f.p` prints useful output like: f = popen2 ("echo 'Hi there!'") print `f.p` - You can carry on reading from a process even after it has died and been close()'d. So long as there is still data in the queue. If there is no data, read just returns an empty string. - Opening a pty currently works BSD style using /dev/pty?? To support other kinds, I'll probably copy some of the rxvt code across.