Skip to content

Commit 2af7ffa

Browse files
authored
Merge pull request #141 from ev-br/alt_checker
ENH: make OutputChecker pluggable
2 parents 4e33c90 + af3d5ae commit 2af7ffa

File tree

4 files changed

+69
-9
lines changed

4 files changed

+69
-9
lines changed

scpdt/impl.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class DTConfig:
8585
a string. If not empty, the string value is used as the skip reason.
8686
"""
8787
def __init__(self, *, # DTChecker configuration
88+
CheckerKlass=None,
8889
default_namespace=None,
8990
check_namespace=None,
9091
rndm_markers=None,
@@ -108,6 +109,8 @@ def __init__(self, *, # DTChecker configuration
108109
pytest_extra_xfail=None,
109110
):
110111
### DTChecker configuration ###
112+
self.CheckerKlass = CheckerKlass or DTChecker
113+
111114
# The namespace to run examples in
112115
self.default_namespace = default_namespace or {}
113116

@@ -340,7 +343,7 @@ def __init__(self, checker=None, verbose=None, optionflags=None, config=None):
340343
if config is None:
341344
config = DTConfig()
342345
if checker is None:
343-
checker = DTChecker(config)
346+
checker = config.CheckerKlass(config)
344347
self.nameerror_after_exception = config.nameerror_after_exception
345348
if optionflags is None:
346349
optionflags = config.optionflags

scpdt/plugin.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ def collect(self):
228228
runner = _get_runner(self.config,
229229
verbose=False,
230230
optionflags=optionflags,
231-
checker=DTChecker(config=self.config.dt_config)
232231
)
233232

234233
# strategy='api': discover doctests in public, non-deprecated objects in module
@@ -243,7 +242,7 @@ def collect(self):
243242
yield pydoctest.DoctestItem.from_parent(
244243
self, name=test.name, runner=runner, dtest=test
245244
)
246-
245+
247246

248247
class DTTextfile(DoctestTextfile):
249248
"""
@@ -268,7 +267,6 @@ def collect(self):
268267
runner = _get_runner(self.config,
269268
verbose=False,
270269
optionflags=optionflags,
271-
checker=DTChecker(config=self.config.dt_config)
272270
)
273271

274272
# Plug in an instance of `DTParser` which parses the doctest examples from the text file and
@@ -283,7 +281,7 @@ def collect(self):
283281
)
284282

285283

286-
def _get_runner(config, checker, verbose, optionflags):
284+
def _get_runner(config, verbose, optionflags):
287285
"""
288286
Override function to return an instance of PytestDTRunner.
289287
@@ -336,5 +334,5 @@ def report_unexpected_exception(self, out, test, example, exc_info):
336334
out.append(failure)
337335
else:
338336
raise failure
339-
340-
return PytestDTRunner(checker=checker, verbose=verbose, optionflags=optionflags, config=config.dt_config)
337+
338+
return PytestDTRunner(verbose=verbose, optionflags=optionflags, config=config.dt_config)

scpdt/tests/test_pytest_configuration.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,38 @@ def test_local_file_cases(pytester):
5757
python_file = Path(path_str)
5858
result = pytester.inline_run(python_file, "--doctest-modules")
5959
assert result.ret == pytest.ExitCode.OK
60+
61+
62+
def test_alt_checker(pytester):
63+
"""Test an alternative Checker."""
64+
65+
# create a temporary conftest.py file
66+
pytester.makeconftest(
67+
"""
68+
import doctest
69+
from scpdt.conftest import dt_config
70+
71+
class Vanilla(doctest.OutputChecker):
72+
def __init__(self, config):
73+
pass
74+
75+
dt_config.CheckerKlass = Vanilla
76+
"""
77+
)
78+
79+
# create a temporary pytest test file
80+
f = pytester.makepyfile(
81+
"""
82+
def func():
83+
'''
84+
>>> 2 / 3 # fails with vanilla doctest.OutputChecker
85+
0.667
86+
'''
87+
pass
88+
"""
89+
)
90+
91+
# run all tests with pytest
92+
result = pytester.inline_run(f, '--doctest-modules')
93+
assert result.ret == pytest.ExitCode.TESTS_FAILED
94+

scpdt/tests/test_runner.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import pytest
66

7-
from . import failure_cases as module, finder_cases as finder_module
8-
from .. import DTFinder, DTRunner, DebugDTRunner
7+
from . import (failure_cases as module,
8+
finder_cases as finder_module,
9+
module_cases)
10+
from .. import DTFinder, DTRunner, DebugDTRunner, DTConfig
911

1012

1113
### Smoke test DTRunner methods. Mainly to check that they are runnable.
@@ -79,3 +81,25 @@ def test_debug_runner_exception(self):
7981
# exception carries the original test
8082
assert orig_exception.test is tests[0]
8183

84+
85+
class VanillaOutputChecker(doctest.OutputChecker):
86+
"""doctest.OutputChecker to drop in for DTChecker.
87+
88+
LSP break: OutputChecker does not have __init__,
89+
here we add it to agree with DTChecker.
90+
"""
91+
def __init__(self, config):
92+
pass
93+
94+
class TestCheckerDropIn:
95+
"""Test DTChecker and vanilla doctest OutputChecker being drop-in replacements.
96+
"""
97+
def test_vanilla_checker(self):
98+
config = DTConfig(CheckerKlass=VanillaOutputChecker)
99+
runner = DebugDTRunner(config=config)
100+
tests = DTFinder().find(module_cases.func)
101+
102+
with pytest.raises(doctest.DocTestFailure) as exc:
103+
for t in tests:
104+
runner.run(t)
105+

0 commit comments

Comments
 (0)