Skip to content

Commit

Permalink
gh-36628: some simplifications in doctest/ folder (ruff C4)
Browse files Browse the repository at this point in the history
    
use `ruff check --fix --select=C4 src/sage/doctest` to fix a few details

plus some manual tweaking afterwards

### 📝 Checklist

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
    
URL: #36628
Reported by: Frédéric Chapoton
Reviewer(s): Matthias Köppe
  • Loading branch information
Release Manager committed Nov 5, 2023
2 parents 7354bdb + ecbd9ac commit 29e95c5
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 55 deletions.
25 changes: 13 additions & 12 deletions src/sage/doctest/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import sys
import time
import json
import re
import shlex
import types
import sage.misc.flatten
Expand Down Expand Up @@ -66,6 +65,7 @@
except ImportError:
pass


class DocTestDefaults(SageObject):
"""
This class is used for doctesting the Sage doctest module.
Expand Down Expand Up @@ -145,7 +145,7 @@ def __init__(self, **kwds):
# automatically anyway. However, this default is still used for
# displaying user-defined optional tags and we don't want to see
# the auto_optional_tags there.
self.optional = set(['sage']) | auto_optional_tags
self.optional = {'sage'} | auto_optional_tags
self.hide = ''
self.probe = ''

Expand Down Expand Up @@ -224,6 +224,7 @@ def skipdir(dirname):
return True
return False


def skipfile(filename, tested_optional_tags=False, *,
if_installed=False, log=None):
"""
Expand Down Expand Up @@ -317,8 +318,8 @@ def skipfile(filename, tested_optional_tags=False, *,
return file_tag_string

elif tested_optional_tags is not True:
extra = set(tag for tag in file_optional_tags
if tag not in tested_optional_tags)
extra = {tag for tag in file_optional_tags
if tag not in tested_optional_tags}
if extra:
file_tag_string = unparse_optional_tags(file_optional_tags, prefix='')
if log:
Expand Down Expand Up @@ -445,7 +446,7 @@ def __init__(self, options, args):
options.hidden_features = set()
if isinstance(options.hide, str):
if not len(options.hide):
options.hide = set([])
options.hide = set()
else:
s = options.hide.lower()
options.hide = set(s.split(','))
Expand All @@ -455,12 +456,12 @@ def __init__(self, options, args):
if 'all' in options.hide:
options.hide.discard('all')
from sage.features.all import all_features
feature_names = set([f.name for f in all_features() if not f.is_standard()])
feature_names = {f.name for f in all_features() if not f.is_standard()}
options.hide = options.hide.union(feature_names)
if 'optional' in options.hide:
options.hide.discard('optional')
from sage.features.all import all_features
feature_names = set([f.name for f in all_features() if f.is_optional()])
feature_names = {f.name for f in all_features() if f.is_optional()}
options.hide = options.hide.union(feature_names)

options.disabled_optional = set()
Expand Down Expand Up @@ -1085,7 +1086,7 @@ def sort_sources(self):
"""
if self.options.nthreads > 1 and len(self.sources) > self.options.nthreads:
self.log("Sorting sources by runtime so that slower doctests are run first....")
default = dict(walltime=0)
default = {'walltime': 0}

def sort_key(source):
basename = source.basename
Expand Down Expand Up @@ -1153,7 +1154,7 @@ def run_doctests(self):
self.cleanup(False)
else:
self.log("No files to doctest")
self.reporter = DictAsObject(dict(error_status=0, stats={}))
self.reporter = DictAsObject({'error_status': 0, 'stats': {}})

def cleanup(self, final=True):
"""
Expand Down Expand Up @@ -1315,9 +1316,9 @@ def run_val_gdb(self, testing=False):
flags = os.getenv("SAGE_MEMCHECK_FLAGS")
if flags is None:
flags = "--leak-resolution=high --leak-check=full --num-callers=25 "
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind","pyalloc.supp"))
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind","sage.supp"))
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind","sage-additional.supp"))
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind", "pyalloc.supp"))
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind", "sage.supp"))
flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE,"valgrind", "sage-additional.supp"))
elif opt.massif:
toolname = "massif"
flags = os.getenv("SAGE_MASSIF_FLAGS", "--depth=6 ")
Expand Down
30 changes: 15 additions & 15 deletions src/sage/doctest/forker.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,10 @@ def compiler(example):
# findlinestarts() returns pairs (index, lineno) where
# "index" is the index in the bytecode where the line
# number changes to "lineno".
linenumbers1 = set(lineno for (index, lineno)
in findlinestarts(code))
linenumbers2 = set(lineno for (index, lineno)
in findlinestarts(execcode))
linenumbers1 = {lineno for (index, lineno)
in findlinestarts(code)}
linenumbers2 = {lineno for (index, lineno)
in findlinestarts(execcode)}
if linenumbers1 != linenumbers2:
raise SyntaxError("doctest is not a single statement")

Expand Down Expand Up @@ -1726,7 +1726,7 @@ def serial_dispatch(self):

with tempfile.TemporaryFile() as outtmpfile:
result = DocTestTask(source)(self.controller.options,
outtmpfile, self.controller.logger)
outtmpfile, self.controller.logger)
outtmpfile.seek(0)
output = bytes_to_str(outtmpfile.read())

Expand Down Expand Up @@ -2334,7 +2334,7 @@ def save_result_output(self):
try:
self.result = self.result_queue.get(block=False)
except Empty:
self.result = (0, DictAsObject(dict(err='noresult')))
self.result = (0, DictAsObject({'err': 'noresult'}))
del self.result_queue

self.outtmpfile.seek(0)
Expand Down Expand Up @@ -2536,17 +2536,17 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None):
result = None
try:
runner = SageDocTestRunner(
SageOutputChecker(),
verbose=options.verbose,
outtmpfile=outtmpfile,
msgfile=msgfile,
sage_options=options,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)
SageOutputChecker(),
verbose=options.verbose,
outtmpfile=outtmpfile,
msgfile=msgfile,
sage_options=options,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)
runner.basename = self.source.basename
runner.filename = self.source.path
N = options.file_iterations
results = DictAsObject(dict(walltime=[], cputime=[],
err=None, walltime_skips=0))
results = DictAsObject({'walltime': [], 'cputime': [],
'err': None, 'walltime_skips': 0})

# multiprocessing.Process instances don't run exit
# functions, so we run the functions added by doctests
Expand All @@ -2571,7 +2571,7 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None):
except BaseException:
exc_info = sys.exc_info()
tb = "".join(traceback.format_exception(*exc_info))
result = (0, DictAsObject(dict(err=exc_info[0], tb=tb)))
result = (0, DictAsObject({'err': exc_info[0], 'tb': tb}))

if result_queue is not None:
result_queue.put(result, False)
Expand Down
16 changes: 8 additions & 8 deletions src/sage/doctest/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def fake_RIFtol(*args):
ansi_escape_sequence = re.compile(r'(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])')

special_optional_regex = 'arb216|arb218|py2|long time|not implemented|not tested|known bug'
tag_with_explanation_regex = fr'((?:\w|[.])+)\s*(?:\((.*?)\))?'
tag_with_explanation_regex = r'((?:\w|[.])+)\s*(?:\((.*?)\))?'
optional_regex = re.compile(fr'(?P<cmd>{special_optional_regex})\s*(?:\((?P<cmd_explanation>.*?)\))?|'
fr'[^ a-z]\s*(optional|needs)(?:\s|[:-])*(?P<tags>(?:(?:{tag_with_explanation_regex})\s*)*)',
re.IGNORECASE)
Expand Down Expand Up @@ -470,7 +470,7 @@ def update_optional_tags(line, tags=None, *, add_tags=None, remove_tags=None, fo
| V V V V V V v v v v
| sage: # optional - magma, needs sage.symbolic
"""
if not (m := re.match('( *sage: *)(.*)', line)):
if not re.match('( *sage: *)(.*)', line):
raise ValueError(f'line must start with a sage: prompt, got: {line}')

current_tags, line_sans_tags, is_persistent = parse_optional_tags(line.rstrip(), return_string_sans_tags=True)
Expand Down Expand Up @@ -1140,8 +1140,8 @@ def update_tag_counts(optional_tags):

def check_and_clear_tag_counts():
if (num_examples := tag_count_within_block['']) >= 4:
if overused_tags := set(tag for tag, count in tag_count_within_block.items()
if tag and count >= num_examples):
if overused_tags := {tag for tag, count in tag_count_within_block.items()
if tag and count >= num_examples}:
overused_tags.update(persistent_optional_tags)
overused_tags.difference_update(self.file_optional_tags)
suggested = unparse_optional_tags(overused_tags, prefix='sage: # ')
Expand Down Expand Up @@ -1210,10 +1210,10 @@ def check_and_clear_tag_counts():
continue

if self.optional_tags is not True:
extra = set(tag
for tag in optional_tags
if (tag not in self.optional_tags
and tag not in available_software))
extra = {tag
for tag in optional_tags
if (tag not in self.optional_tags
and tag not in available_software)}
if extra:
if any(tag in external_software for tag in extra):
# never probe "external" software
Expand Down
18 changes: 9 additions & 9 deletions src/sage/doctest/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def __init__(self, controller):
sage: DTR = DocTestReporter(DC)
"""
self.controller = controller
self.postscript = dict(lines=[], cputime=0, walltime=0)
self.postscript = {"lines": [], "cputime": 0, "walltime": 0}
self.sources_completed = 0
self.stats = {}
self.error_status = 0
Expand Down Expand Up @@ -408,7 +408,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
ntests, result_dict = results
except (TypeError, ValueError):
ntests = 0
result_dict = DictAsObject(dict(err='badresult'))
result_dict = DictAsObject({"err": 'badresult'})
if timeout:
fail_msg = "Timed out"
if ntests > 0:
Expand All @@ -429,7 +429,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
log(output)
log("*"*70)
postscript['lines'].append(cmd + " # %s" % fail_msg)
stats[basename] = dict(failed=True, walltime=1e6, ntests=ntests)
stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests}
if not the_baseline_stats.get('failed', False):
self.error_status |= 4
elif return_code:
Expand All @@ -445,7 +445,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
log(output)
log("*"*70)
postscript['lines'].append(cmd + " # %s" % fail_msg)
stats[basename] = dict(failed=True, walltime=1e6, ntests=ntests)
stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests}
if not the_baseline_stats.get('failed', False):
self.error_status |= (8 if return_code > 0 else 16)
else:
Expand Down Expand Up @@ -501,9 +501,9 @@ def report(self, source, timeout, return_code, results, output, pid=None):
if hasattr(result_dict, 'tb'):
log(result_dict.tb)
if hasattr(result_dict, 'walltime'):
stats[basename] = dict(failed=True, walltime=wall, ntests=ntests)
stats[basename] = {"failed": True, "walltime": wall, "ntests": ntests}
else:
stats[basename] = dict(failed=True, walltime=1e6, ntests=ntests)
stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests}
self.error_status |= 64
if result_dict.err is None or result_dict.err == 'tab':
f = result_dict.failures
Expand All @@ -515,16 +515,16 @@ def report(self, source, timeout, return_code, results, output, pid=None):
if not the_baseline_stats.get('failed', False):
self.error_status |= 1
if f or result_dict.err == 'tab':
stats[basename] = dict(failed=True, walltime=wall, ntests=ntests)
stats[basename] = {"failed": True, "walltime": wall, "ntests": ntests}
else:
stats[basename] = dict(walltime=wall, ntests=ntests)
stats[basename] = {"walltime": wall, "ntests": ntests}
postscript['cputime'] += cpu
postscript['walltime'] += wall

try:
optionals = result_dict.optionals
except AttributeError:
optionals = dict()
optionals = {}
for tag in sorted(optionals):
nskipped = optionals[tag]
if tag == "long time":
Expand Down
19 changes: 11 additions & 8 deletions src/sage/doctest/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def _process_doc(self, doctests, doc, namespace, start):
new_doctests = self.parse_docstring(docstring, namespace, start)
sig_on_count_doc_doctest = "sig_on_count() # check sig_on/off pairings (virtual doctest)\n"
for dt in new_doctests:
if len(dt.examples) > 0 and not (hasattr(dt.examples[-1],'sage_source')
if len(dt.examples) > 0 and not (hasattr(dt.examples[-1], 'sage_source')
and dt.examples[-1].sage_source == sig_on_count_doc_doctest):
# Line number refers to the end of the docstring
sigon = doctest.Example(sig_on_count_doc_doctest, "0\n", lineno=docstring.count("\n"))
Expand Down Expand Up @@ -305,7 +305,7 @@ def _create_doctests(self, namespace, tab_okay=None):
False
"""
if tab_okay is None:
tab_okay = isinstance(self,TexSource)
tab_okay = isinstance(self, TexSource)
self._init()
self.line_shift = 0
self.parser = SageDocTestParser(self.options.optional,
Expand Down Expand Up @@ -371,9 +371,9 @@ def _create_doctests(self, namespace, tab_okay=None):
if unparsed_doc:
self._process_doc(doctests, doc, namespace, start)

extras = dict(tab=not tab_okay and tab_locations,
line_number=contains_line_number,
optionals=self.parser.optionals)
extras = {"tab": not tab_okay and tab_locations,
"line_number": contains_line_number,
"optionals": self.parser.optionals}
if self.options.randorder is not None and self.options.randorder is not False:
# we want to randomize even when self.randorder = 0
random.seed(self.options.randorder)
Expand Down Expand Up @@ -569,13 +569,13 @@ def __init__(self, path, options):
base, ext = os.path.splitext(path)
valid_code_ext = ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx')
if ext in valid_code_ext:
self.__class__ = dynamic_class('PythonFileSource',(FileDocTestSource,PythonSource))
self.__class__ = dynamic_class('PythonFileSource', (FileDocTestSource, PythonSource))
self.encoding = "utf-8"
elif ext == '.tex':
self.__class__ = dynamic_class('TexFileSource',(FileDocTestSource,TexSource))
self.__class__ = dynamic_class('TexFileSource', (FileDocTestSource, TexSource))
self.encoding = "utf-8"
elif ext == '.rst' or ext == '.rst.txt':
self.__class__ = dynamic_class('RestFileSource',(FileDocTestSource,RestSource))
self.__class__ = dynamic_class('RestFileSource', (FileDocTestSource, RestSource))
self.encoding = "utf-8"
else:
valid_ext = ", ".join(valid_code_ext + ('.tex', '.rst', '.rst.txt'))
Expand Down Expand Up @@ -955,6 +955,7 @@ def parse_docstring(self, docstring, namespace, start):
return [self.parser.get_doctest(docstring, namespace, str(self.qualified_name),
self.printpath, start + 1)]


class PythonSource(SourceLanguage):
"""
This class defines the functions needed for the extraction of doctests from python sources.
Expand Down Expand Up @@ -1252,6 +1253,7 @@ def _neutralize_doctests(self, reindent):
neutralized.append(" "*reindent + line)
return "".join(neutralized)


class TexSource(SourceLanguage):
"""
This class defines the functions needed for the extraction of
Expand Down Expand Up @@ -1628,6 +1630,7 @@ def parse_docstring(self, docstring, namespace, start):
self.printpath, start + 1)
return [outer_doctest] + inner_doctests


class DictAsObject(dict):
"""
A simple subclass of dict that inserts the items from the initializing dictionary into attributes.
Expand Down
6 changes: 3 additions & 3 deletions src/sage/doctest/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def dict_difference(self, other):
sage: dict_difference(D2.__dict__, D1.__dict__)
{'foobar': 'hello', 'timeout': 100}
"""
D = dict()
D = {}
for k, v in self.items():
try:
if other[k] == v:
Expand Down Expand Up @@ -275,8 +275,8 @@ def start(self):
sage: D.start(); D.set
set()
"""
self.set = set([])
self.got = set([])
self.set = set()
self.got = set()

def __getitem__(self, name):
"""
Expand Down

0 comments on commit 29e95c5

Please sign in to comment.