hgbook
diff 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 diff
1.1 --- a/en/examples/run-example Sat Jun 24 17:42:40 2006 -0700 1.2 +++ b/en/examples/run-example Sun Jun 25 22:04:50 2006 -0700 1.3 @@ -1,16 +1,36 @@ 1.4 #!/usr/bin/python 1.5 +# 1.6 +# This program takes something that resembles a shell script and runs 1.7 +# it, spitting input (commands from the script) and output into text 1.8 +# files, for use in examples. 1.9 1.10 import cStringIO 1.11 import os 1.12 import pty 1.13 import re 1.14 +import shutil 1.15 import sys 1.16 +import tempfile 1.17 +import time 1.18 1.19 +def tex_escape(s): 1.20 + if '\\' in s: 1.21 + s = s.replace('\\', '\\\\') 1.22 + if '{' in s: 1.23 + s = s.replace('{', '\\{') 1.24 + if '}' in s: 1.25 + s = s.replace('}', '\\}') 1.26 + return s 1.27 + 1.28 class example: 1.29 + shell = '/bin/bash' 1.30 + pi_re = re.compile('#\$\s*(name):\s*(.*)$') 1.31 + 1.32 def __init__(self, name): 1.33 self.name = name 1.34 1.35 def parse(self): 1.36 + '''yield each hunk of input from the file.''' 1.37 fp = open(self.name) 1.38 cfp = cStringIO.StringIO() 1.39 for line in fp: 1.40 @@ -20,28 +40,54 @@ 1.41 cfp.seek(0) 1.42 cfp.truncate() 1.43 1.44 - name_re = re.compile('#\s*name:\s*(.*)$') 1.45 - 1.46 def status(self, s): 1.47 sys.stdout.write(s) 1.48 if not s.endswith('\n'): 1.49 sys.stdout.flush() 1.50 1.51 + def drain(self, ifp, ofp): 1.52 + while True: 1.53 + s = ifp.read(4096) 1.54 + if not s: break 1.55 + if ofp: ofp.write(tex_escape(s)) 1.56 + 1.57 def run(self): 1.58 ofp = None 1.59 - self.status('running %s ' % os.path.basename(self.name)) 1.60 - for hunk in self.parse(): 1.61 - m = self.name_re.match(hunk) 1.62 - if m: 1.63 - self.status('.') 1.64 - out = m.group(1) 1.65 - assert os.sep not in out 1.66 - if out: 1.67 - ofp = open('%s.%s.out' % (self.name, out), 'w') 1.68 + basename = os.path.basename(self.name) 1.69 + self.status('running %s ' % basename) 1.70 + tmpdir = tempfile.mkdtemp(prefix=basename) 1.71 + try: 1.72 + for hunk in self.parse(): 1.73 + # is this line a processing instruction? 1.74 + m = self.pi_re.match(hunk) 1.75 + if m: 1.76 + pi, rest = m.groups() 1.77 + if pi == 'name': 1.78 + self.status('.') 1.79 + out = rest 1.80 + assert os.sep not in out 1.81 + if out: 1.82 + ofp = open('%s.%s.out' % (self.name, out), 'w') 1.83 + else: 1.84 + ofp = None 1.85 else: 1.86 - ofp = None 1.87 - elif ofp: ofp.write(hunk) 1.88 - self.status('\n') 1.89 + # it's something we should execute 1.90 + cin, cout = os.popen4('cd %s; %s' % (tmpdir, hunk)) 1.91 + cin.close() 1.92 + if ofp: 1.93 + # first, print the command we ran 1.94 + if not hunk.startswith('#'): 1.95 + nl = hunk.endswith('\n') 1.96 + hunk = ('$ \\textbf{%s}' % 1.97 + tex_escape(hunk.rstrip('\n'))) 1.98 + if nl: hunk += '\n' 1.99 + ofp.write(hunk) 1.100 + # then its output 1.101 + self.drain(cout, ofp) 1.102 + self.status('\n') 1.103 + finally: 1.104 + os.wait() 1.105 + shutil.rmtree(tmpdir) 1.106 1.107 def main(path='.'): 1.108 args = sys.argv[1:] 1.109 @@ -53,6 +99,7 @@ 1.110 if name == 'run-example' or name.startswith('.'): continue 1.111 if name.endswith('.out') or name.endswith('~'): continue 1.112 example(os.path.join(path, name)).run() 1.113 + print >> open(os.path.join(path, '.run'), 'w'), time.asctime() 1.114 1.115 if __name__ == '__main__': 1.116 main()