Traceback (most recent call last):
[/example]
That >>> tells us that you are running inside the interactive PYTHON
interpreter. That means you now have TWO processes trying to read from
the SAME keyboard/console window, and they are likely going to be taking
turns... Python is going to put out its prompt while the subprocess is
starting, and queues ITS read statement -- the subprocess then puts out
ITS prompt and queues a read. You type something, and the FIRST read
sees it -- the Python interactive interpreter.
Here's the latest mod on my massive test sample... No changes to the
"shell" class -- it was already rigged to pipe data in both directions
(the subprocess is NOT trying to read the keyboard/console). Instead of
passing in canned test commands I now made a loop that does a "capture"
until "None" is returned (time-out), then the PYTHON program prompts for
a command to be sent on to the subprocess.
-=-=-=-=-=-=-=-=-
import subprocess
import os
import threading
import Queue
class Shell(object):
def __init__(self, command):
# flag for shutdown
self.die = False
self.timeOut = 1.0
# queue for buffering "lines"
self.lineQueue = Queue.Queue()
# list for collecting characters read from subprocess
# allows for character reader to collect characters
# while line reader processes a prior batch
self.charList = []
# event to signal that, at least, one character is
# available. event is used to allow time-out wait()
self.charEvent = threading.Event()
# event to signal that something has been written to the
# subprocess, and that the line reader should start
# collecting stuff (this is used to avoid multiple "prompt"
# timeouts from falsely affecting the user
self.lineEvent = threading.Event()
# lock to wrap access to self.charList
self.charLock = threading.Lock()
# create subprocess object with supplied command
self.process = subprocess.Popen(command,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
# initialize, and start the character reader
# thread -- this is the thread that actually reads the
# subprocess output stream, one character at a time
self.charThread = threading.Thread(target=self.readChar)
self.charThread.start()
# initialize, and start the line reader thread
# -- this is the thread that processes collected characters
# OR on a timeout of the event, queues up a prompt and
# then busy-waits until the queue is empty (meaning the
# user application has processed up to
self.lineThread = threading.Thread(target=self.readLine)
self.lineThread.start()
# set the line event so the line reader knows it should read
up
# to the next time-out/prompt
self.lineEvent.set()
def readChar(self):
while not self.die:
# read one character at a time from subprocess; blocks
ch = self.process.stdout.read(1)
# get the lock to access the character list, append it
self.charLock.acquire()
self.charList.append(ch)
# signal the line reader that, at least one, character
# is in the list
self.charEvent.set()
# let the line reader access the character list
self.charLock.release()
def readLine(self):
lineChar = []
while not self.die:
# block until told that the subprocess has been commanded
# and thereby should be producing some sort of output
self.lineEvent.wait()
# block for up to one second waiting for the character
# reader to produce some sort of output
self.charEvent.wait(self.timeOut)
# get the lock allowing access to the character list
self.charLock.acquire()
# clear the character reader event so the next pass
# will block(time-out) if no more data
self.charEvent.clear()
# save the character list locally so the character reader
# can add more stuff to it (after clearing it)
tCharList = self.charList
self.charList = []
# release the character list lock so reader can touch it
self.charLock.release()
# if the list is not empty, we have some data
if tCharList:
# process each character looking for a new line
character
# note: no filtering is done, all characters will be
# returned to user
for ch in tCharList:
lineChar.append(ch)
# on a new line, collect all preceding and make a
# string out of them, queue the string
if ch == "\n":
self.lineQueue.put("".join(lineChar))
lineChar = []
else:
# time out, assume prompt stage, queue partial line
self.lineQueue.put("".join(lineChar))
lineChar = []
# queue None as signal
self.lineQueue.put(None)
# AND clear the line event so we don't try to read
# anymore data (and get time-out/prompts) until the
# user has sent some command
self.lineEvent.clear()
def input(self):
# return the next available data line (which may be None for
# time-out/prompt. This will BLOCK if called when the queue
# is empty
return self.lineQueue.get()
def empty(self):
# return the state of the line queue
return self.lineQueue.empty()
def send(self, data, timeout = 1.0):
# send the data to the subprocess
self.process.stdin.write(data)
# reset the timeout value -- this allows the user to specify
# a long timeout for known number crunchers so that one
# doesn't get an errorneous (early) "prompt" indication
self.timeOut = timeout
# signal the line reader that data has been sent, and it
# should start processing return data
self.lineEvent.set()
def shutdown(self):
self.die = True
self.charEvent.set()
self.charThread.join()
self.lineEvent.set()
self.lineThread.join()
myCommand = Shell("cmd.exe")
while True:
try:
print "\n***** Start capture"
while True:
ln = myCommand.input()
if not ln: break
print ln,
print "\n***** End capture"
UserData = raw_input("\nApplication Prompt (<ctrl-z> to exit)>
")
myCommand.send(UserData + "\n")
except:
break
myCommand.send("exit\n")
myCommand.shutdown()
-=-=-=-=-=-=-
E:\UserData\Dennis Lee Bieber\My Documents>python script2.py
***** Start capture
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
E:\UserData\Dennis Lee Bieber\My Documents>
***** End capture
Application Prompt (<ctrl-z> to exit)> dir
***** Start capture
dir
Volume in drive E is Data
Volume Serial Number is 2626-D991
Directory of E:\UserData\Dennis Lee Bieber\My Documents
10/25/2005 09:03 PM <DIR> .
10/25/2005 09:03 PM <DIR> ..
07/25/2005 10:39 PM <DIR> .metadata
10/06/2005 09:54 AM <DIR> Ada Progs
08/13/2005 02:01 PM <DIR> Agent Data
10/21/2005 09:29 AM 421,820 apress_offer.pdf
07/03/2005 11:36 AM 132 cp.py
07/17/2005 12:25 PM <DIR> Cyberlink
07/06/2005 09:32 AM 102,400 db1.mdb
07/26/2005 12:20 AM 26,614 eclipse_code.xml
10/25/2005 08:06 PM <DIR> Eudora
06/24/2005 08:50 PM 667 fake_oosums.ads
06/24/2005 08:50 PM 695 fake_oosums.ali
09/06/2005 09:01 PM <DIR> Genealogy
07/13/2005 10:56 PM <DIR> HomeSite
05/08/2005 01:05 PM <DIR> Investing
10/21/2005 10:04 PM <DIR> Java Progs
08/04/2005 10:13 PM 162 main.py
10/11/2005 10:43 PM <DIR> My Downloads
05/01/2005 10:31 AM <DIR> My eBooks
04/22/2005 12:09 AM <DIR> My Music
07/10/2005 11:43 AM <DIR> My Pictures
06/29/2005 11:55 PM <DIR> My PSP Files
05/23/2005 09:30 AM <DIR> My Videos
05/01/2005 12:49 PM <DIR> Office Documents
06/27/2005 03:19 PM 7,961,778
org.eclipse.jdt.doc.user.I20050627-1435.pdf
06/27/2005 03:19 PM 6,791,109
org.eclipse.platform.doc.user.I20050627-1435.pdf
10/11/2005 10:52 PM 56 oth_tsr_rm_750.ram
07/20/2005 09:32 AM 108,457 parkerred15yc.jpg
09/03/2005 10:36 PM <DIR> Python Progs
10/25/2005 08:16 PM <DIR> Quicken
07/10/2005 12:09 PM 3,356,248 results.xml
06/11/2005 12:03 PM 935 Scout_Ship Database.lnk
07/03/2005 12:38 PM <DIR> Scout_Ship My Documents
10/24/2005 09:23 AM 971 Script1.py
09/25/2005 12:40 PM 1,107 Script1_old.py
10/25/2005 09:03 PM 6,352 Script2.py
08/28/2005 11:47 AM <DIR> SimpleMu Logs
06/24/2005 08:56 PM 1,201 student_pack.ads
06/24/2005 08:49 PM 1,144 student_pack.ads.0
06/24/2005 08:56 PM 1,342 student_pack.ali
08/02/2005 11:39 PM 4,096 t.DOC
06/20/2005 10:11 AM 104 t.rx
08/05/2005 08:41 PM 66,452 Untitled-1.tif
08/05/2005 08:41 PM <DIR> VCheck
10/03/2005 02:58 AM <DIR> Visual Studio
10/03/2005 02:51 AM <DIR> Visual Studio 2005
22 File(s) 18,853,842 bytes
25 Dir(s) 267,148,263,424 bytes free
E:\UserData\Dennis Lee Bieber\My Documents>
***** End capture
Application Prompt (<ctrl-z> to exit)> time
***** Start capture
time
The current time is: 21:04:25.74
Enter the new time:
***** End capture
Application Prompt (<ctrl-z> to exit)>
***** Start capture
E:\UserData\Dennis Lee Bieber\My Documents>
***** End capture
Application Prompt (<ctrl-z> to exit)> ^Z
E:\UserData\Dennis Lee Bieber\My Documents>
--