hgbook
view en/examples/run-example @ 4:33a2e7b9978d
Make it possible to include example input and output from real programs.
Instead of having to cut and paste example text, the task is automated.
Instead of having to cut and paste example text, the task is automated.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sun Jun 25 22:04:50 2006 -0700 (2006-06-25) |
parents | 906d9021f9e5 |
children | 69d90ab9fd80 |
line source
1 #!/usr/bin/python
2 #
3 # This program takes something that resembles a shell script and runs
4 # it, spitting input (commands from the script) and output into text
5 # files, for use in examples.
7 import cStringIO
8 import os
9 import pty
10 import re
11 import shutil
12 import sys
13 import tempfile
14 import time
16 def tex_escape(s):
17 if '\\' in s:
18 s = s.replace('\\', '\\\\')
19 if '{' in s:
20 s = s.replace('{', '\\{')
21 if '}' in s:
22 s = s.replace('}', '\\}')
23 return s
25 class example:
26 shell = '/bin/bash'
27 pi_re = re.compile('#\$\s*(name):\s*(.*)$')
29 def __init__(self, name):
30 self.name = name
32 def parse(self):
33 '''yield each hunk of input from the file.'''
34 fp = open(self.name)
35 cfp = cStringIO.StringIO()
36 for line in fp:
37 cfp.write(line)
38 if not line.rstrip().endswith('\\'):
39 yield cfp.getvalue()
40 cfp.seek(0)
41 cfp.truncate()
43 def status(self, s):
44 sys.stdout.write(s)
45 if not s.endswith('\n'):
46 sys.stdout.flush()
48 def drain(self, ifp, ofp):
49 while True:
50 s = ifp.read(4096)
51 if not s: break
52 if ofp: ofp.write(tex_escape(s))
54 def run(self):
55 ofp = None
56 basename = os.path.basename(self.name)
57 self.status('running %s ' % basename)
58 tmpdir = tempfile.mkdtemp(prefix=basename)
59 try:
60 for hunk in self.parse():
61 # is this line a processing instruction?
62 m = self.pi_re.match(hunk)
63 if m:
64 pi, rest = m.groups()
65 if pi == 'name':
66 self.status('.')
67 out = rest
68 assert os.sep not in out
69 if out:
70 ofp = open('%s.%s.out' % (self.name, out), 'w')
71 else:
72 ofp = None
73 else:
74 # it's something we should execute
75 cin, cout = os.popen4('cd %s; %s' % (tmpdir, hunk))
76 cin.close()
77 if ofp:
78 # first, print the command we ran
79 if not hunk.startswith('#'):
80 nl = hunk.endswith('\n')
81 hunk = ('$ \\textbf{%s}' %
82 tex_escape(hunk.rstrip('\n')))
83 if nl: hunk += '\n'
84 ofp.write(hunk)
85 # then its output
86 self.drain(cout, ofp)
87 self.status('\n')
88 finally:
89 os.wait()
90 shutil.rmtree(tmpdir)
92 def main(path='.'):
93 args = sys.argv[1:]
94 if args:
95 for a in args:
96 example(a).run()
97 return
98 for name in os.listdir(path):
99 if name == 'run-example' or name.startswith('.'): continue
100 if name.endswith('.out') or name.endswith('~'): continue
101 example(os.path.join(path, name)).run()
102 print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
104 if __name__ == '__main__':
105 main()