Skip to content

Commit

Permalink
Trac #30778: sage.doctest.control: Exclude doctests in files via file…
Browse files Browse the repository at this point in the history
… directives ''# sage.doctest: optional - xyz'

When a file is marked `# sage.doctest: optional - xyz`, we omit it from
doctesting unless `--optional=xyz` is given.

This will save us from having to add lots of `# optional - ...` tags to
files in the course of modularization (#29705)

We do this by extending `sage.doctest.control.skipfile`, which already
parses files for `# nodoctest` file directives.

Previous related proposals/discussions: #3260, #20427

Also related: #30746

URL: https://trac.sagemath.org/30778
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe, John Palmieri
Reviewer(s): John Palmieri, Matthias Koeppe
  • Loading branch information
Release Manager committed Oct 10, 2021
2 parents 38556c2 + 5cc1288 commit f96cbf7
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 85 deletions.
20 changes: 20 additions & 0 deletions src/doc/en/developer/coding_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,26 @@ framework. Here is a comprehensive list:

sage: SloaneEncyclopedia[60843] # optional - sloane_database

.. NOTE::

If one of the first 10 lines of a file starts with any of
``r""" sage.doctest: optional - keyword``
(or ``""" sage.doctest: optional - keyword``
or ``# sage.doctest: optional - keyword``
or ``% sage.doctest: optional - keyword``
or ``.. sage.doctest: optional - keyword``,
or any of these with different spacing),
then that file will be skipped unless
the ``--optional=keyword`` flag is passed to ``sage -t``.

This does not apply to files which are explicitly given
as command line arguments: those are always tested.

If you add such a line to a file, you are strongly encouraged
to add a note to the module-level documentation, saying that
the doctests in this file will be skipped unless the
appropriate conditions are met.

- **internet:** For lines that require an internet connection::

sage: oeis(60843) # optional - internet
Expand Down
40 changes: 38 additions & 2 deletions src/sage/doctest/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@
from .reporting import DocTestReporter
from .util import Timer, count_noun, dict_difference
from .external import external_software, available_software
from .parsing import parse_optional_tags

nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest')
optionaltag_regex = re.compile(r'^\w+$')
optionalfiledirective_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)')

# Optional tags which are always automatically added

Expand Down Expand Up @@ -204,11 +206,23 @@ def skipdir(dirname):
return True
return False

def skipfile(filename):
def skipfile(filename, tested_optional_tags=False):
"""
Return True if and only if the file ``filename`` should not be
doctested.
INPUT:
- ``filename`` - name of a file
- ``tested_optional_tags`` - a list or tuple or set of optional tags to test,
or ``False`` (no optional test) or ``True`` (all optional tests)
If ``filename`` contains a line of the form ``"# sage.doctest:
optional - xyz")``, then this will return ``False`` if "xyz" is in
``tested_optional_tags``. Otherwise, it returns the matching tag
("optional - xyz").
EXAMPLES::
sage: from sage.doctest.control import skipfile
Expand All @@ -221,6 +235,18 @@ def skipfile(filename):
....: _ = f.write("# nodoctest")
sage: skipfile(filename)
True
sage: with open(filename, "w") as f:
....: _ = f.write("# sage.doctest: optional - xyz")
sage: skipfile(filename, False)
'optional - xyz'
sage: bool(skipfile(filename, False))
True
sage: skipfile(filename, ['abc'])
'optional - xyz'
sage: skipfile(filename, ['abc', 'xyz'])
False
sage: skipfile(filename, True)
False
"""
base, ext = os.path.splitext(filename)
if ext not in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst', '.tex'):
Expand All @@ -230,6 +256,16 @@ def skipfile(filename):
for line in F:
if nodoctest_regex.match(line):
return True
if tested_optional_tags is not True:
# Adapted from code in SageDocTestParser.parse
m = optionalfiledirective_regex.match(line)
if m:
if tested_optional_tags is False:
return m.group(2)
optional_tags = parse_optional_tags('#' + m.group(2))
extra = optional_tags - set(tested_optional_tags)
if extra:
return m.group(2)
line_count += 1
if line_count >= 10:
break
Expand Down Expand Up @@ -786,7 +822,7 @@ def expand():
if dir[0] == "." or skipdir(os.path.join(root,dir)):
dirs.remove(dir)
for file in files:
if not skipfile(os.path.join(root,file)):
if not skipfile(os.path.join(root, file), self.options.optional):
yield os.path.join(root, file)
else:
# the user input this file explicitly, so we don't skip it
Expand Down
Loading

0 comments on commit f96cbf7

Please sign in to comment.