Skip to content

Commit b890535

Browse files
committed
ENH: allow per-doctest requirements in pytest_extra_requires
1 parent 792ef57 commit b890535

File tree

3 files changed

+26
-14
lines changed

3 files changed

+26
-14
lines changed

scipy_doctest/impl.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ class DTConfig:
9393
Each key is a doctest name to skip, and the corresponding value is
9494
a string. If not empty, the string value is used as the skip reason.
9595
pytest_extra_requires : dict
96-
Paths to conditionally ignore unless requirements are met.
97-
The format is ``{path/or/glob/pattern: requirement}``, where the values are
98-
PEP 508 dependency specifiers. If a requirement is not met, the behavior
99-
is equivalent to using the ``--ignore=...`` command line switch.
96+
Paths or functions to conditionally ignore unless requirements are met.
97+
The format is ``{path/or/glob/pattern: requirement, full.func.name: requiremet}``,
98+
where the values are PEP 508 dependency specifiers. If a requirement is not met,
99+
the behavior is equivalent to using the ``--ignore=...`` command line switch for
100+
paths, and to using a `pytest_extra_skip` for function names.
100101
CheckerKlass : object, optional
101102
The class for the Checker object. Must mimic the ``DTChecker`` API:
102103
subclass the `doctest.OutputChecker` and make the constructor signature

scipy_doctest/plugin.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
import warnings
66
import doctest
77

8-
from importlib.metadata import version as get_version, PackageNotFoundError
9-
from packaging.requirements import Requirement
10-
118
import pytest
129
import _pytest
1310
from _pytest import doctest as pydoctest, outcomes
@@ -16,7 +13,7 @@
1613

1714
from .impl import DTParser, DebugDTRunner
1815
from .conftest import dt_config
19-
from .util import np_errstate, matplotlib_make_nongui, temp_cwd
16+
from .util import np_errstate, matplotlib_make_nongui, temp_cwd, is_req_satisfied
2017
from .frontend import find_doctests
2118

2219

@@ -93,12 +90,7 @@ def pytest_ignore_collect(collection_path, config):
9390

9491
for entry, req_str in config.dt_config.pytest_extra_requires.items():
9592
if fnmatch_ex(entry, collection_path):
96-
# check the requirement
97-
req = Requirement(req_str)
98-
try:
99-
return not (get_version(req.name) in req.specifier)
100-
except PackageNotFoundError:
101-
return True
93+
return not is_req_satisfied(req_str)
10294

10395

10496
def is_private(item):
@@ -139,6 +131,13 @@ def _maybe_add_markers(item, config):
139131
pytest.mark.xfail(reason=reason)
140132
)
141133

134+
extra_requires = dt_config.pytest_extra_requires
135+
if req_str := extra_requires.get(item.name, None):
136+
if not is_req_satisfied(req_str):
137+
item.add_marker(
138+
pytest.mark.skip(reason=f"requires {req_str}")
139+
)
140+
142141

143142
def pytest_collection_modifyitems(config, items):
144143
"""

scipy_doctest/util.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import inspect
1111
from contextlib import contextmanager
1212

13+
from importlib.metadata import version as get_version, PackageNotFoundError
14+
from packaging.requirements import Requirement
1315

1416
@contextmanager
1517
def matplotlib_make_nongui():
@@ -255,6 +257,16 @@ def get_public_objects(module, skiplist=None):
255257
return (items, names), failures
256258

257259

260+
def is_req_satisfied(req_str):
261+
""" Check if a PEP 508-compliant requirement is satisfied or not.
262+
"""
263+
req = Requirement(req_str)
264+
try:
265+
return get_version(req.name) in req.specifier
266+
except PackageNotFoundError:
267+
return False
268+
269+
258270
# XXX: not used ATM
259271
modules = []
260272
def generate_log(module, test):

0 commit comments

Comments
 (0)