hgbook
view 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 |
line source
1 #!/usr/bin/env 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 signal
13 import stat
14 import sys
15 import tempfile
16 import time
18 def tex_escape(s):
19 if '\\' in s:
20 s = s.replace('\\', '\\\\')
21 if '{' in s:
22 s = s.replace('{', '\\{')
23 if '}' in s:
24 s = s.replace('}', '\\}')
25 return s
27 class example:
28 shell = '/bin/bash'
29 prompt = '__run_example_prompt__\n'
30 pi_re = re.compile('#\$\s*(name):\s*(.*)$')
32 def __init__(self, name):
33 self.name = name
35 def parse(self):
36 '''yield each hunk of input from the file.'''
37 fp = open(self.name)
38 cfp = cStringIO.StringIO()
39 for line in fp:
40 cfp.write(line)
41 if not line.rstrip().endswith('\\'):
42 yield cfp.getvalue()
43 cfp.seek(0)
44 cfp.truncate()
46 def status(self, s):
47 sys.stdout.write(s)
48 if not s.endswith('\n'):
49 sys.stdout.flush()
51 def send(self, s):
52 self.cfp.write(s)
53 self.cfp.flush()
55 def receive(self):
56 out = cStringIO.StringIO()
57 while True:
58 s = self.cfp.readline().replace('\r\n', '\n')
59 if not s or s == self.prompt:
60 break
61 out.write(s)
62 return out.getvalue()
64 def sendreceive(self, s):
65 self.send(s)
66 r = self.receive()
67 if r.startswith(s):
68 r = r[len(s):]
69 return r
71 def run(self):
72 ofp = None
73 basename = os.path.basename(self.name)
74 self.status('running %s ' % basename)
75 tmpdir = tempfile.mkdtemp(prefix=basename)
76 rcfile = os.path.join(tmpdir, '.bashrc')
77 rcfp = open(rcfile, 'w')
78 print >> rcfp, 'PS1="%s"' % self.prompt
79 print >> rcfp, 'unset HISTFILE'
80 print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd()
81 print >> rcfp, 'export LANG=C'
82 print >> rcfp, 'export LC_ALL=C'
83 print >> rcfp, 'export TZ=GMT'
84 print >> rcfp, 'export HGRC="%s/.hgrc"' % tmpdir
85 print >> rcfp, 'export HGRCPATH=$HGRC'
86 print >> rcfp, 'cd %s' % tmpdir
87 rcfp.close()
88 sys.stdout.flush()
89 sys.stderr.flush()
90 pid, fd = pty.fork()
91 if pid == 0:
92 cmdline = ['/bin/bash', '--noediting', '--noprofile', '--norc']
93 try:
94 os.execv(cmdline[0], cmdline)
95 except OSError, err:
96 print >> sys.stderr, '%s: %s' % (cmdline[0], err.strerror)
97 sys.stderr.flush()
98 os._exit(0)
99 self.cfp = os.fdopen(fd, 'w+')
100 try:
101 # setup env and prompt
102 self.sendreceive('source %s\n\n' % rcfile)
103 for hunk in self.parse():
104 # is this line a processing instruction?
105 m = self.pi_re.match(hunk)
106 if m:
107 pi, rest = m.groups()
108 if pi == 'name':
109 self.status('.')
110 out = rest
111 assert os.sep not in out
112 if out:
113 ofp = open('%s.%s.out' % (self.name, out), 'w')
114 else:
115 ofp = None
116 elif hunk.strip():
117 # it's something we should execute
118 output = self.sendreceive(hunk)
119 if not ofp:
120 continue
121 # first, print the command we ran
122 if not hunk.startswith('#'):
123 nl = hunk.endswith('\n')
124 hunk = ('$ \\textbf{%s}' %
125 tex_escape(hunk.rstrip('\n')))
126 if nl: hunk += '\n'
127 ofp.write(hunk)
128 # then its output
129 ofp.write(tex_escape(output))
130 self.status('\n')
131 open(self.name + '.run', 'w')
132 finally:
133 try:
134 output = self.sendreceive('exit\n')
135 if ofp:
136 ofp.write(output)
137 self.cfp.close()
138 except IOError:
139 pass
140 os.kill(pid, signal.SIGTERM)
141 os.wait()
142 shutil.rmtree(tmpdir)
144 def main(path='.'):
145 args = sys.argv[1:]
146 if args:
147 for a in args:
148 example(a).run()
149 return
150 for name in os.listdir(path):
151 if name == 'run-example' or name.startswith('.'): continue
152 if name.endswith('.out') or name.endswith('~'): continue
153 if name.endswith('.run'): continue
154 pathname = os.path.join(path, name)
155 st = os.lstat(pathname)
156 if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
157 example(pathname).run()
158 print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
160 if __name__ == '__main__':
161 main()