Files
swift-mirror/utils/demo-tool
Ted Kremenek 46cae2a645 [demo-tool] Add support for backspacing.
Swift SVN r18618
2014-05-24 15:24:39 +00:00

192 lines
5.5 KiB
Python
Executable File

#!/usr/bin/env python
import json
import optparse
import subprocess
import time
import random
import sys
def send_to_screen(screen_name, arg0=None, command='stuff'):
args = ['screen', '-S', screen_name, '-p', '0',
'-X', command]
if arg0 is not None:
args.append(arg0)
p = subprocess.Popen(args)
p.communicate()
def load_as_transcript(f):
def add_block(clearScreen=False,noReturn=False):
# If so, add the current block after trimming leading and trailing blank
# lines.
while current_block and current_block[0].isspace():
current_block.pop(0)
while current_block and current_block[-1].isspace():
current_block.pop()
# Add the block if it is non-empty.
if current_block or comment_block:
# Form the joined script..
script = ''.join(current_block)
comment = ''.join(comment_block)
# Strip off any trailing newline.
if script.endswith('\n'):
script = script[:-1]
demo_script.append({ 'command' : script,
'comment' : comment,
'clearScreen' : clearScreen,
'noReturn' : noReturn })
# Clear the block and comment block.
del current_block[:]
del comment_block[:]
f.seek(0)
demo_script = []
current_block = []
comment_block = []
for ln in f:
# Check if this is a block delimiter.
if ln.strip() == '>>>':
add_block()
continue
# Check if this is a block delimiter
# but marked to clear the screen.
if ln.strip() == '>>>CLEAR':
add_block(clearScreen=True)
continue
# Check if this is a block delimiter
# but marked to not insert a newline.
if ln.strip() == '>>>NORETURN':
add_block(noReturn=True)
continue
# Check if the line starts with a '#'
# to indicate a prompter comment.
if ln.startswith('#'):
comment_block.append(ln)
continue
# Check for backspace.
if ln.strip() == '>>>BACKSPACE':
current_block.append('\b')
continue
# Otherwise, add the line to the current block.
current_block.append(ln)
return demo_script
def main():
parser = optparse.OptionParser("""\
usage: %%prog [options] <demo script>
Run a command line demo script using 'screen'. The script file should be either
a JSON document listing the commands to run, as in::
[
{ "command" : "ls" },
{ "command" : "echo Hello" }
]
or, alternately, it should be a text filed delimited by '>>>', as in::
>>>
ls
>>>
echo Hello
where leading and trailing blank lines around each block will be discarded.
The script requires the 'screen' session to have been started in another window,
for example::
$ screen -S demo
""")
# Determine up front if the terminal supports color.
hasColor = sys.stdout.isatty()
def prompterPrint(string):
if hasColor:
attr = [ '32', '1' ]
print '\x1b[%sm%s\x1b[0m' % (';'.join(attr), string)
else:
print string
def send(*args, **kwargs):
return send_to_screen(opts.screen_name, *args, **kwargs)
parser.add_option("-S", "--screen-name", dest="screen_name", metavar="NAME",
help="name of the screen sesison to use [%default]",
action="store", default="demo")
opts, args = parser.parse_args()
if len(args) == 1:
path, = args
else:
parser.error("invalid number of arguments")
# Read in the demo script.
with open(path) as f:
try:
demo_script = json.load(f)
except ValueError:
demo_script = load_as_transcript(f)
# Validate the entries.
for item in demo_script:
command = str(item.get('command'))
if not isinstance(command, str):
raise SystemError("error: invalid item in script: %r" % (
item,))
# Notify screen that we are starting the demo.
raw_input('press enter to begin demo...')
# Iterate over each command, sending it and waiting for user direction to
# continue.
for item in demo_script:
shouldClear = bool(item['clearScreen'])
noReturn = bool(item['noReturn'])
command = str(item['command'])
comment = str(item['comment'])
if comment:
prompterPrint(comment)
if command:
# Send the command slowly, as if it was typed.
print "sending command: %r" % (command.replace('\n', '<cr>'),)
for c in command:
send(c)
if c == "\n":
time.sleep(0.1)
else:
time.sleep(random.random() * 0.03)
if not noReturn:
# Wait for user input, then send a return.
raw_input('press enter to send return...')
send('\n')
if item is not demo_script[-1]:
raw_input('press enter to continue to next command...')
# Send the command slowly, as if it was typed.
if shouldClear:
print "clearing screen"
send(command="clear")
send('\n')
# Notify screen that the demo is over, after a small wait period.
# time.sleep(0.1)
# send('Demo session is over.', command='wall')
if __name__ == '__main__':
main()