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.
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()