Skip to content

Commit 3188e5b

Browse files
committed
Ensure skipped doctest are visible
when they are skipped due to `__doctest_skip__` or `__doctest_requires__`.
1 parent 5af936a commit 3188e5b

File tree

2 files changed

+75
-7
lines changed

2 files changed

+75
-7
lines changed

pytest_doctestplus/plugin.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -879,14 +879,17 @@ def find(self, obj, name=None, module=None, globs=None, extraglobs=None):
879879

880880
if hasattr(obj, '__doctest_skip__') or hasattr(obj, '__doctest_requires__'):
881881

882-
def test_filter(test):
882+
def conditionally_insert_skip(test):
883+
"""
884+
Insert skip statement if `test` matches `__doctest_(skip|requires)__`.
885+
"""
883886
for pat in getattr(obj, '__doctest_skip__', []):
884887
if pat == '*':
885-
return False
888+
self._prepend_skip(test)
886889
elif pat == '.' and test.name == name:
887-
return False
890+
self._prepend_skip(test)
888891
elif fnmatch.fnmatch(test.name, '.'.join((name, pat))):
889-
return False
892+
self._prepend_skip(test)
890893

891894
reqs = getattr(obj, '__doctest_requires__', {})
892895
for pats, mods in reqs.items():
@@ -903,14 +906,38 @@ def test_filter(test):
903906
else:
904907
continue # The pattern does not apply
905908

906-
if not self.check_required_modules(mods):
907-
return False
909+
for mod in mods:
910+
self._prepend_importorskip(test, module=mod)
908911
return True
909912

910-
tests = list(filter(test_filter, tests))
913+
for _test in tests:
914+
conditionally_insert_skip(_test)
911915

912916
return tests
913917

918+
def _prepend_skip(self, test):
919+
"""Prepends `pytest.skip` before the doctest."""
920+
source = (
921+
"import pytest; "
922+
"pytest.skip('listed in `__doctest_skip__`'); "
923+
# Don't impact what's available in the namespace
924+
"del pytest"
925+
)
926+
importorskip = doctest.Example(source=source, want="")
927+
test.examples.insert(0, importorskip)
928+
929+
def _prepend_importorskip(self, test, *, module):
930+
"""Prepends `pytest.importorskip` before the doctest."""
931+
source = (
932+
"import pytest; "
933+
# Hide output of this statement in `_`, otherwise doctests fail
934+
f"_ = pytest.importorskip({module!r}); "
935+
# Don't impact what's available in the namespace
936+
"del pytest"
937+
)
938+
importorskip = doctest.Example(source=source, want="")
939+
test.examples.insert(0, importorskip)
940+
914941

915942
def write_modified_file(fname, new_fname, changes, encoding=None):
916943
# Sort in reversed order to edit the lines:

tests/test_doctestplus.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,3 +1542,44 @@ def f():
15421542

15431543
original_fixed = original.replace("1\n 2", "\n ".join(["0", "1", "2", "3"]))
15441544
assert result == original_fixed
1545+
1546+
1547+
def test_skip_module_variable(testdir):
1548+
p = testdir.makepyfile("""
1549+
__doctest_skip__ = ["f"]
1550+
1551+
def f():
1552+
'''
1553+
>>> 1 + 2
1554+
5
1555+
'''
1556+
pass
1557+
1558+
def g():
1559+
'''
1560+
>>> 1 + 1
1561+
2
1562+
'''
1563+
pass
1564+
""")
1565+
testdir.inline_run(p, '--doctest-plus').assertoutcome(passed=1, skipped=1)
1566+
1567+
1568+
def test_requires_module_variable(testdir):
1569+
p = testdir.makepyfile("""
1570+
__doctest_requires__ = {("f",): ["module_that_is_not_availabe"]}
1571+
1572+
def f():
1573+
'''
1574+
>>> import module_that_is_not_availabe
1575+
'''
1576+
pass
1577+
1578+
def g():
1579+
'''
1580+
>>> 1 + 1
1581+
2
1582+
'''
1583+
pass
1584+
""")
1585+
testdir.inline_run(p, '--doctest-plus').assertoutcome(passed=1, skipped=1)

0 commit comments

Comments
 (0)