Mercurial > 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.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sun, 25 Jun 2006 22:04:50 -0700 |
parents | 906d9021f9e5 |
children | 69d90ab9fd80 |
line wrap: on
line diff
--- a/en/examples/run-example Sat Jun 24 17:42:40 2006 -0700 +++ b/en/examples/run-example Sun Jun 25 22:04:50 2006 -0700 @@ -1,16 +1,36 @@ #!/usr/bin/python +# +# This program takes something that resembles a shell script and runs +# it, spitting input (commands from the script) and output into text +# files, for use in examples. import cStringIO import os import pty import re +import shutil import sys +import tempfile +import time +def tex_escape(s): + if '\\' in s: + s = s.replace('\\', '\\\\') + if '{' in s: + s = s.replace('{', '\\{') + if '}' in s: + s = s.replace('}', '\\}') + return s + class example: + shell = '/bin/bash' + pi_re = re.compile('#\$\s*(name):\s*(.*)$') + def __init__(self, name): self.name = name def parse(self): + '''yield each hunk of input from the file.''' fp = open(self.name) cfp = cStringIO.StringIO() for line in fp: @@ -20,28 +40,54 @@ cfp.seek(0) cfp.truncate() - name_re = re.compile('#\s*name:\s*(.*)$') - def status(self, s): sys.stdout.write(s) if not s.endswith('\n'): sys.stdout.flush() + def drain(self, ifp, ofp): + while True: + s = ifp.read(4096) + if not s: break + if ofp: ofp.write(tex_escape(s)) + def run(self): ofp = None - self.status('running %s ' % os.path.basename(self.name)) - for hunk in self.parse(): - m = self.name_re.match(hunk) - if m: - self.status('.') - out = m.group(1) - assert os.sep not in out - if out: - ofp = open('%s.%s.out' % (self.name, out), 'w') + basename = os.path.basename(self.name) + self.status('running %s ' % basename) + tmpdir = tempfile.mkdtemp(prefix=basename) + try: + for hunk in self.parse(): + # is this line a processing instruction? + m = self.pi_re.match(hunk) + if m: + pi, rest = m.groups() + if pi == 'name': + self.status('.') + out = rest + assert os.sep not in out + if out: + ofp = open('%s.%s.out' % (self.name, out), 'w') + else: + ofp = None else: - ofp = None - elif ofp: ofp.write(hunk) - self.status('\n') + # it's something we should execute + cin, cout = os.popen4('cd %s; %s' % (tmpdir, hunk)) + cin.close() + if ofp: + # first, print the command we ran + if not hunk.startswith('#'): + nl = hunk.endswith('\n') + hunk = ('$ \\textbf{%s}' % + tex_escape(hunk.rstrip('\n'))) + if nl: hunk += '\n' + ofp.write(hunk) + # then its output + self.drain(cout, ofp) + self.status('\n') + finally: + os.wait() + shutil.rmtree(tmpdir) def main(path='.'): args = sys.argv[1:] @@ -53,6 +99,7 @@ if name == 'run-example' or name.startswith('.'): continue if name.endswith('.out') or name.endswith('~'): continue example(os.path.join(path, name)).run() + print >> open(os.path.join(path, '.run'), 'w'), time.asctime() if __name__ == '__main__': main()