Print and pexpect logging

ilya1725 picture ilya1725 · Nov 27, 2012 · Viewed 18.9k times · Source

I have a bit of a code that uses pexpect to control a process and some prints in the code. The main goal (in this question) is to have the pexpect output and prints logged into some log file. The problem I ran into is that the pexpect lines (data sent and received) is mingled with the prints with no apparent logic. I was expecting that the print strings and pexpect outputs will be logged in the order they were issued.

Sample code is the following:

#!/usr/bin/env python

import pexpect
import time, sys, os

###############################################################################
# Subclass of file object to avoid recording extensive whitespace characters
class CleanFile(file):
    def write (self, text):
        # Remove the whitespaces
        out_text = ''
        # process the backspace properly
        bline = ''
        for c in text:
            if (ord(c) == 0x8):
                if (len(bline) == 0):
                    # Move the file pointer.
                    file.seek(self, -1, os.SEEK_CUR);
                else:
                    bline = bline[:-1]
            else:
                bline += c

        # remove whitespaces from inside a line
        out_text += ''.join(c for c in bline if (ord(c) >= 32 or ord(c) == 10));

        file.write(self, out_text);

###############################################################################
def main():
    fout = CleanFile ("options.log_file.log", 'w')

    sys.stdout = os.fdopen (sys.stdout.fileno(), 'w', 0)
    os.dup2 (fout.fileno(), sys.stdout.fileno());

    p = pexpect.spawn ('tclsh')
    p.logfile = fout

    print "Got into tclsh."
    p.sendline('ls');
    p.expect (['%',pexpect.EOF])

    p.sendline('info tclversion');
    p.expect (['%',pexpect.EOF])

    print "Got the version\n"

    p.sendline('info commands %');
    p.expect (['%',pexpect.EOF])

    p.sendline('exit');

    print 'Ended session'

###############################################################################
if __name__ == "__main__":
    main()

This is the output log file contents:

Got into tclsh.
ls
% lsinfo tclversion

log  options.log_file.log  pexpect_test.py  runtests.py  runtests_steinway.py
% info tclversionGot the version

info commands %

8.4
% info commands %exit
Ended session

Is there any way to make the pexpect and print outputs sequential?


Update: Based on the pexpect manual page: "Please note, however, that buffering can affect this behavior, since input arrives in unpredictable chunks". So it may potentially affect the logging.

Answer

Edu picture Edu · Nov 29, 2012

If you can wait until the script ends for the results, don't set a log file for pexpect commands, save results of the commands to variables, and print everything in the end.

Note also that you are missing the output of the info commands command. This can be fixed by adding one expect() for waiting the tclsh interpreter to start and removing the '%' from the end of the command. I assumed that was a typo.

Modify main function to be:

def main():                                                          
    fout = CleanFile ("options.log_file.log", 'w')                   

    sys.stdout = os.fdopen (sys.stdout.fileno(), 'w', 0)             
    os.dup2 (fout.fileno(), sys.stdout.fileno());                    

    p = pexpect.spawn ('tclsh')                                      
    p.expect (['%',pexpect.EOF])                                     

    p.sendline('ls');                                                
    p.expect (['%',pexpect.EOF])                                     
    ls = p.before                                                    

    p.sendline('info tclversion');                                   
    p.expect (['%',pexpect.EOF])                                     
    tclversion = p.before                                            

    p.sendline('info commands');                                     
    p.expect (['%',pexpect.EOF])                                     
    commands = p.before                                              

    p.sendline('exit');                                              
    p.close()                                                        

    print "Got into tclsh."                                          
    print ls                                                         
    print tclversion                                                 
    print "Got the version\n"                                        
    print commands                                                   
    print "Ended session"                                            

The output is then:

Got into tclsh.                                                      
 ls                                                                  
options.log_file.log  pexpect_test.py                                

 info tclversion                                                     
8.5                                                                  

Got the version                                                      

 info commands                                           
tell socket subst open eof pwd glob list pid exec auto_load_index time unknown  
eval lassign lrange fblocked lsearch auto_import gets case lappend proc break v 
ariable llength auto_execok return linsert error catch clock info split array i 
f fconfigure concat join lreplace source fcopy global switch auto_qualify updat 
e close cd for auto_load file append lreverse format unload read package set bi 
nary namespace scan apply trace seek while chan flush after vwait dict continue 
 uplevel foreach lset rename fileevent regexp lrepeat upvar encoding expr unset 
 load regsub history interp exit puts incr lindex lsort tclLog string           

Ended session