Skip to content

Commit 7302ff3

Browse files
author
Matthias Koeppe
committed
src/bin/sage-fixdoctests: Run doctester for all files in parallel, handle directories
1 parent 50baf64 commit 7302ff3

File tree

1 file changed

+55
-30
lines changed

1 file changed

+55
-30
lines changed

src/bin/sage-fixdoctests

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import sys
4040
from argparse import ArgumentParser, FileType
4141
from pathlib import Path
4242

43-
from sage.doctest.control import skipfile
43+
from sage.doctest.control import DocTestDefaults, DocTestController
4444
from sage.doctest.parsing import parse_file_optional_tags, parse_optional_tags, unparse_optional_tags, update_optional_tags
4545
from sage.env import SAGE_ROOT
4646
from sage.features import PythonModule
@@ -307,17 +307,21 @@ def process_block(block, src_in_lines, file_optional_tags):
307307

308308

309309
# set input and output files
310-
if len(args.filename) == 2 and not args.overwrite and not args.no_overwrite:
311-
inputs, outputs = [args.filename[0]], [args.filename[1]]
312-
print("sage-fixdoctests: When passing two filenames, the second one is taken as an output filename; "
313-
"this is deprecated. To pass two input filenames, use the option --overwrite.")
314-
elif args.no_overwrite:
315-
inputs, outputs = args.filename, [input + ".fixed" for input in args.filename]
316-
else:
317-
inputs = outputs = args.filename
310+
def output_filename(filename):
311+
if len(args.filename) == 2 and not args.overwrite and not args.no_overwrite:
312+
if args.filename[0] == filename:
313+
print("sage-fixdoctests: When passing two filenames, the second one is taken as an output filename; "
314+
"this is deprecated. To pass two input filenames, use the option --overwrite.")
315+
return args.filename[1]
316+
return filename + ".fixed"
317+
if args.no_overwrite:
318+
return filename + ".fixed"
319+
return filename
318320

319321
# Test the doctester, putting the output of the test into sage's temporary directory
320-
if not args.no_test:
322+
if args.no_test:
323+
doc_out = ''
324+
else:
321325
executable = f'{os.path.relpath(args.venv)}/bin/sage' if args.venv else 'sage'
322326
environment_args = f'--environment {args.environment} ' if args.environment != runtest_default_environment else ''
323327
long_args = f'--long ' if args.long else ''
@@ -331,38 +335,54 @@ if not args.no_test:
331335
if status := os.waitstatus_to_exitcode(os.system(f'{cmdline} > {shlex.quote(doc_file)}')):
332336
print(f'Doctester exited with error status {status}')
333337
sys.exit(status)
338+
# Run the doctester, putting the output of the test into sage's temporary directory
339+
input_args = " ".join(shlex.quote(f) for f in args.filename)
340+
cmdline = f'{shlex.quote(executable)} -t -p {environment_args}{long_args}{probe_args}{lib_args}{input_args}'
341+
print(f'Running "{cmdline}"')
342+
os.system(f'{cmdline} > {shlex.quote(doc_file)}')
334343

335-
for input, output in zip(inputs, outputs):
336-
if (skipfile_result := skipfile(input, True, log=print)) is True:
337-
continue
338-
339-
if args.no_test:
340-
doc_out = ''
341-
else:
342-
# Run the doctester, putting the output of the test into sage's temporary directory
343-
cmdline = f'{shlex.quote(executable)} -t {environment_args}{long_args}{probe_args}{lib_args}{shlex.quote(input)}'
344-
print(f'Running "{cmdline}"')
345-
os.system(f'{cmdline} > {shlex.quote(doc_file)}')
346-
347-
with open(doc_file, 'r') as doc:
348-
doc_out = doc.read()
344+
with open(doc_file, 'r') as doc:
345+
doc_out = doc.read()
349346

350347
# echo control messages
351348
for m in re.finditer('^Skipping .*', doc_out, re.MULTILINE):
352349
print('sage-runtests: ' + m.group(0))
353-
break
354-
else:
355-
sep = "**********************************************************************\n"
356-
doctests = doc_out.split(sep)
350+
351+
sep = "**********************************************************************\n"
352+
doctests = doc_out.split(sep)
353+
354+
seen = set()
355+
356+
def block_filename(block):
357+
if not (m := re.match('File "([^"]*)", line ([0-9]+), in ', block)):
358+
return None
359+
return m.group(1)
360+
361+
def expanded_filename_args():
362+
DD = DocTestDefaults(optional='all')
363+
DC = DocTestController(DD, args.filename)
364+
DC.add_files()
365+
DC.expand_files_into_sources()
366+
for source in DC.sources:
367+
yield source.path
368+
369+
def process_grouped_blocks(grouped_iterator):
370+
371+
for input, blocks in grouped_iterator:
372+
373+
if not input: # Blocks of noise
374+
continue
375+
if input in seen:
376+
continue
377+
seen.add(input)
357378

358379
with open(input, 'r') as test_file:
359380
src_in = test_file.read()
360381
src_in_lines = src_in.splitlines()
361382
shallow_copy_of_src_in_lines = list(src_in_lines)
362-
363383
file_optional_tags = set(parse_file_optional_tags(enumerate(src_in_lines)))
364384

365-
for block in doctests:
385+
for block in blocks:
366386
process_block(block, src_in_lines, file_optional_tags)
367387

368388
# Now source line numbers do not matter any more, and lines can be real lines again
@@ -394,6 +414,7 @@ for input, output in zip(inputs, outputs):
394414
persistent_optional_tags = {}
395415

396416
if src_in_lines != shallow_copy_of_src_in_lines:
417+
output = output_filename(input)
397418
with open(output, 'w') as test_output:
398419
for line in src_in_lines:
399420
if line is None:
@@ -411,3 +432,7 @@ for input, output in zip(inputs, outputs):
411432
subprocess.call(['git', '--no-pager', 'diff', relative], cwd=SAGE_ROOT)
412433
else:
413434
print(f"No fixes made in '{input}'")
435+
436+
process_grouped_blocks(
437+
itertools.chain(itertools.groupby(doctests, block_filename),
438+
((filename, []) for filename in expanded_filename_args())))

0 commit comments

Comments
 (0)