1b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) 2b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 3b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#!/usr/bin/env python2.4 4b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) 5b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 7b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 8b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikThese are functions for use when doctest-testing a document. 9b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 10b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 11b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport subprocess 12b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport doctest 13b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport os 14b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport sys 15b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport shutil 16b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport re 17b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport cgi 18b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport rfc822 19b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom cStringIO import StringIO 20b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste.util import PySourceColor 21b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 22b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 23b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikhere = os.path.abspath(__file__) 24b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikpaste_parent = os.path.dirname( 25b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.path.dirname(os.path.dirname(here))) 26b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 27b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef run(command): 28b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = run_raw(command) 29b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if data: 30b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print(data) 31b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 32b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef run_raw(command): 33b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 34b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Runs the string command, returns any output. 35b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 36b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik proc = subprocess.Popen(command, shell=True, 37b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stderr=subprocess.STDOUT, 38b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout=subprocess.PIPE, env=_make_env()) 39b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = proc.stdout.read() 40b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik proc.wait() 41b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik while data.endswith('\n') or data.endswith('\r'): 42b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = data[:-1] 43b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if data: 44b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = '\n'.join( 45b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik [l for l in data.splitlines() if l]) 46b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return data 47b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 48b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 49b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 50b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef run_command(command, name, and_print=False): 51b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output = run_raw(command) 52b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = '$ %s\n%s' % (command, output) 53b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_file('shell-command', name, description='shell transcript', 54b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data=data) 55b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if and_print and output: 56b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print(output) 57b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 58b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _make_env(): 59b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik env = os.environ.copy() 60b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik env['PATH'] = (env.get('PATH', '') 61b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + ':' 62b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + os.path.join(paste_parent, 'scripts') 63b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + ':' 64b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + os.path.join(paste_parent, 'paste', '3rd-party', 65b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'sqlobject-files', 'scripts')) 66b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik env['PYTHONPATH'] = (env.get('PYTHONPATH', '') 67b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + ':' 68b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik + paste_parent) 69b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return env 70b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 71b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef clear_dir(dir): 72b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 73b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Clears (deletes) the given directory 74b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 75b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik shutil.rmtree(dir, True) 76b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 77b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef ls(dir=None, recurse=False, indent=0): 78b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 79b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Show a directory listing 80b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 81b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dir = dir or os.getcwd() 82b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fns = os.listdir(dir) 83b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fns.sort() 84b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for fn in fns: 85b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik full = os.path.join(dir, fn) 86b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if os.path.isdir(full): 87b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fn = fn + '/' 88b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print(' '*indent + fn) 89b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if os.path.isdir(full) and recurse: 90b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ls(dir=full, recurse=True, indent=indent+2) 91b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 92b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdefault_app = None 93b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdefault_url = None 94b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 95b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef set_default_app(app, url): 96b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik global default_app 97b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik global default_url 98b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik default_app = app 99b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik default_url = url 100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef resource_filename(fn): 102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Returns the filename of the resource -- generally in the directory 104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik resources/DocumentName/fn 105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return os.path.join( 107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.path.dirname(sys.testing_document_filename), 108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'resources', 109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.path.splitext(os.path.basename(sys.testing_document_filename))[0], 110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fn) 111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef show(path_info, example_name): 113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fn = resource_filename(example_name + '.html') 114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out = StringIO() 115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert default_app is not None, ( 116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "No default_app set") 117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik url = default_url + path_info 118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write('<span class="doctest-url"><a href="%s">%s</a></span><br>\n' 119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (url, url)) 120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write('<div class="doctest-example">\n') 121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik proc = subprocess.Popen( 122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ['paster', 'serve' '--server=console', '--no-verbose', 123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '--url=' + path_info], 124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stderr=subprocess.PIPE, 125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout=subprocess.PIPE, 126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik env=_make_env()) 127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout, errors = proc.communicate() 128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout = StringIO(stdout) 129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers = rfc822.Message(stdout) 130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik content = stdout.read() 131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for header, value in headers.items(): 132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if header.lower() == 'status' and int(value.split()[0]) == 200: 133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik continue 134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if header.lower() in ('content-type', 'content-length'): 135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik continue 136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if (header.lower() == 'set-cookie' 137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and value.startswith('_SID_')): 138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik continue 139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write('<span class="doctest-header">%s: %s</span><br>\n' 140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (header, value)) 141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines = [l for l in content.splitlines() if l.strip()] 142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for line in lines: 143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write(line + '\n') 144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if errors: 145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write('<pre class="doctest-errors">%s</pre>' 146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % errors) 147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.write('</div>\n') 148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik result = out.getvalue() 149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not os.path.exists(fn): 150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(fn, 'wb') 151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.write(result) 152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(fn, 'rb') 155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik expected = f.read() 156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not html_matches(expected, result): 158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('Pages did not match. Expected from %s:' % fn) 159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('-'*60) 160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print(expected) 161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('='*60) 162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('Actual output:') 163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('-'*60) 164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print(result) 165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef html_matches(pattern, text): 167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = re.escape(pattern) 168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = regex.replace(r'\.\.\.', '.*') 169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = re.sub(r'0x[0-9a-f]+', '.*', regex) 170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik regex = '^%s$' % regex 171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return re.search(regex, text) 172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef convert_docstring_string(data): 174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if data.startswith('\n'): 175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = data[1:] 176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines = data.splitlines() 177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik new_lines = [] 178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for line in lines: 179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if line.rstrip() == '.': 180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik new_lines.append('') 181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik new_lines.append(line) 183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = '\n'.join(new_lines) + '\n' 184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return data 185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef create_file(path, version, data): 187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = convert_docstring_string(data) 188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik write_data(path, data) 189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_file(path, version) 190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef append_to_file(path, version, data): 192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = convert_docstring_string(data) 193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(path, 'a') 194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.write(data) 195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # I think these appends can happen so quickly (in less than a second) 197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # that the .pyc file doesn't appear to be expired, even though it 198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # is after we've made this change; so we have to get rid of the .pyc 199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # file: 200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if path.endswith('.py'): 201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pyc_file = path + 'c' 202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if os.path.exists(pyc_file): 203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.unlink(pyc_file) 204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_file(path, version, description='added to %s' % path, 205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data=data) 206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef show_file(path, version, description=None, data=None): 208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ext = os.path.splitext(path)[1] 209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if data is None: 210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(path, 'rb') 211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = f.read() 212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if ext == '.py': 214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = ('<div class="source-code">%s</div>' 215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % PySourceColor.str2html(data, PySourceColor.dark)) 216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = '<pre class="source-code">%s</pre>' % cgi.escape(data, 1) 218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = '<span class="source-filename">%s</span><br>%s' % ( 219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik description or path, html) 220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik write_data(resource_filename('%s.%s.gen.html' % (path, version)), 221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html) 222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef call_source_highlight(input, format): 224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik proc = subprocess.Popen(['source-highlight', '--out-format=html', 225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '--no-doc', '--css=none', 226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '--src-lang=%s' % format], shell=False, 227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout=subprocess.PIPE) 228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stdout, stderr = proc.communicate(input) 229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik result = stdout 230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik proc.wait() 231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return result 232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef write_data(path, data): 235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dir = os.path.dirname(os.path.abspath(path)) 236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not os.path.exists(dir): 237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.makedirs(dir) 238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(path, 'wb') 239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.write(data) 240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef change_file(path, changes): 244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(os.path.abspath(path), 'rb') 245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines = f.readlines() 246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for change_type, line, text in changes: 248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if change_type == 'insert': 249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines[line:line] = [text] 250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif change_type == 'delete': 251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lines[line:text] = [] 252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert 0, ( 254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "Unknown change_type: %r" % change_type) 255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f = open(path, 'wb') 256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.write(''.join(lines)) 257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik f.close() 258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass LongFormDocTestParser(doctest.DocTestParser): 260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This parser recognizes some reST comments as commands, without 263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik prompts or expected output, like: 264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .. run: 266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik do_this(... 268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ...) 269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik _EXAMPLE_RE = re.compile(r""" 272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Source consists of a PS1 line followed by zero or more PS2 lines. 273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?: (?P<source> 274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:^(?P<indent> [ ]*) >>> .*) # PS1 line 275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:\n [ ]* \.\.\. .*)*) # PS2 lines 276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik \n? 277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Want consists of any non-blank lines that do not start with PS1. 278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?P<want> (?:(?![ ]*$) # Not a blank line 279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?![ ]*>>>) # Not a line starting with PS1 280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .*$\n? # But any other line 281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik )*)) 282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik | 283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?: # This is for longer commands that are prefixed with a reST 284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # comment like '.. run:' (two colons makes that a directive). 285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # These commands cannot have any output. 286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:^\.\.[ ]*(?P<run>run):[ ]*\n) # Leading command/command 288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:[ ]*\n)? # Blank line following 289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?P<runsource> 290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:(?P<runindent> [ ]+)[^ ].*$) 291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:\n [ ]+ .*)*) 292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ) 293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik | 294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?: # This is for shell commands 295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?P<shellsource> 297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:^(P<shellindent> [ ]*) [$] .*) # Shell line 298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?:\n [ ]* [>] .*)*) # Continuation 299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik \n? 300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Want consists of any non-blank lines that do not start with $ 301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?P<shellwant> (?:(?![ ]*$) 302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (?![ ]*[$]$) 303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik .*$\n? 304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik )*)) 305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """, re.MULTILINE | re.VERBOSE) 306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def _parse_example(self, m, name, lineno): 308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik r""" 309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Given a regular expression match from `_EXAMPLE_RE` (`m`), 310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return a pair `(source, want)`, where `source` is the matched 311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik example's source code (with prompts and indentation stripped); 312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and `want` is the example's expected output (with indentation 313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik stripped). 314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik `name` is the string's name, and `lineno` is the line number 316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik where the example starts; both are used for error messages. 317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik >>> def parseit(s): 319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ... p = LongFormDocTestParser() 320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ... return p._parse_example(p._EXAMPLE_RE.search(s), '<string>', 1) 321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik >>> parseit('>>> 1\n1') 322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ('1', {}, '1', None) 323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik >>> parseit('>>> (1\n... +1)\n2') 324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ('(1\n+1)', {}, '2', None) 325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik >>> parseit('.. run:\n\n test1\n test2\n') 326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ('test1\ntest2', {}, '', None) 327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Get the example's indentation level. 329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik runner = m.group('run') or '' 330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik indent = len(m.group('%sindent' % runner)) 331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Divide source into lines; check that they're properly 333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # indented; and then strip their indentation & prompts. 334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik source_lines = m.group('%ssource' % runner).split('\n') 335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if runner: 336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._check_prefix(source_lines[1:], ' '*indent, name, lineno) 337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._check_prompt_blank(source_lines, indent, name, lineno) 339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._check_prefix(source_lines[2:], ' '*indent + '.', name, lineno) 340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if runner: 341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik source = '\n'.join([sl[indent:] for sl in source_lines]) 342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik source = '\n'.join([sl[indent+4:] for sl in source_lines]) 344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if runner: 346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik want = '' 347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_msg = None 348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Divide want into lines; check that it's properly indented; and 350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # then strip the indentation. Spaces before the last newline should 351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # be preserved, so plain rstrip() isn't good enough. 352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik want = m.group('want') 353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik want_lines = want.split('\n') 354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): 355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik del want_lines[-1] # forget final newline & spaces after it 356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._check_prefix(want_lines, ' '*indent, name, 357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lineno + len(source_lines)) 358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik want = '\n'.join([wl[indent:] for wl in want_lines]) 359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 360b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # If `want` contains a traceback message, then extract it. 361b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik m = self._EXCEPTION_RE.match(want) 362b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if m: 363b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_msg = m.group('msg') 364b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 365b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_msg = None 366b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 367b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Extract options from the source. 368b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik options = self._find_options(source, name, lineno) 369b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 370b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return source, options, want, exc_msg 371b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 372b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 373b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def parse(self, string, name='<string>'): 374b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 375b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Divide the given string into examples and intervening text, 376b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik and return them as a list of alternating Examples and strings. 377b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Line numbers for the Examples are 0-based. The optional 378b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik argument `name` is a name identifying this string, and is only 379b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik used for error messages. 380b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 381b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik string = string.expandtabs() 382b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # If all lines begin with the same indentation, then strip it. 383b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik min_indent = self._min_indent(string) 384b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if min_indent > 0: 385b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik string = '\n'.join([l[min_indent:] for l in string.split('\n')]) 386b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 387b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output = [] 388b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik charno, lineno = 0, 0 389b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Find all doctest examples in the string: 390b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for m in self._EXAMPLE_RE.finditer(string): 391b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Add the pre-example text to `output`. 392b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output.append(string[charno:m.start()]) 393b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Update lineno (lines before this example) 394b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lineno += string.count('\n', charno, m.start()) 395b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Extract info from the regexp match. 396b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (source, options, want, exc_msg) = \ 397b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self._parse_example(m, name, lineno) 398b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Create an Example, and add it to the list. 399b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not self._IS_BLANK_OR_COMMENT(source): 400b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: Erg, this is the only line I need to change... 401b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output.append(doctest.Example( 402b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik source, want, exc_msg, 403b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lineno=lineno, 404b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik indent=min_indent+len(m.group('indent') or m.group('runindent')), 405b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik options=options)) 406b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Update lineno (lines inside this example) 407b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik lineno += string.count('\n', m.start(), m.end()) 408b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Update charno. 409b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik charno = m.end() 410b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Add any remaining post-example text to `output`. 411b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output.append(string[charno:]) 412b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return output 413b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 414b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 415b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 416b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikif __name__ == '__main__': 417b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if sys.argv[1:] and sys.argv[1] == 'doctest': 418b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik doctest.testmod() 419b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik sys.exit() 420b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not paste_parent in sys.path: 421b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik sys.path.append(paste_parent) 422b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for fn in sys.argv[1:]: 423b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fn = os.path.abspath(fn) 424b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: OK, ick; but this module gets loaded twice 425b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik sys.testing_document_filename = fn 426b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik doctest.testfile( 427b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fn, module_relative=False, 428b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, 429b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik parser=LongFormDocTestParser()) 430b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik new = os.path.splitext(fn)[0] + '.html' 431b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert new != fn 432b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.system('rst2html.py %s > %s' % (fn, new)) 433