hgbook

view en/examples/run-example @ 73:9604dd885616

Fix run-example script on Debian.
Still works on Fedora, too.
author Bryan O'Sullivan <bos@serpentine.com>
date Wed Aug 30 00:01:45 2006 -0700 (2006-08-30)
parents 12df31afb4e1
children 2bfa2499e971
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 errno
9 import os
10 import pty
11 import re
12 import select
13 import shutil
14 import signal
15 import stat
16 import sys
17 import tempfile
18 import time
20 def tex_escape(s):
21 if '\\' in s:
22 s = s.replace('\\', '\\\\')
23 if '{' in s:
24 s = s.replace('{', '\\{')
25 if '}' in s:
26 s = s.replace('}', '\\}')
27 return s
29 class example:
30 shell = '/usr/bin/env bash'
31 prompt = '__run_example_prompt__ '
32 pi_re = re.compile(r'#\$\s*(name):\s*(.*)$')
34 def __init__(self, name):
35 self.name = name
37 def parse(self):
38 '''yield each hunk of input from the file.'''
39 fp = open(self.name)
40 cfp = cStringIO.StringIO()
41 for line in fp:
42 cfp.write(line)
43 if not line.rstrip().endswith('\\'):
44 yield cfp.getvalue()
45 cfp.seek(0)
46 cfp.truncate()
48 def status(self, s):
49 sys.stdout.write(s)
50 if not s.endswith('\n'):
51 sys.stdout.flush()
53 def send(self, s):
54 while s:
55 count = os.write(self.cfd, s)
56 s = s[count:]
58 def receive(self):
59 out = cStringIO.StringIO()
60 while True:
61 try:
62 s = os.read(self.cfd, 1024)
63 except OSError, err:
64 if err.errno == errno.EIO:
65 return ''
66 raise
67 out.write(s)
68 s = out.getvalue()
69 if s.endswith(self.prompt):
70 return s.replace('\r\n', '\n')[:-len(self.prompt)]
72 def sendreceive(self, s):
73 self.send(s)
74 r = self.receive()
75 if r.startswith(s):
76 r = r[len(s):]
77 return r
79 def run(self):
80 ofp = None
81 basename = os.path.basename(self.name)
82 self.status('running %s ' % basename)
83 tmpdir = tempfile.mkdtemp(prefix=basename)
84 rcfile = os.path.join(tmpdir, '.bashrc')
85 rcfp = open(rcfile, 'w')
86 print >> rcfp, 'PS1="%s"' % self.prompt
87 print >> rcfp, 'unset HISTFILE'
88 print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd()
89 print >> rcfp, 'export LANG=C'
90 print >> rcfp, 'export LC_ALL=C'
91 print >> rcfp, 'export TZ=GMT'
92 print >> rcfp, 'export HGRC="%s/.hgrc"' % tmpdir
93 print >> rcfp, 'export HGRCPATH=$HGRC'
94 print >> rcfp, 'cd %s' % tmpdir
95 rcfp.close()
96 sys.stdout.flush()
97 sys.stderr.flush()
98 pid, self.cfd = pty.fork()
99 if pid == 0:
100 cmdline = ['/usr/bin/env', 'bash', '--noediting', '--noprofile',
101 '--norc']
102 try:
103 os.execv(cmdline[0], cmdline)
104 except OSError, err:
105 print >> sys.stderr, '%s: %s' % (cmdline[0], err.strerror)
106 sys.stderr.flush()
107 os._exit(0)
108 try:
109 try:
110 # eat first prompt string from shell
111 os.read(self.cfd, 1024)
112 # setup env and prompt
113 self.sendreceive('source %s\n' % rcfile)
114 for hunk in self.parse():
115 # is this line a processing instruction?
116 m = self.pi_re.match(hunk)
117 if m:
118 pi, rest = m.groups()
119 if pi == 'name':
120 self.status('.')
121 out = rest
122 assert os.sep not in out
123 if out:
124 ofp = open('%s.%s.out' % (self.name, out), 'w')
125 else:
126 ofp = None
127 elif hunk.strip():
128 # it's something we should execute
129 output = self.sendreceive(hunk)
130 if not ofp:
131 continue
132 # first, print the command we ran
133 if not hunk.startswith('#'):
134 nl = hunk.endswith('\n')
135 hunk = ('$ \\textbf{%s}' %
136 tex_escape(hunk.rstrip('\n')))
137 if nl: hunk += '\n'
138 ofp.write(hunk)
139 # then its output
140 ofp.write(tex_escape(output))
141 self.status('\n')
142 open(self.name + '.run', 'w')
143 except:
144 print >> sys.stderr, '(killed)'
145 os.kill(pid, signal.SIGKILL)
146 pid, rc = os.wait()
147 raise
148 else:
149 try:
150 output = self.sendreceive('exit\n')
151 if ofp:
152 ofp.write(output)
153 os.close(self.cfd)
154 except IOError:
155 pass
156 os.kill(pid, signal.SIGTERM)
157 pid, rc = os.wait()
158 if rc:
159 if os.WIFEXITED(rc):
160 print >> sys.stderr, '(exit %s)' % os.WEXITSTATUS(rc)
161 elif os.WIFSIGNALED(rc):
162 print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc)
163 return rc
164 finally:
165 shutil.rmtree(tmpdir)
167 def main(path='.'):
168 args = sys.argv[1:]
169 errs = 0
170 if args:
171 for a in args:
172 if example(a).run():
173 errs += 1
174 return errs
175 for name in os.listdir(path):
176 if name == 'run-example' or name.startswith('.'): continue
177 if name.endswith('.out') or name.endswith('~'): continue
178 if name.endswith('.run'): continue
179 pathname = os.path.join(path, name)
180 st = os.lstat(pathname)
181 if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
182 if example(pathname).run():
183 errs += 1
184 print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
185 return errs
187 if __name__ == '__main__':
188 sys.exit(main())