I have an interactive console program in windows. I need to press keystroke like 'e' 'c' to the cmd window. It's convenient for human operating , but very difficult for program automation.
And now, I would like to wrap the console program, in order to make it more easy to be manipulated in other program like python.
However, the console program uses 'getch()' to get keyboard input , which is not standard input. So, I can't simply send key into stdin.
Did anyone ever come across this problem?
Thanks.
class ThreadWorker(threading.Thread):
def __init__(self, callable, *args, **kwargs):
super(ThreadWorker, self).__init__()
self.callable = callable
self.args = args
self.kwargs = kwargs
self.setDaemon(True)
def run(self):
try:
self.callable(*self.args, **self.kwargs)
except Exception, e:
print e
def start(self):
global running
running = True
threading.Thread.start(self)
def console_presskey(char):
proc.stdin.write(char)
proc.stdin.flush()
def start():
def worker(pipe):
while running:
line = pipe.readline()
if line == '':
break
else:
print line,
proc = Popen("something.exe",stdout=PIPE,stdin=PIPE)
stdout_worker = ThreadWorker(worker, proc.stdout)
stderr_worker = ThreadWorker(worker, proc.stderr)
stdout_worker.start()
stderr_worker.start()
if __name__ == "__main__":
start()
sleep(2)
console_presskey('e')
sleep(2)
console_presskey('c')
EDIT:
Finally, I use win32 SendMessage function to get things done. I forked a new subprocess, then hide it , get its hWnd and pid.
here is the code:
import threading
import re
import os
from subprocess import Popen, PIPE,STDOUT,CREATE_NEW_CONSOLE,STARTUPINFO,STARTF_USESHOWWINDOW,SW_HIDE
from time import sleep
import win32process,win32con,win32gui,win32api
TargetPower = 'N/A'
Mode = 'N/A'
Channel = 'N/A'
con_stdin = ''
con_stdout = ''
stdout_worker = ''
stdin_woker = ''
running = False
proc = ''
Console_hwnd = ''
#status = 'NotStarted' # or Running
class ThreadWorker(threading.Thread):
def __init__(self, callable, *args, **kwargs):
super(ThreadWorker, self).__init__()
self.callable = callable
self.args = args
self.kwargs = kwargs
self.setDaemon(True)
def run(self):
try:
self.callable(*self.args, **self.kwargs)
except Exception, e:
print e
def start(self):
global running
running = True
threading.Thread.start(self)
def get_hwnds_for_pid (pid):
def callback (hwnd, hwnds):
#if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
_, found_pid = win32process.GetWindowThreadProcessId(hwnd)
#print hwnd
if found_pid == pid:
hwnds.append(hwnd)
return True
hwnds = []
win32gui.EnumWindows(callback, hwnds)
#if hwnds == []:
#raise
return hwnds
def sendkey(char):
global Console_hwnd
hwnd = Console_hwnd[0]
code = ord(char)
win32api.SendMessage(hwnd, win32con.WM_CHAR, code, 0)
print '[*]',char,'Pressed.'
#Another Keypress example. only works with keycode
#win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_F9, 0)
#print char,code,"down"
#win32api.PostMessage(hwnd, win32con.WM_KEYUP, code, 0)
#print char,code,"up"
def sendesc():
global Console_hwnd
hwnd = Console_hwnd[0]
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_ESCAPE, 0)
print '[*]',"Esc down"
win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_ESCAPE, 0)
print '[*]',"Esc up"
def start():
def worker(pipe):
global TargetPower
global Mode
global Channel
global running
while running:
line = pipe.readline()
if line == '':
break
elif line.startswith('|') or line.startswith('==='):
pass
elif line.startswith("Operating in"):
info = line
for i in range(7):
info += pipe.readline()
#print 'ART> '+info,
try:
TargetPower = eval(re.search(r'(Output|Target) (power|Power) = .{4}',info).group(0).split('=')[1])
#27.0
Mode = re.search(r'gOffRate = (.+?),',info).group(1).lstrip().rstrip()
#6MBps
Channel = re.search(r'channel ([\d.]+GHz),',info).group(1)
#2.412GHz
except Exception,e:
TargetPower = 'N/A'
Mode = 'N/A'
Channel = 'N/A'
print e
elif line =='\r\n':
print '>',
else:
print 'ART>'+line,
print 'worker done.'
global proc
si = STARTUPINFO()
si.dwFlags |= STARTF_USESHOWWINDOW
si.wShowWindow = SW_HIDE
#proc = Popen("art.bat",stdout=PIPE,creationflags=CREATE_NEW_CONSOLE) #qt works!
proc = Popen("art.bat",stdout=PIPE,creationflags=CREATE_NEW_CONSOLE,startupinfo=si)
#proc = Popen("art.bat",stdout=PIPE,startupinfo=si) #hidden
#proc = Popen("cmd") #for test
sleep(2)
print '[*] pid: ',proc.pid
global Console_hwnd
Console_hwnd = get_hwnds_for_pid(proc.pid)
print '[*] hwnd:',Console_hwnd[0]
global stdout_worker
global stderr_worker
stdout_worker = ThreadWorker(worker, proc.stdout)
stderr_worker = ThreadWorker(worker, proc.stderr)
stdout_worker.start()
stderr_worker.start()
def stop():
global stdout_worker
global stderr_worker
global running
print 'stop'
global proc
sendesc()
sendesc()
sendesc()
Popen("taskkill /F /T /PID %i"%proc.pid , shell=True)
try:
running = False
TargetPower = 'N/A'
Mode = 'N/A'
Channel = 'N/A'
except Exception,e:
print e
if __name__ == "__main__":
start()
sleep(1)
sendkey('e')
sleep(1)
sendkey('c')
sleep(10)
stop()
while True:
pass
As far as I know, getch() does use standard input. COuld it be that the application did receive that input, but you don't receive its output? That's a common problem, as output often gets buffered if it is not directed at a terminal. Can you adjust the program to flush its output after each line?
I see a number of problems with your python code: you don't create a pipe for stderr, and the proc variable appears to be local to the start method.
You might want to leave the subprocess output inherited from the python process for the time being. That should suppress buffering and rules out any errors in the ThreadWorker as well.
All you need to do is:
Write a file with each step in a new line:
for instance:
step 1
step 2
step 3
then call the program like this:
program.exe < input.txt
nuff said!!
Related
Hello,
I have the below code that corrects user input and I want to exit the blocking function keyboard.read_event when the control is returned from the correction thread.
The whole program works well but I cannot exit immediately after the corrector thread is finished (the program waits for key press).
I tried using a custom Exception for interrupting the keyboard.read_event function, but I didn't manage to make it work.
import keyboard
import threading
import time
class Interrupt_Custom_Exception(Exception):
"""Base class for other exceptions"""
pass
#########################################################
def delete_and_write(times_to_delete, word_to_write):
print("------------Deleting & Rewrite Started---")
time.sleep(2)
print("------------Deleting & Rewrite Ended---")
# simulate deletion and rewrite
#**here I tried the raise Interrupt_Custom_Exception and tried to catch it at the code in the class, but didn't work**
def write_the_suppressed_string(string):
keyboard.write(string)
#########################################################
class keyboard_monitor(threading.Thread):
def __init__(self,thread_name, threadID, word_typed, keyboard_suppress, counter_for_key_pressed):
threading.Thread.__init__(self)
self.name = thread_name
self.threaID = threadID
self.fstring = word_typed
self.counter_for_key_presses = counter_for_key_pressed
self.suppressed = keyboard_suppress
self.temp = ""
def stop(self):
self._is_running = False
def run(self):
if (self.suppressed is False):
while(True):
event = keyboard.read_event(suppress = self.suppressed)
if (event.event_type == keyboard.KEY_DOWN):
if (event.name == "space"):
suppressed_monitor = keyboard_monitor("suppressed_monitor", 2, self.fstring, True, self.counter_for_key_presses)
suppressed_monitor.start()
suppressed_monitor.join()
print("RETURNED TO MAIN MONITOR")
self.counter_for_key_presses = 0
self.fstring = ""
elif (event.name in "abcdefghijklmnopqrstuvwxyz"):
self.fstring = ''.join([self.fstring, event.name])
self.counter_for_key_presses += 1
elif (self.suppressed is True):
def listen_to_keyboard():
event = keyboard.read_event(suppress=self.suppressed)
# **here is where the program waits and don't continue when the correction thread is finished.**
if (event.event_type == keyboard.KEY_DOWN):
print("---KEYS PRESSED WHILE SUPPRESSED = {}---".format(event.name))
if (event.name in "abcdefghijklmnopqrstuvwxyz"):
self.fstring = ''.join([self.fstring, event.name])
self.counter_for_key_presses += 1
try:
#########################################################
# INITIALY CORRECTING THE WORD PASSED FROM THE NORMAL KEY MONITOR
self.temp = self.fstring
self.fstring = ""
thread_delete_and_rewrite = threading.Thread(
target = delete_and_write, args=(self.counter_for_key_presses, self.temp))
thread_delete_and_rewrite.start()
# raise Interrupt_Custom_Exception
#########################################################
print("-BEFORE WHILE LOOP-")
while(thread_delete_and_rewrite.is_alive() is True): # **this works ok but if the control enters the listen_to_keyboard function waits there until a key is pressed. I want somehow to stop this manually and continue the code after this while**
print("--ENTERING THE WHILE LOOP--")
listen_to_keyboard()
print("----EXITING THE WHILE LOOP----\n")
except Interrupt_Custom_Exception:
print("!!!!!!!!!!!!!!!!!CAUGHT IT!!!!!!!!!!!!!!!!!!!")
print("----EXITING THE WHILE LOOP----\n")
print("------BEFORE FINAL WRITE------")
if (self.fstring != ""):
thread_write = threading.Thread(
target = write_the_suppressed_string, args=(self.fstring, ))
thread_write.start()
thread_write.join()
print("SUPPRESSED ENDED")
self._is_running = False
if __name__ == "__main__":
kb_not_suppressed = keyboard_monitor("not_suppressed_monitor", 1, "", False, 0)
kb_not_suppressed.start()
kb_not_suppressed.join()
Any idea on how to exit this blocking function would be very very useful.
Thanks in advance.
It's not possible unless you find some keyboard.read_event that has a timeout, or does a non-blocking check if there's a event. I haven't found any of those in keyboard module ;/
A big workaround would be to keyboard.press in case you want to exit. Not sure if you can detect if it's not from the user. It's up to you if it's acceptable.
I'm writing a Python module to read jstest output and make Xbox gamepad working in Python on Linux. I need to start in background infinite while loop in __init__ on another thread that looks like this:
import os
from threading import Thread
import time
import select
import subprocess
class Joystick:
"""Initializes base class and launches jstest and xboxdrv"""
def __init__(self, refreshRate=2000, deadzone=4000):
self.proc = subprocess.Popen(['xboxdrv', '-D', '-v', '--detach-kernel-driver', '--dpad-as-button'], stdout=subprocess.PIPE, bufsize=0)
self.pipe = self.proc.stdout
self.refresh = refreshRate
self.refreshDelay = 1.0 / refreshRate
self.refreshTime = 0 # indicates the next refresh
self.deadzone = deadzone
self.start()
self.xbox = subprocess.Popen(['jstest', '--normal', '/dev/input/js0'], stdout=subprocess.PIPE, bufsize=-1, universal_newlines=True)
self.response = self.xbox.stdout.readline()
a = Thread(target=self.reload2())
a.start()
print("working")
def reload2(self):
while True:
self.response = self.xbox.stdout.readline()
print("read")
time.sleep(0.5)
def start(self):
global leftVibrateAmount, rightVibrateAmount
leftVibrateAmount = 0
rightVibrateAmount = 0
readTime = time.time() + 1 # here we wait a while
found = False
while readTime > time.time() and not found:
readable, writeable, exception = select.select([self.pipe], [], [], 0)
if readable:
response = self.pipe.readline()
# tries to detect if controller is connected
if response == b'[ERROR] XboxdrvDaemon::run(): fatal exception: DBusSubsystem::request_name(): failed to become primary owner of dbus name\n':
raise IOError("Another instance of xboxdrv is running.")
elif response == b'[INFO] XboxdrvDaemon::connect(): connecting slot to thread\n':
found = True
self.reading = response
elif response == b'':
raise IOError('Are you running as sudo?')
if not found:
self.pipe.close()
# halt if controller not found
raise IOError("Xbox controller/receiver isn't connected")
The loop is defined to start running in __init__ function like so:
a = threading.Thread(target=self.reload2) # code hangs here
a.start()
But each time I create variable "a", whole program hangs in while loop, which should be running in another thread.
Thanks for help.
You may be having issues with your __init__. I put it in a simple class as an example, and it runs as expected.
import time
from threading import Thread
class InfiniteLooper():
def __init__(self):
a = Thread(target=self.reload2) # reload, not reload(), otherwise you're executing reload2 and assigning the result to Target, but it's an infinite loop, so...
print('Added thread')
a.start()
print('Thread started')
def reload2(self):
while True:
self.response = input('Enter something')
print('read')
time.sleep(0.5)
loop = InfiniteLooper()
Output:
Added thread
Thread started
Enter something
1
read
Enter something
1
read
As you can see, the "Enter something" appears after I've added the thread and started it. It also loops fine
I'm simply trying to run two different daemon threads and print a line from each for testing. While this code works :
import time
import threading
from threading import Thread
from myFunctions import *
class Monitoring:
def alarms(self):
return alarms.run()
def generator(self):
return generator.run()
def run(self):
generator = threading.Thread(target=self.alarms)
generator.daemon = True
generator.start()
alarm = threading.Thread(target=self.generator)
alarm.daemon = True
alarm.start()
print("started thread")
if __name__ == '__main__':
try:
d = Monitoring()
d.daemon = True
d.run()
print("started the thread")
while 1:
time.sleep(1)
except KeyboardInterrupt:
alarms.close()
generator.close()
print("Main - Keyboard interrupt in __main__")
Something like this doesn't seem to work and only the first thread "alarms" start. Why is that?
class Monitoring:
def __init__(self,a,g):
self.a = a
self.g = g
def run(self):
generator = threading.Thread(target=self.a)
generator.daemon = True
generator.start()
alarm = threading.Thread(target=self.g)
alarm.daemon = True
alarm.start()
print("#class run() ")
if __name__ == '__main__':
try:
d = Monitoring(alarms.run(), generator.run())
d.daemon = True
d.run()
print("#__main__")
while 1:
time.sleep(1)
except KeyboardInterrupt:
alarms.close()
generator.close()
print("Main - Keyboard interrupt in __main__")
In the line
d = Monitoring(alarms.run(), generator.run())
the functions alarms.run and generator.run are called instantly and the return values (probably None) are given as arguments to the Monitoring constructor.
To use the function objects as arguments (which are then called in a new thread), use
d = Monitoring(alarms.run, generator.run)
instead.
I have a thread class and I want to start/stop my thread many times in my main function. I used this link with this method for solving my problem. Here is a simple thread which prints the keystrokes in the console:
global isWindows
isWindows = False
try:
from win32api import STD_INPUT_HANDLE
from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT
import win32gui
import threading
from time import sleep
import sys
isWindows = True
except ImportError as e:
import sys
import select
import termios
class KeyPoller(threading.Thread):
def __init__(self):
super(KeyPoller, self).__init__()
#threading.Thread.__init__(self)
self.stop_event = threading.Event()
global isWindows
if isWindows:
self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)
self.curEventLength = 0
self.curKeysLength = 0
self.capturedChars = []
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
def poll(self):
if isWindows:
if not len(self.capturedChars) == 0:
return self.capturedChars.pop(0)
eventsPeek = self.readHandle.PeekConsoleInput(10000)
if len(eventsPeek) == 0:
return None
if not len(eventsPeek) == self.curEventLength:
for curEvent in eventsPeek[self.curEventLength:]:
if curEvent.EventType == KEY_EVENT:
if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
pass
else:
curChar = str(curEvent.Char)
self.capturedChars.append(curChar)
self.curEventLength = len(eventsPeek)
if not len(self.capturedChars) == 0:
return self.capturedChars.pop(0)
else:
return None
else:
dr,dw,de = select.select([sys.stdin], [], [], 0)
if not dr == []:
return sys.stdin.read(1)
return None
def stop(self):
print("stopping the thread")
self.stop_event.set()
def stopped(self):
return self.stop_event.is_set()
def run(self):
while not self.stopped():
c=self.poll()
if not c is None:
print(c)
if __name__=='__main__':
thr=KeyPoller()
print("starting the thread #1")
thr.start()
sleep(5)
print("stopping the thread #1")
# sadly if you press any key in this time it would be saved and printed after thr2.start
thr.stop()
thr.join()
sleep(5)
thr2=KeyPoller()
print("starting the thread #2")
thr2.start()
sleep(5)
print("stopping the thread #2")
thr2.stop()
print("Exiting the whole program")
My problem is when I call thr.stop() and try to press some keystrokes it exits the while loop and it seems that the thread has already stopped but when I call thr2.start() it prints the old keystrokes from the first instance of my thread and it seems that all keystrokes are still there no matter if I call stop function or not.
From my point of view I don't see the sense of initialize treading.event() as instance variable. Event is for threading/process synchronization and if you declare this in a instance, it not could be seen for others instances.
Having said that, given your code, I would use a boolean variable for stop_event. This works for me in Linux environment.
class KeyPoller(threading.Thread):
def __init__(self):
super(KeyPoller, self).__init__()
#threading.Thread.__init__(self)
self.stop_event = False
global isWindows
if isWindows:
self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)
self.curEventLength = 0
self.curKeysLength = 0
self.capturedChars = []
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
def poll(self):
if isWindows:
if not len(self.capturedChars) == 0:
return self.capturedChars.pop(0)
eventsPeek = self.readHandle.PeekConsoleInput(10000)
if len(eventsPeek) == 0:
return None
if not len(eventsPeek) == self.curEventLength:
for curEvent in eventsPeek[self.curEventLength:]:
if curEvent.EventType == KEY_EVENT:
if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
pass
else:
curChar = str(curEvent.Char)
self.capturedChars.append(curChar)
self.curEventLength = len(eventsPeek)
if not len(self.capturedChars) == 0:
return self.capturedChars.pop(0)
else:
return None
else:
dr,dw,de = select.select([sys.stdin], [], [], 0)
if not dr == []:
return sys.stdin.read(1)
return None
def stop(self):
print("stopping the thread")
self.stop_event = True
def stopped(self):
return self.stop_event
def run(self):
while not self.stopped():
c=self.poll()
if not c is None:
print(c)
I am maintaining an operator terminal based on cmd. The customer asked for an alerting behavior. e.g. a message shown onscreen when some asynchronous event occurs. I made a thread that periodically checks for alerts, and when it finds some, it just prints them to stdout.
This seems to work OK, but it doesn't seem very elegant, and it has a problem:
Because cmd doesn't know an alert happened, the message is followed onscreen by blank. The command prompt is not reprinted, and any user input is left pending.
Is there a better way to do asynchronous alerts during Python cmd? With the method as-is, can I interrupt cmd and get it to redraw its prompt?
I tried from my thread to poke a newline in stdin using StringIO, but this is not ideal, and I haven't gotten it work right.
Example code:
import cmd, sys
import threading, time
import io
import sys
class MyShell(cmd.Cmd):
intro = '*** Terminal ***\nType help or ? to list commands.\n'
prompt = '> '
file = None
def alert(self):
time.sleep(5)
print ('\n\n*** ALERT!\n')
sys.stdin = io.StringIO("\n")
def do_bye(self, arg):
'Stop recording, close the terminal, and exit: BYE'
print('Exiting.')
sys.exit(0)
return True
def do_async(self, arg):
'Set a five second timer to pop an alert.'
threading.Thread(target=self.alert).start()
def emptyline(self):
pass
def parse(arg):
'Convert a series of zero or more numbers to an argument tuple'
return tuple(map(int, arg.split()))
if __name__ == '__main__':
MyShell().cmdloop()
I ended up overriding Cmd.cmdloop with my own version, replacing the readlines() with my own readlines that use non-blocking terminal IO.
Non-Blocking terminal IO info here:
Non-Blocking terminal IO
Unfortunately, this opens another can trouble in that it is messy and breaks auto-completion and command history. Fortunately, the customer was OK with having to push Enter to redo the prompt, so I don't need to worry about it anymore.
Incomplete example code showing the non-blocking terminal input approach:
import cmd, sys
import threading, time
import io
import os
if os.name=='nt':
import msvcrt
def getAnyKey():
if msvcrt.kbhit():
return msvcrt.getch()
return None
else:
import sys
import select
import tty
import termios
import atexit
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
old_settings = termios.tcgetattr(sys.stdin)
def restoreSettings():
global old_settings
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
atexit.register(restoreSettings)
def getAnyKey():
try:
if isData():
return sys.stdin.read(1)
return None
except:
pass
return None
class MyShell(cmd.Cmd):
prompt = '> '
file = None
realstdin = sys.stdin
mocking=False
breakReadLine=False
def alert(self):
time.sleep(5)
print ('\n\n*** ALERT!\n')
self.breakReadLine=True
# ----- basic commands -----
def do_bye(self, arg):
'Stop recording, close the terminal, and exit: BYE'
print('Exiting.')
sys.exit(0)
return True
def do_async(self, arg):
'Set a five second timer to pop an alert.'
threading.Thread(target=self.alert).start()
def emptyline(self):
pass
def myReadLine(self):
sys.stdout.flush()
self.breakReadLine=False
line=''
while not self.breakReadLine:
c=getAnyKey()
if not c is None:
c=c.decode("utf-8")
if c=='\x08' and len(line):
line=line[0:-1]
elif c in ['\r','\n']:
print('\n')
return line
else:
line+=c
print(c,end='')
sys.stdout.flush()
def mycmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
the remainder of the line as argument.
"""
self.preloop()
if self.use_rawinput and self.completekey:
try:
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey+": complete")
except ImportError:
pass
try:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
if self.use_rawinput:
try:
print(self.prompt,end='')
line = self.myReadLine()#input(self.prompt)
except EOFError:
line = 'EOF'
else:
self.stdout.write(self.prompt)
self.stdout.flush()
line = self.myReadLine()#self.stdin.readline()
if not line is None:
line = line.rstrip('\r\n')
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
if self.use_rawinput and self.completekey:
try:
import readline
readline.set_completer(self.old_completer)
except ImportError:
pass
def cmdloop_with_keyboard_interrupt(self, intro):
doQuit = False
while doQuit != True:
try:
if intro!='':
cintro=intro
intro=''
self.mycmdloop(cintro)
else:
self.intro=''
self.mycmdloop()
doQuit = True
except KeyboardInterrupt:
sys.stdout.write('\n')
def parse(arg):
'Convert a series of zero or more numbers to an argument tuple'
return tuple(map(int, arg.split()))
if __name__ == '__main__':
#MyShell().cmdloop()
MyShell().cmdloop_with_keyboard_interrupt('*** Terminal ***\nType help or ? to list commands.\n')