hgbook
annotate en/examples/run-example @ 68:c574ce277a2b
Mostly random attempt to see if fiddling with the child will help the parent.
Motivated by problems people are having on Debian sid and FreeBSD.
Motivated by problems people are having on Debian sid and FreeBSD.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Mon Aug 07 15:45:08 2006 -0700 (2006-08-07) |
parents | a2f0b010d6e3 |
children | 365b41b6a15e |
rev | line source |
---|---|
bos@67 | 1 #!/usr/bin/env python |
bos@4 | 2 # |
bos@4 | 3 # This program takes something that resembles a shell script and runs |
bos@4 | 4 # it, spitting input (commands from the script) and output into text |
bos@4 | 5 # files, for use in examples. |
bos@3 | 6 |
bos@3 | 7 import cStringIO |
bos@3 | 8 import os |
bos@3 | 9 import pty |
bos@3 | 10 import re |
bos@4 | 11 import shutil |
bos@6 | 12 import signal |
bos@36 | 13 import stat |
bos@3 | 14 import sys |
bos@4 | 15 import tempfile |
bos@4 | 16 import time |
bos@3 | 17 |
bos@4 | 18 def tex_escape(s): |
bos@4 | 19 if '\\' in s: |
bos@4 | 20 s = s.replace('\\', '\\\\') |
bos@4 | 21 if '{' in s: |
bos@4 | 22 s = s.replace('{', '\\{') |
bos@4 | 23 if '}' in s: |
bos@4 | 24 s = s.replace('}', '\\}') |
bos@4 | 25 return s |
bos@4 | 26 |
bos@3 | 27 class example: |
bos@4 | 28 shell = '/bin/bash' |
bos@6 | 29 prompt = '__run_example_prompt__\n' |
bos@4 | 30 pi_re = re.compile('#\$\s*(name):\s*(.*)$') |
bos@4 | 31 |
bos@3 | 32 def __init__(self, name): |
bos@3 | 33 self.name = name |
bos@3 | 34 |
bos@3 | 35 def parse(self): |
bos@4 | 36 '''yield each hunk of input from the file.''' |
bos@3 | 37 fp = open(self.name) |
bos@3 | 38 cfp = cStringIO.StringIO() |
bos@3 | 39 for line in fp: |
bos@3 | 40 cfp.write(line) |
bos@3 | 41 if not line.rstrip().endswith('\\'): |
bos@3 | 42 yield cfp.getvalue() |
bos@3 | 43 cfp.seek(0) |
bos@3 | 44 cfp.truncate() |
bos@3 | 45 |
bos@3 | 46 def status(self, s): |
bos@3 | 47 sys.stdout.write(s) |
bos@3 | 48 if not s.endswith('\n'): |
bos@3 | 49 sys.stdout.flush() |
bos@3 | 50 |
bos@6 | 51 def send(self, s): |
bos@6 | 52 self.cfp.write(s) |
bos@6 | 53 self.cfp.flush() |
bos@6 | 54 |
bos@6 | 55 def receive(self): |
bos@6 | 56 out = cStringIO.StringIO() |
bos@4 | 57 while True: |
bos@6 | 58 s = self.cfp.readline().replace('\r\n', '\n') |
bos@6 | 59 if not s or s == self.prompt: |
bos@6 | 60 break |
bos@6 | 61 out.write(s) |
bos@6 | 62 return out.getvalue() |
bos@4 | 63 |
bos@6 | 64 def sendreceive(self, s): |
bos@6 | 65 self.send(s) |
bos@6 | 66 r = self.receive() |
bos@6 | 67 if r.startswith(s): |
bos@6 | 68 r = r[len(s):] |
bos@6 | 69 return r |
bos@6 | 70 |
bos@3 | 71 def run(self): |
bos@3 | 72 ofp = None |
bos@4 | 73 basename = os.path.basename(self.name) |
bos@4 | 74 self.status('running %s ' % basename) |
bos@4 | 75 tmpdir = tempfile.mkdtemp(prefix=basename) |
bos@6 | 76 rcfile = os.path.join(tmpdir, '.bashrc') |
bos@6 | 77 rcfp = open(rcfile, 'w') |
bos@6 | 78 print >> rcfp, 'PS1="%s"' % self.prompt |
bos@6 | 79 print >> rcfp, 'unset HISTFILE' |
bos@19 | 80 print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd() |
bos@6 | 81 print >> rcfp, 'export LANG=C' |
bos@6 | 82 print >> rcfp, 'export LC_ALL=C' |
bos@6 | 83 print >> rcfp, 'export TZ=GMT' |
bos@6 | 84 print >> rcfp, 'export HGRC="%s/.hgrc"' % tmpdir |
bos@6 | 85 print >> rcfp, 'export HGRCPATH=$HGRC' |
bos@6 | 86 print >> rcfp, 'cd %s' % tmpdir |
bos@6 | 87 rcfp.close() |
bos@68 | 88 sys.stdout.flush() |
bos@68 | 89 sys.stderr.flush() |
bos@6 | 90 pid, fd = pty.fork() |
bos@6 | 91 if pid == 0: |
bos@68 | 92 cmdline = ['/bin/bash', '--noediting', '--noprofile', '--norc'] |
bos@68 | 93 try: |
bos@68 | 94 os.execv(cmdline[0], cmdline) |
bos@68 | 95 except OSError, err: |
bos@68 | 96 print >> sys.stderr, '%s: %s' % (cmdline[0], err.strerror) |
bos@68 | 97 sys.stderr.flush() |
bos@68 | 98 os._exit(0) |
bos@6 | 99 self.cfp = os.fdopen(fd, 'w+') |
bos@4 | 100 try: |
benoit@22 | 101 # setup env and prompt |
benoit@22 | 102 self.sendreceive('source %s\n\n' % rcfile) |
bos@4 | 103 for hunk in self.parse(): |
bos@4 | 104 # is this line a processing instruction? |
bos@4 | 105 m = self.pi_re.match(hunk) |
bos@4 | 106 if m: |
bos@4 | 107 pi, rest = m.groups() |
bos@4 | 108 if pi == 'name': |
bos@4 | 109 self.status('.') |
bos@4 | 110 out = rest |
bos@4 | 111 assert os.sep not in out |
bos@4 | 112 if out: |
bos@4 | 113 ofp = open('%s.%s.out' % (self.name, out), 'w') |
bos@4 | 114 else: |
bos@4 | 115 ofp = None |
bos@6 | 116 elif hunk.strip(): |
bos@4 | 117 # it's something we should execute |
bos@6 | 118 output = self.sendreceive(hunk) |
bos@6 | 119 if not ofp: |
bos@6 | 120 continue |
bos@6 | 121 # first, print the command we ran |
bos@6 | 122 if not hunk.startswith('#'): |
bos@6 | 123 nl = hunk.endswith('\n') |
bos@6 | 124 hunk = ('$ \\textbf{%s}' % |
bos@6 | 125 tex_escape(hunk.rstrip('\n'))) |
bos@6 | 126 if nl: hunk += '\n' |
bos@6 | 127 ofp.write(hunk) |
bos@4 | 128 # then its output |
bos@19 | 129 ofp.write(tex_escape(output)) |
bos@4 | 130 self.status('\n') |
bos@45 | 131 open(self.name + '.run', 'w') |
bos@4 | 132 finally: |
bos@6 | 133 try: |
bos@6 | 134 output = self.sendreceive('exit\n') |
bos@6 | 135 if ofp: |
bos@6 | 136 ofp.write(output) |
bos@6 | 137 self.cfp.close() |
bos@6 | 138 except IOError: |
bos@6 | 139 pass |
bos@6 | 140 os.kill(pid, signal.SIGTERM) |
bos@4 | 141 os.wait() |
bos@4 | 142 shutil.rmtree(tmpdir) |
bos@3 | 143 |
bos@3 | 144 def main(path='.'): |
bos@3 | 145 args = sys.argv[1:] |
bos@3 | 146 if args: |
bos@3 | 147 for a in args: |
bos@3 | 148 example(a).run() |
bos@3 | 149 return |
bos@3 | 150 for name in os.listdir(path): |
bos@3 | 151 if name == 'run-example' or name.startswith('.'): continue |
bos@3 | 152 if name.endswith('.out') or name.endswith('~'): continue |
bos@45 | 153 if name.endswith('.run'): continue |
bos@19 | 154 pathname = os.path.join(path, name) |
bos@36 | 155 st = os.lstat(pathname) |
bos@36 | 156 if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: |
bos@19 | 157 example(pathname).run() |
bos@4 | 158 print >> open(os.path.join(path, '.run'), 'w'), time.asctime() |
bos@3 | 159 |
bos@3 | 160 if __name__ == '__main__': |
bos@3 | 161 main() |