-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathdemo.py
109 lines (84 loc) · 3.02 KB
/
demo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# pragma NO COVER
from code import interact
import itertools
import os.path
import sys
import time
class DemoRunner(object):
"""An interactive runner of demo scripts."""
KEYPRESS_DELAY = 0.05
GLOBALS, LOCALS = globals(), locals()
CODE, COMMENT = 'code', 'comment'
def __init__(self, fp):
self.lines = [line.rstrip() for line in fp.readlines()]
@classmethod
def from_module(cls, module):
path = os.path.join(os.path.dirname(module.__file__),
'demo', 'demo.py')
return cls(open(path, 'r'))
def run(self):
line_groups = itertools.groupby(self.lines, self.get_line_type)
for group_type, lines in line_groups:
if group_type == self.COMMENT:
self.write(lines)
elif group_type == self.CODE:
self.code(lines)
interact('(Hit CTRL-D to exit...)', local=self.LOCALS)
def wait(self):
raw_input()
@classmethod
def get_line_type(cls, line):
if line.startswith('#'):
return cls.COMMENT
else:
return cls.CODE
def get_indent_level(self, line):
if not line.strip():
return None
return len(line) - len(line.lstrip())
def write(self, lines):
print
print '\n'.join(lines),
self.wait()
def code(self, lines):
code_lines = []
for line in lines:
indent = self.get_indent_level(line)
# If we've completed a block,
# run whatever code was built up in code_lines.
if indent == 0:
self._execute_lines(code_lines)
code_lines = []
# Print the prefix for the line depending on the indentation level.
if indent == 0:
print '>>> ',
elif indent > 0:
print '\n... ',
elif indent is None:
continue
# Break the line into the code section and the comment section.
if '#' in line:
code, comment = line.split('#', 2)
else:
code, comment = line, None
# 'Type' out the comment section.
for char in code.rstrip():
time.sleep(self.KEYPRESS_DELAY)
sys.stdout.write(char)
sys.stdout.flush()
# Print the comment section (not typed out).
if comment:
sys.stdout.write(' # %s' % comment.strip())
# Add the current line to the list of lines to be run
# in this block.
code_lines.append(line)
# If we had any code built up that wasn't part of a completed block
# (ie, the lines ended with an indented line),
# run that code.
if code_lines:
self._execute_lines(code_lines)
def _execute_lines(self, lines):
if lines:
self.wait()
# Yes, this is crazy unsafe... but it's demo code.
exec('\n'.join(lines), self.GLOBALS, self.LOCALS)