Skip to content

Commit 9d7bc5b

Browse files
ev-brSheila-nk
andauthored
ENH: make collection strategy user configurable in the pytest layer (#128)
* ENH: make collection strategy user configurable in the pytest layer The default is the vanilla doctest collection, strategy=None. To select strategy='api', use the command flag $ pytest --doctest-modules --doctest-collect=api When to use what. strategy='api' is meant for packages with non-trivial internal structure and where you only enfore doctests correctness for public objects. For individual single-file modules, you probably want strategy=None. * DOC: update README.md Co-authored-by: Sheila <[email protected]> --------- Co-authored-by: Sheila <[email protected]>
1 parent 365d72d commit 9d7bc5b

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,18 @@ An in-depth explanation is given in the [tailoring your doctesting experience](h
207207
Once the plugin is registered, you can run your doctests by executing the following command:
208208

209209
```bash
210-
python -m pytest --doctest-modules
210+
$ python -m pytest --doctest-modules
211211
```
212212
or
213213
```bash
214-
pytest --pyargs <your-package> --doctest-modules
214+
$ pytest --pyargs <your-package> --doctest-modules
215+
```
216+
217+
By default, all doctests are collected. To only collect public objects, `strategy="api"`,
218+
use the command flag
219+
220+
```bash
221+
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
215222
```
216223

217224
### Tailoring Your Doctesting Experience

scpdt/plugin.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717
from scpdt.frontend import find_doctests
1818

1919

20+
def pytest_addoption(parser):
21+
group = parser.getgroup("collect")
22+
23+
group.addoption(
24+
"--doctest-collect",
25+
action="store",
26+
default="None",
27+
help="Doctest collection strategy: vanilla pytest ('None', default), or 'api'",
28+
choices=("None", "api"),
29+
dest="collection_strategy"
30+
)
31+
32+
2033
def pytest_configure(config):
2134
"""
2235
Perform initial configuration for the pytest plugin.
@@ -58,7 +71,9 @@ def pytest_collection_modifyitems(config, items):
5871
# pytest-y runs in between DTModule.collect and this hook (should that something
5972
# be the proper home for all collection?)
6073

61-
if config.getoption("--doctest-modules"):
74+
need_filter_unique = config.getvalue("collection_strategy") == 'api'
75+
76+
if config.getoption("--doctest-modules") and need_filter_unique:
6277
unique_items = []
6378

6479
for item in items:
@@ -79,6 +94,11 @@ def pytest_collection_modifyitems(config, items):
7994
# scipy.stats.distributions is not
8095
extra_skips = config.dt_config.pytest_extra_skips
8196

97+
# NB: The below looks at the name of a test module/object. A seemingly less
98+
# hacky alternative is to populate a set of seen `item.dtest` attributes
99+
# (which are actual DocTest objects). The issue with that is it's tricky
100+
# for skips. Do we skip linalg.det or linalg._basic.det? (collection order
101+
# is not guaranteed)
82102
parent_full_name = item.parent.module.__name__
83103
is_public = "._" not in parent_full_name
84104
is_duplicate = parent_full_name in extra_skips or item.name in extra_skips
@@ -131,10 +151,15 @@ def collect(self):
131151
checker=DTChecker(config=self.config.dt_config)
132152
)
133153

154+
# strategy='api': discover doctests in public, non-deprecated objects in module
155+
# strategy=None : use vanilla stdlib doctest discovery
156+
strategy = self.config.getvalue("collection_strategy")
157+
if strategy == 'None':
158+
strategy = None
159+
134160
try:
135-
# We utilize scpdt's `find_doctests` function to discover doctests in public, non-deprecated objects in the module
136161
# NB: additional postprocessing in pytest_collection_modifyitems
137-
for test in find_doctests(module, strategy="api", name=module.__name__, config=dt_config):
162+
for test in find_doctests(module, strategy=strategy, name=module.__name__, config=dt_config):
138163
if test.examples: # skip empty doctests
139164
yield pydoctest.DoctestItem.from_parent(
140165
self, name=test.name, runner=runner, dtest=test

0 commit comments

Comments
 (0)