diff --git a/CHANGES.rst b/CHANGES.rst index ba905a0..9997379 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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) diff --git a/pytest_doctestplus/plugin.py b/pytest_doctestplus/plugin.py index d323c12..6487da7 100644 --- a/pytest_doctestplus/plugin.py +++ b/pytest_doctestplus/plugin.py @@ -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): @@ -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 diff --git a/pytest_doctestplus/sphinx/doctestplus.py b/pytest_doctestplus/sphinx/doctestplus.py index f370005..7600caa 100644 --- a/pytest_doctestplus/sphinx/doctestplus.py +++ b/pytest_doctestplus/sphinx/doctestplus.py @@ -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 diff --git a/tests/docs/skip_some_remote_data.rst b/tests/docs/skip_some_remote_data.rst index 74c2ac2..c5e7399 100644 --- a/tests/docs/skip_some_remote_data.rst +++ b/tests/docs/skip_some_remote_data.rst @@ -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 diff --git a/tests/test_doctestplus.py b/tests/test_doctestplus.py index e2a3abd..63a5572 100644 --- a/tests/test_doctestplus.py +++ b/tests/test_doctestplus.py @@ -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(