Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: adding doctest-remote-data-all directive #281

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
doctests in narrative documentations based on availability of
dependencies. [#280]

- Adding new directive ``doctest-remote-data-all`` to conditionally skip all
doctests in narrative documentations based on availability of
remote-data. [#281]

- Versions of Python <3.9 are no longer supported. [#274]

1.3.0 (2024-11-25)
Expand Down
20 changes: 16 additions & 4 deletions pytest_doctestplus/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ class DocTestParserPlus(doctest.DocTestParser):

- ``.. doctest-remote-data::``: Skip the next doctest chunk if
--remote-data is not passed.

- ``.. doctest-remote-data-all::``: Skip all subsequent doctest
chunks if --remote-data is not passed.
"""

def parse(self, s, name=None):
Expand Down Expand Up @@ -429,15 +432,24 @@ def parse(self, s, name=None):
required_all = []
skip_next = False
lines = entry.strip().splitlines()

requires_all_match = [re.match(
fr'{comment_char}\s+doctest-requires-all\s*::\s+(.*)', x) for x in lines]
if any(requires_all_match):
required_all = [re.split(r'\s*[,\s]\s*', match.group(1)) for match in requires_all_match if match][0]

required_all = [re.split(r'\s*[,\s]\s*', match.group(1))
for match in requires_all_match if match][0]
required_modules_all = DocTestFinderPlus.check_required_modules(required_all)
if not required_modules_all:
skip_all = True
continue

if config.getoption('remote_data', 'none') != 'any':
if any(re.match(fr'{comment_char}\s+doctest-remote-data-all\s*::', x.strip())
for x in lines):
skip_all = True
continue

if any(re.match(
f'{comment_char} doctest-skip-all', x.strip()) for x in lines) or not required_modules_all:
if any(re.match(f'{comment_char} doctest-skip-all', x.strip()) for x in lines):
skip_all = True
continue

Expand Down
1 change: 1 addition & 0 deletions pytest_doctestplus/sphinx/doctestplus.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def setup(app):
app.add_directive('doctest-skip-all', DoctestSkipDirective)
app.add_directive('doctest', DoctestSkipDirective, override=True)
app.add_directive('doctest-remote-data', DoctestSkipDirective)
app.add_directive('doctest-remote-data-all', DoctestSkipDirective)
# Code blocks that use this directive will not appear in the generated
# documentation. This is intended to hide boilerplate code that is only
# useful for testing documentation using doctest, but does not actually
Expand Down
17 changes: 17 additions & 0 deletions tests/docs/skip_some_remote_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,20 @@ This should be skipped otherwise the test should fail::
3
>>> import warnings
>>> warnings.warn('A warning occurred', UserWarning) # doctest: +IGNORE_WARNINGS


Remote data all followed by code cell
=====================================

This codeblock should fail, but is skipped:

.. doctest-remote-data-all::

>>> 1 + 1
3

The this following block should be executed with the simple remote data, but
should be skipped with the all version.

>>> 1 + 2
5
35 changes: 35 additions & 0 deletions tests/test_doctestplus.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,41 @@ def test_doctest_skip(testdir):
testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1)


def test_remote_data_all(testdir):
testdir.makeini(
"""
[pytest]
doctestplus = enabled
""")

p = testdir.makefile(
'.rst',
"""
This is a narrative docs, which some of the lines requiring remote-data access.
The first code block always passes, the second is skipped without remote data and the
last section is skipped due to the all option.

>>> print("Test")
Test

.. doctest-remote-data-all::

>>> from contextlib import closing
>>> from urllib.request import urlopen
>>> with closing(urlopen('https://www.astropy.org')) as remote:
... remote.read() # doctest: +IGNORE_OUTPUT

Narrative before a codeblock that should fail normally but with the all option in the
directive it is skipped over thus producing a passing status.

>>> print(123)
"""
)

testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(failed=1)
testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(passed=1)


# We repeat all testst including remote data with and without it opted in
def test_remote_data_url(testdir):
testdir.makeini(
Expand Down
Loading