From 2a1cc26313f266f5bc65a939f07497481f11d15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Tue, 21 Jan 2025 14:55:14 -0800 Subject: [PATCH 1/4] ENH: adding doctest-requires-all directive --- pytest_doctestplus/plugin.py | 15 ++++++++++++++- pytest_doctestplus/sphinx/doctestplus.py | 1 + tests/test_doctestplus.py | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pytest_doctestplus/plugin.py b/pytest_doctestplus/plugin.py index 3f8e6da..40307e3 100644 --- a/pytest_doctestplus/plugin.py +++ b/pytest_doctestplus/plugin.py @@ -389,6 +389,10 @@ class DocTestParserPlus(doctest.DocTestParser): doctest chunk if the given modules/packages are not installed. + - ``.. doctest-requires-all:: module1, module2``: Skip all subsequent + doctest chunks if the given modules/packages are not + installed. + - ``.. doctest-skip-all``: Skip all subsequent doctests. - ``.. doctest-remote-data::``: Skip the next doctest chunk if @@ -422,10 +426,19 @@ def parse(self, s, name=None): if isinstance(entry, str) and entry: required = [] + 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): + print(requires_all_match) + 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 any(re.match( - f'{comment_char} doctest-skip-all', x.strip()) for x in lines): + f'{comment_char} doctest-skip-all', x.strip()) for x in lines) or not required_modules_all: skip_all = True continue diff --git a/pytest_doctestplus/sphinx/doctestplus.py b/pytest_doctestplus/sphinx/doctestplus.py index b9b24a2..f370005 100644 --- a/pytest_doctestplus/sphinx/doctestplus.py +++ b/pytest_doctestplus/sphinx/doctestplus.py @@ -43,6 +43,7 @@ class DoctestRequiresDirective(DoctestSkipDirective): def setup(app): app.add_directive('doctest-requires', DoctestRequiresDirective) + app.add_directive('doctest-requires-all', DoctestRequiresDirective) app.add_directive('doctest-skip', DoctestSkipDirective) app.add_directive('doctest-skip-all', DoctestSkipDirective) app.add_directive('doctest', DoctestSkipDirective, override=True) diff --git a/tests/test_doctestplus.py b/tests/test_doctestplus.py index 6b54786..e2a3abd 100644 --- a/tests/test_doctestplus.py +++ b/tests/test_doctestplus.py @@ -435,6 +435,29 @@ def test_requires(testdir): testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) +def test_requires_all(testdir): + testdir.makeini( + """ + [pytest] + doctestplus = enabled + """) + + # should be ignored + p = testdir.makefile( + '.rst', + """ + .. doctest-requires-all:: foobar + + >>> import foobar + + This is a narrative line, before another doctest snippet + + >>> import foobar + """ + ) + testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) + + def test_ignore_warnings_module(testdir): # First check that we get a warning if we don't add the IGNORE_WARNINGS From 0ac57e819d6b7f84d8acc311c904a65dbc6c864f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 23 Jan 2025 11:15:49 -0800 Subject: [PATCH 2/4] DOC: adding doctest-requires-all to the docs --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 739c22b..fafe755 100644 --- a/README.rst +++ b/README.rst @@ -336,6 +336,11 @@ the package's ``setup.cfg`` file. The syntax for this option is a list of Multiple requirements can be specified if separated by semicolons. + +It is also possible to conditionally skip all the doctests in a narrative +documentation with ``doctest-requires-all``. + + Remote Data ~~~~~~~~~~~ From 44407a6742659171acaeeb27f0ad5b03645552b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 23 Jan 2025 11:17:22 -0800 Subject: [PATCH 3/4] DOC: adding changelog --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 095e28f..ba905a0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ - Fixing compatibility with pytest-asyncio. [#278] +- Adding new directive ``doctest-requires-all`` to conditionally skip all + doctests in narrative documentations based on availability of + dependencies. [#280] + - Versions of Python <3.9 are no longer supported. [#274] 1.3.0 (2024-11-25) From 74cebb887d704e2bf7964127a429b62cdcdf71d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Thu, 23 Jan 2025 13:54:55 -0800 Subject: [PATCH 4/4] Cleanup from review Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com> --- README.rst | 2 -- pytest_doctestplus/plugin.py | 1 - 2 files changed, 3 deletions(-) diff --git a/README.rst b/README.rst index fafe755..48b26f6 100644 --- a/README.rst +++ b/README.rst @@ -336,11 +336,9 @@ the package's ``setup.cfg`` file. The syntax for this option is a list of Multiple requirements can be specified if separated by semicolons. - It is also possible to conditionally skip all the doctests in a narrative documentation with ``doctest-requires-all``. - Remote Data ~~~~~~~~~~~ diff --git a/pytest_doctestplus/plugin.py b/pytest_doctestplus/plugin.py index 40307e3..d323c12 100644 --- a/pytest_doctestplus/plugin.py +++ b/pytest_doctestplus/plugin.py @@ -432,7 +432,6 @@ def parse(self, s, name=None): requires_all_match = [re.match( fr'{comment_char}\s+doctest-requires-all\s*::\s+(.*)', x) for x in lines] if any(requires_all_match): - print(requires_all_match) 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)