Skip to content

Commit b75c380

Browse files
Add run-examples.py and fix some of the examples.
1 parent 1b79755 commit b75c380

File tree

2 files changed

+242
-17
lines changed

2 files changed

+242
-17
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import os
2+
import os.path
3+
import shutil
4+
import subprocess
5+
import sys
6+
import tempfile
7+
from textwrap import dedent
8+
import traceback
9+
10+
11+
HOWTO_DIR = os.path.dirname(__file__)
12+
HOWTO_FILE = os.path.join(HOWTO_DIR, 'multiple-interpreters.rst')
13+
14+
VERBOSITY = 3
15+
16+
17+
def get_examples(lines):
18+
examples = {}
19+
current = None
20+
expected = None
21+
start = None
22+
for lno, line in enumerate(lines, 1):
23+
if current is None:
24+
if line.endswith('::'):
25+
# It *might* be an example.
26+
current = []
27+
else:
28+
if not current:
29+
if line.strip():
30+
if line == ' from concurrent import interpreters':
31+
# It *is* an example.
32+
current.append(line)
33+
expected = []
34+
start = lno
35+
else:
36+
# It wasn't actually an example.
37+
current = None
38+
elif not line.strip() or line.startswith(' '):
39+
# The example continues.
40+
current.append(line)
41+
before, sep, after = line.partition('# prints: ')
42+
if sep:
43+
assert not before.strip(), before
44+
expected.append(after)
45+
else:
46+
# We've reached the end of the example.
47+
assert not current[-1].strip(), current
48+
example = dedent(os.linesep.join(current))
49+
expected = ''.join(f'{l}{os.linesep}' for l in expected)
50+
examples[start] = (example, expected)
51+
current = expected = start = None
52+
53+
if line.endswith('::'):
54+
# It *might* be an example.
55+
current = []
56+
if current:
57+
if current[-1].strip():
58+
current.append('')
59+
example = dedent(os.linesep.join(current))
60+
expected = ''.join(f'{l}{os.linesep}' for l in expected)
61+
examples[start] = (example, expected)
62+
current = expected = start = None
63+
return examples
64+
65+
66+
def write_example(examplesdir, name, text):
67+
filename = os.path.join(examplesdir, f'example-{name}.py')
68+
with open(filename, 'w') as outfile:
69+
outfile.write(text)
70+
return filename, name, text
71+
72+
73+
def run_example(filename, expected):
74+
if isinstance(expected, str):
75+
rc = 0
76+
stdout = expected
77+
stderr = ''
78+
else:
79+
rc, stdout, stderr = expected
80+
81+
proc = subprocess.run(
82+
[sys.executable, filename],
83+
stdout=subprocess.PIPE,
84+
stderr=subprocess.PIPE,
85+
text=True,
86+
cwd=os.path.dirname(filename),
87+
)
88+
if proc.returncode != rc:
89+
if proc.returncode != 0 and proc.stderr:
90+
print(proc.stderr)
91+
raise AssertionError((proc.returncode, rc))
92+
assert proc.stdout == stdout, (proc.stdout, stdout)
93+
assert proc.stderr == stderr, (proc.stderr, stderr)
94+
95+
96+
#######################################
97+
# the script
98+
99+
def parse_args(argv=sys.argv[1:], prog=sys.argv[0]):
100+
import argparse
101+
parser = argparse.ArgumentParser(prog=prog)
102+
103+
parser.add_argument('--dry-run', dest='dryrun', action='store_true')
104+
105+
parser.add_argument('-v', '--verbose', action='count', default=0)
106+
parser.add_argument('-q', '--quiet', action='count', default=0)
107+
108+
parser.add_argument('requested', nargs='*')
109+
110+
args = parser.parse_args(argv)
111+
ns = vars(args)
112+
113+
args.verbosity = max(0, VERBOSITY + ns.pop('verbose') - ns.pop('quiet'))
114+
115+
requested = []
116+
for req in args.requested:
117+
for i in req.replace(',', ' ').split():
118+
requested.append(int(i))
119+
args.requested = requested or None
120+
121+
return ns
122+
123+
124+
def main(requested=None, *, verbosity=VERBOSITY, dryrun=False):
125+
with open(HOWTO_FILE) as infile:
126+
examples = get_examples(l.rstrip(os.linesep) for l in infile)
127+
examplesdir = tempfile.mkdtemp(prefix='multi-interp-howto-')
128+
try:
129+
if requested:
130+
requested = set(requested)
131+
_req = f', {len(requested)} requested'
132+
else:
133+
_req = ''
134+
summary = f'# ({len(examples)} found{_req})'
135+
print(summary)
136+
137+
failed = []
138+
for i, (lno, (text, expected)) in enumerate(examples.items(), 1):
139+
if requested and i not in requested:
140+
continue
141+
name = 'multiinterp-{i}'
142+
print()
143+
print('#'*60)
144+
print(f'# example {i} ({os.path.relpath(HOWTO_FILE)}:{lno})')
145+
print('#'*60)
146+
print()
147+
if verbosity > VERBOSITY or (verbosity == VERBOSITY and dryrun):
148+
print(text)
149+
print('----')
150+
if expected.rstrip():
151+
print(expected.rstrip(os.linesep))
152+
print('----')
153+
if dryrun:
154+
continue
155+
filename, _, _ = write_example(examplesdir, name, text)
156+
try:
157+
run_example(filename, expected)
158+
except Exception:
159+
traceback.print_exc()
160+
failed.append(str(i))
161+
162+
req = f'/{len(requested)}' if requested else ''
163+
if failed:
164+
print()
165+
print(f'{len(failed)} failed: {",".join(failed)}')
166+
print(summary)
167+
elif verbosity > VERBOSITY or dryrun:
168+
print()
169+
print(f'{len(failed)} failed')
170+
print(summary)
171+
172+
return len(failed)
173+
finally:
174+
shutil.rmtree(examplesdir)
175+
176+
177+
if __name__ == '__main__':
178+
kwargs = parse_args()
179+
ec = main(**kwargs)
180+
sys.exit(ec)

0 commit comments

Comments
 (0)