scipy-doctest 1.6__py3-none-any.whl → 1.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipy_doctest/__init__.py +1 -2
- scipy_doctest/conftest.py +0 -1
- scipy_doctest/frontend.py +6 -7
- scipy_doctest/impl.py +94 -35
- scipy_doctest/plugin.py +9 -9
- scipy_doctest/tests/failure_cases_2.py +0 -1
- scipy_doctest/tests/finder_cases.py +0 -2
- scipy_doctest/tests/local_file_cases.py +0 -2
- scipy_doctest/tests/module_cases.py +0 -2
- scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst +2 -2
- scipy_doctest/tests/test_finder.py +6 -9
- scipy_doctest/tests/test_parser.py +0 -3
- scipy_doctest/tests/test_pytest_configuration.py +1 -2
- scipy_doctest/tests/test_runner.py +0 -1
- scipy_doctest/tests/test_skipmarkers.py +12 -7
- scipy_doctest/tests/test_testmod.py +1 -1
- scipy_doctest/util.py +2 -3
- {scipy_doctest-1.6.dist-info → scipy_doctest-1.7.1.dist-info}/METADATA +59 -45
- scipy_doctest-1.7.1.dist-info/RECORD +30 -0
- {scipy_doctest-1.6.dist-info → scipy_doctest-1.7.1.dist-info}/WHEEL +1 -1
- scipy_doctest-1.6.dist-info/RECORD +0 -30
- {scipy_doctest-1.6.dist-info → scipy_doctest-1.7.1.dist-info}/entry_points.txt +0 -0
- {scipy_doctest-1.6.dist-info → scipy_doctest-1.7.1.dist-info/licenses}/LICENSE +0 -0
scipy_doctest/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ Configurable, whitespace-insensitive, floating-point-aware doctest helpers.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
__version__ = "1.
|
|
6
|
+
__version__ = "1.7.1"
|
|
7
7
|
|
|
8
8
|
try:
|
|
9
9
|
# register internal modules with pytest; obscure errors galore otherwise
|
|
@@ -19,4 +19,3 @@ except ModuleNotFoundError:
|
|
|
19
19
|
|
|
20
20
|
from .impl import DTChecker, DTFinder, DTParser, DTRunner, DebugDTRunner, DTConfig # noqa
|
|
21
21
|
from .frontend import testmod, testfile, find_doctests, run_docstring_examples # noqa
|
|
22
|
-
|
scipy_doctest/conftest.py
CHANGED
scipy_doctest/frontend.py
CHANGED
|
@@ -46,7 +46,7 @@ def find_doctests(module, strategy=None,
|
|
|
46
46
|
Returns
|
|
47
47
|
-------
|
|
48
48
|
tests : list
|
|
49
|
-
A list of `doctest.DocTest`s that are defined by the module docstring,
|
|
49
|
+
A list of `doctest.DocTest` s that are defined by the module docstring,
|
|
50
50
|
and by its contained objects’ docstrings. The selection is controlled
|
|
51
51
|
by the `strategy` argument.
|
|
52
52
|
|
|
@@ -141,7 +141,7 @@ def testmod(m=None, name=None, globs=None, verbose=None,
|
|
|
141
141
|
In verbose mode, the summary is detailed, else very brief (in fact,
|
|
142
142
|
empty if all tests passed)
|
|
143
143
|
Default is True.
|
|
144
|
-
|
|
144
|
+
verbose : int
|
|
145
145
|
Control the run verbosity:
|
|
146
146
|
0 means only report failures,
|
|
147
147
|
1 means emit object names,
|
|
@@ -175,7 +175,7 @@ def testmod(m=None, name=None, globs=None, verbose=None,
|
|
|
175
175
|
(result, history)
|
|
176
176
|
`result` is a namedtuple ``TestResult(failed, attempted)``
|
|
177
177
|
`history` is a dict with details of which objects were examined (the
|
|
178
|
-
keys are object names and values are individual objects' ``TestResult``s)
|
|
178
|
+
keys are object names and values are individual objects' ``TestResult`` s)
|
|
179
179
|
|
|
180
180
|
Examples
|
|
181
181
|
--------
|
|
@@ -204,7 +204,7 @@ def testmod(m=None, name=None, globs=None, verbose=None,
|
|
|
204
204
|
For complex packages, prefer `strategy='api'`, which works as follows:
|
|
205
205
|
- take the names of public objects from the `__all__` attribute of the package.
|
|
206
206
|
- if `__all__` is not defined, take `dir(module)` and filter out names
|
|
207
|
-
|
|
207
|
+
which start with a leading underscore and dunders.
|
|
208
208
|
- filter out deprecated items, i.e. those which raise `DeprecationWarning`.
|
|
209
209
|
|
|
210
210
|
"""
|
|
@@ -306,7 +306,7 @@ def testfile(filename, module_relative=True, name=None, package=None,
|
|
|
306
306
|
In verbose mode, the summary is detailed, else very brief (in fact,
|
|
307
307
|
empty if all tests passed)
|
|
308
308
|
Default is True.
|
|
309
|
-
|
|
309
|
+
verbose : int
|
|
310
310
|
Control the run verbosity:
|
|
311
311
|
0 means only report failures,
|
|
312
312
|
1 means emit object names,
|
|
@@ -335,7 +335,7 @@ def testfile(filename, module_relative=True, name=None, package=None,
|
|
|
335
335
|
(result, history)
|
|
336
336
|
`result` is a namedtuple ``TestResult(failed, attempted)``
|
|
337
337
|
`history` is a dict with details of which objects were examined (the
|
|
338
|
-
keys are object names and values are individual objects' ``TestResult``s)
|
|
338
|
+
keys are object names and values are individual objects' ``TestResult`` s)
|
|
339
339
|
"""
|
|
340
340
|
# initial configuration
|
|
341
341
|
if config is None:
|
|
@@ -478,4 +478,3 @@ def _main():
|
|
|
478
478
|
if result.failed:
|
|
479
479
|
return 1
|
|
480
480
|
return 0
|
|
481
|
-
|
scipy_doctest/impl.py
CHANGED
|
@@ -4,19 +4,12 @@ import inspect
|
|
|
4
4
|
import doctest
|
|
5
5
|
from doctest import NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL
|
|
6
6
|
from itertools import zip_longest
|
|
7
|
+
from sys import version_info
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
|
|
10
11
|
from . import util
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
## shim numpy 1.x vs 2.0
|
|
14
|
-
if np.__version__ < "2":
|
|
15
|
-
VisibleDeprecationWarning = np.VisibleDeprecationWarning
|
|
16
|
-
else:
|
|
17
|
-
VisibleDeprecationWarning = np.exceptions.VisibleDeprecationWarning
|
|
18
|
-
|
|
19
|
-
|
|
20
13
|
# Register the optionflag to skip whole blocks, i.e.
|
|
21
14
|
# sequences of Examples without an intervening text.
|
|
22
15
|
SKIPBLOCK = doctest.register_optionflag('SKIPBLOCK')
|
|
@@ -68,6 +61,7 @@ class DTConfig:
|
|
|
68
61
|
>>> for test in tests:
|
|
69
62
|
... with user_context(test):
|
|
70
63
|
... runner.run(test)
|
|
64
|
+
|
|
71
65
|
Default is a noop.
|
|
72
66
|
local_resources: dict
|
|
73
67
|
If a test needs some local files, list them here. The format is
|
|
@@ -190,7 +184,8 @@ class DTConfig:
|
|
|
190
184
|
'.bar(', '.title', '.ylabel', '.xlabel', 'set_ylim', 'set_xlim',
|
|
191
185
|
'# reformatted', '.set_xlabel(', '.set_ylabel(', '.set_zlabel(',
|
|
192
186
|
'.set(xlim=', '.set(ylim=', '.set(xlabel=', '.set(ylabel=', '.xlim(',
|
|
193
|
-
'ax.set('
|
|
187
|
+
'ax.set(', '.text(',
|
|
188
|
+
}
|
|
194
189
|
self.stopwords = stopwords
|
|
195
190
|
|
|
196
191
|
if pseudocode is None:
|
|
@@ -224,8 +219,13 @@ class DTConfig:
|
|
|
224
219
|
|
|
225
220
|
|
|
226
221
|
def try_convert_namedtuple(got):
|
|
227
|
-
|
|
228
|
-
|
|
222
|
+
"""
|
|
223
|
+
Converts a string representation of a named tuple into a plain tuple.
|
|
224
|
+
|
|
225
|
+
Suppose that "got" is smth like MoodResult(statistic=10, pvalue=0.1).
|
|
226
|
+
Then convert it to the tuple (10, 0.1), so that can later compare tuples.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
229
|
num = got.count('=')
|
|
230
230
|
if num == 0:
|
|
231
231
|
# not a nameduple, bail out
|
|
@@ -264,6 +264,8 @@ def try_convert_printed_array(got):
|
|
|
264
264
|
|
|
265
265
|
|
|
266
266
|
def has_masked(got):
|
|
267
|
+
"""Check if a given string represents a NumPy masked array.
|
|
268
|
+
"""
|
|
267
269
|
return 'masked_array' in got and '--' in got
|
|
268
270
|
|
|
269
271
|
|
|
@@ -291,6 +293,20 @@ def try_split_shape_from_abbrv(s_got):
|
|
|
291
293
|
|
|
292
294
|
|
|
293
295
|
class DTChecker(doctest.OutputChecker):
|
|
296
|
+
"""
|
|
297
|
+
A drop-in replacement for `doctest.OutputChecker`.
|
|
298
|
+
|
|
299
|
+
Allows robust output comparison for numerical values and special
|
|
300
|
+
cases involving NumPy arrays, masked arrays, namedtuples, and object
|
|
301
|
+
memory addresses. It is configurable via a `DTConfig` object.
|
|
302
|
+
|
|
303
|
+
Parameters:
|
|
304
|
+
-----------
|
|
305
|
+
config : DTConfig, optional
|
|
306
|
+
Configuration object that controls various aspects of output checking.
|
|
307
|
+
If not provided, a default `DTConfig` instance is used.
|
|
308
|
+
|
|
309
|
+
"""
|
|
294
310
|
obj_pattern = re.compile(r'at 0x[0-9a-fA-F]+>')
|
|
295
311
|
vanilla = doctest.OutputChecker()
|
|
296
312
|
|
|
@@ -331,13 +347,8 @@ class DTChecker(doctest.OutputChecker):
|
|
|
331
347
|
# OK then, convert strings to objects
|
|
332
348
|
ns = dict(self.config.check_namespace)
|
|
333
349
|
try:
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
# also array abbreviations: try `np.diag(np.arange(1000))`
|
|
337
|
-
warnings.simplefilter('ignore', VisibleDeprecationWarning)
|
|
338
|
-
|
|
339
|
-
a_want = eval(want, dict(ns))
|
|
340
|
-
a_got = eval(got, dict(ns))
|
|
350
|
+
a_want = eval(want, dict(ns))
|
|
351
|
+
a_got = eval(got, dict(ns))
|
|
341
352
|
except Exception:
|
|
342
353
|
# Maybe we're printing a numpy array? This produces invalid python
|
|
343
354
|
# code: `print(np.arange(3))` produces "[0 1 2]" w/o commas between
|
|
@@ -351,9 +362,9 @@ class DTChecker(doctest.OutputChecker):
|
|
|
351
362
|
s_got = try_convert_printed_array(s_got)
|
|
352
363
|
|
|
353
364
|
return self.check_output(s_want, s_got, optionflags)
|
|
354
|
-
|
|
365
|
+
|
|
355
366
|
#handle array abbreviation for n-dimensional arrays, n >= 1
|
|
356
|
-
ndim_array = (s_want.startswith("array([") and "..." in s_want and
|
|
367
|
+
ndim_array = (s_want.startswith("array([") and "..." in s_want and
|
|
357
368
|
s_got.startswith("array([") and "..." in s_got)
|
|
358
369
|
if ndim_array:
|
|
359
370
|
s_want, want_shape = try_split_shape_from_abbrv(s_want)
|
|
@@ -432,16 +443,31 @@ class DTChecker(doctest.OutputChecker):
|
|
|
432
443
|
except Exception:
|
|
433
444
|
pass
|
|
434
445
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
warnings.simplefilter('ignore', VisibleDeprecationWarning)
|
|
438
|
-
|
|
439
|
-
# This line is the crux of the whole thing. The rest is mostly scaffolding.
|
|
440
|
-
result = np.allclose(want, got, atol=self.atol, rtol=self.rtol, equal_nan=True)
|
|
446
|
+
# This line is the crux of the whole thing. The rest is mostly scaffolding.
|
|
447
|
+
result = np.allclose(want, got, atol=self.atol, rtol=self.rtol, equal_nan=True)
|
|
441
448
|
return result
|
|
442
449
|
|
|
443
450
|
|
|
444
451
|
class DTRunner(doctest.DocTestRunner):
|
|
452
|
+
"""
|
|
453
|
+
A drop-in replacement for `doctest.DocTestRunner`.
|
|
454
|
+
|
|
455
|
+
Improves how test names are reported and allows better control over exception handling.
|
|
456
|
+
It integrates with `DTConfig` to apply customized settings for doctest execution.
|
|
457
|
+
|
|
458
|
+
Parameters:
|
|
459
|
+
-----------
|
|
460
|
+
checker : doctest.OutputChecker, optional
|
|
461
|
+
A custom output checker, defaults to `DTConfig.CheckerKlass(config)`.
|
|
462
|
+
verbose : bool, optional
|
|
463
|
+
If `True`, enables verbose output.
|
|
464
|
+
optionflags : int, optional
|
|
465
|
+
Bitwise OR of `doctest` option flags; defaults to `DTConfig.optionflags`.
|
|
466
|
+
config : DTConfig, optional
|
|
467
|
+
A configuration object controlling doctest behavior; a default instance is used if not provided.
|
|
468
|
+
|
|
469
|
+
"""
|
|
470
|
+
|
|
445
471
|
DIVIDER = "\n"
|
|
446
472
|
|
|
447
473
|
def __init__(self, checker=None, verbose=None, optionflags=None, config=None):
|
|
@@ -489,15 +515,20 @@ class DTRunner(doctest.DocTestRunner):
|
|
|
489
515
|
def get_history(self):
|
|
490
516
|
"""Return a dict with names of items which were run.
|
|
491
517
|
|
|
492
|
-
Actually the dict is `{name : (
|
|
493
|
-
|
|
494
|
-
|
|
518
|
+
Actually the dict is `{name : (failures, tries, skips)}` (before Python
|
|
519
|
+
3.13, just `{name : (failures, tries, skips)}`) where `name` is the
|
|
520
|
+
name of an object, and the value is a tuple of the numbers of examples
|
|
521
|
+
which failed, were tried, and were skipped, respectively.
|
|
495
522
|
"""
|
|
496
|
-
|
|
523
|
+
if version_info >= (3, 13):
|
|
524
|
+
return self._stats
|
|
525
|
+
else:
|
|
526
|
+
return self._name2ft
|
|
497
527
|
|
|
498
528
|
|
|
499
529
|
class DebugDTRunner(DTRunner):
|
|
500
|
-
"""
|
|
530
|
+
"""
|
|
531
|
+
Doctest runner which raises an exception on the first error.
|
|
501
532
|
|
|
502
533
|
Almost verbatim copy of `doctest.DebugRunner`.
|
|
503
534
|
"""
|
|
@@ -521,8 +552,24 @@ class DebugDTRunner(DTRunner):
|
|
|
521
552
|
|
|
522
553
|
|
|
523
554
|
class DTFinder(doctest.DocTestFinder):
|
|
524
|
-
"""A Finder with helpful defaults.
|
|
525
555
|
"""
|
|
556
|
+
A drop-in replacement for `doctest.DocTestFinder` with helpful defaults.
|
|
557
|
+
|
|
558
|
+
Parameters:
|
|
559
|
+
-----------
|
|
560
|
+
verbose : bool, optional
|
|
561
|
+
If `True`, enables verbose output during doctest discovery.
|
|
562
|
+
parser : doctest.DocTestParser, optional
|
|
563
|
+
A custom parser for extracting doctests; defaults to `DTParser(config)`.
|
|
564
|
+
recurse : bool, default=True
|
|
565
|
+
Whether to recursively search for doctests in nested objects.
|
|
566
|
+
exclude_empty : bool, default=True
|
|
567
|
+
Whether to exclude objects that have no doctests.
|
|
568
|
+
config : DTConfig, optional
|
|
569
|
+
A configuration object controlling doctest behavior; a default instance is used if not provided.
|
|
570
|
+
|
|
571
|
+
"""
|
|
572
|
+
|
|
526
573
|
def __init__(self, verbose=None, parser=None, recurse=True,
|
|
527
574
|
exclude_empty=True, config=None):
|
|
528
575
|
if config is None:
|
|
@@ -537,7 +584,7 @@ class DTFinder(doctest.DocTestFinder):
|
|
|
537
584
|
if globs is None:
|
|
538
585
|
globs = dict(self.config.default_namespace)
|
|
539
586
|
# XXX: does this make similar checks in testmod/testfile duplicate?
|
|
540
|
-
if module not in self.config.skiplist:
|
|
587
|
+
if module not in self.config.skiplist:
|
|
541
588
|
tests = super().find(obj, name, module, globs, extraglobs)
|
|
542
589
|
|
|
543
590
|
if inspect.isclass(obj):
|
|
@@ -550,8 +597,21 @@ class DTFinder(doctest.DocTestFinder):
|
|
|
550
597
|
|
|
551
598
|
|
|
552
599
|
class DTParser(doctest.DocTestParser):
|
|
553
|
-
"""A Parser with a stopword list.
|
|
554
600
|
"""
|
|
601
|
+
A drop-in replacement for `doctest.DocTestParser` with a stopword list.
|
|
602
|
+
|
|
603
|
+
It filters out stopwords, pseudocode, and random markers from doctests.
|
|
604
|
+
|
|
605
|
+
Parameters:
|
|
606
|
+
-----------
|
|
607
|
+
config : DTConfig, optional
|
|
608
|
+
A configuration object containing:
|
|
609
|
+
- `stopwords`: A list of words that signal a doctest should be ignored.
|
|
610
|
+
- `pseudocode`: A list of markers indicating non-executable code.
|
|
611
|
+
- `rndm_markers`: A list of markers indicating results may vary.
|
|
612
|
+
|
|
613
|
+
"""
|
|
614
|
+
|
|
555
615
|
def __init__(self, config=None):
|
|
556
616
|
if config is None:
|
|
557
617
|
config = DTConfig()
|
|
@@ -608,4 +668,3 @@ class DTParser(doctest.DocTestParser):
|
|
|
608
668
|
example.want += " # _ignore\n"
|
|
609
669
|
examples.append(example)
|
|
610
670
|
return examples
|
|
611
|
-
|
scipy_doctest/plugin.py
CHANGED
|
@@ -47,7 +47,7 @@ def pytest_ignore_collect(collection_path, config):
|
|
|
47
47
|
"""
|
|
48
48
|
Determine whether to ignore the specified collection path.
|
|
49
49
|
This function is used to exclude the 'tests' directory and test modules when
|
|
50
|
-
the
|
|
50
|
+
the `--doctest-modules` option is used.
|
|
51
51
|
"""
|
|
52
52
|
if config.getoption("--doctest-modules"):
|
|
53
53
|
path_str = str(collection_path)
|
|
@@ -62,7 +62,7 @@ def pytest_ignore_collect(collection_path, config):
|
|
|
62
62
|
def is_private(item):
|
|
63
63
|
"""Decide if an DocTestItem `item` is private.
|
|
64
64
|
|
|
65
|
-
Private items are ignored in pytest_collect_modifyitem`.
|
|
65
|
+
Private items are ignored in `pytest_collect_modifyitem`.
|
|
66
66
|
"""
|
|
67
67
|
# Here we look at the name of a test module/object. A seemingly less
|
|
68
68
|
# hacky alternative is to populate a set of seen `item.dtest` attributes
|
|
@@ -174,7 +174,7 @@ def _is_deprecated(module):
|
|
|
174
174
|
class DTModule(DoctestModule):
|
|
175
175
|
"""
|
|
176
176
|
This class extends the DoctestModule class provided by pytest.
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
DTModule is responsible for overriding the behavior of the collect method.
|
|
179
179
|
The collect method is called by pytest to collect and generate test items for doctests
|
|
180
180
|
in the specified module or file.
|
|
@@ -222,7 +222,7 @@ class DTModule(DoctestModule):
|
|
|
222
222
|
|
|
223
223
|
optionflags = dt_config.optionflags
|
|
224
224
|
|
|
225
|
-
# Plug in the custom runner: `PytestDTRunner`
|
|
225
|
+
# Plug in the custom runner: `PytestDTRunner`
|
|
226
226
|
runner = _get_runner(self.config,
|
|
227
227
|
verbose=False,
|
|
228
228
|
optionflags=optionflags,
|
|
@@ -245,7 +245,7 @@ class DTModule(DoctestModule):
|
|
|
245
245
|
class DTTextfile(DoctestTextfile):
|
|
246
246
|
"""
|
|
247
247
|
This class extends the DoctestTextfile class provided by pytest.
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
DTTextfile is responsible for overriding the behavior of the collect method.
|
|
250
250
|
The collect method is called by pytest to collect and generate test items for doctests
|
|
251
251
|
in the specified text files.
|
|
@@ -282,7 +282,7 @@ class DTTextfile(DoctestTextfile):
|
|
|
282
282
|
def _get_runner(config, verbose, optionflags):
|
|
283
283
|
"""
|
|
284
284
|
Override function to return an instance of PytestDTRunner.
|
|
285
|
-
|
|
285
|
+
|
|
286
286
|
This function creates and returns an instance of PytestDTRunner, a custom runner class
|
|
287
287
|
that extends the behavior of DebugDTRunner for running doctests in pytest.
|
|
288
288
|
"""
|
|
@@ -290,13 +290,13 @@ def _get_runner(config, verbose, optionflags):
|
|
|
290
290
|
def run(self, test, compileflags=None, out=None, clear_globs=False):
|
|
291
291
|
"""
|
|
292
292
|
Run tests in context managers.
|
|
293
|
-
|
|
293
|
+
|
|
294
294
|
Restore the errstate/print state after each docstring.
|
|
295
295
|
Also, make MPL backend non-GUI and close the figures.
|
|
296
|
-
|
|
296
|
+
|
|
297
297
|
The order of context managers is actually relevant. Consider
|
|
298
298
|
user_context_mgr that turns warnings into errors.
|
|
299
|
-
|
|
299
|
+
|
|
300
300
|
Additionally, suppose that MPL deprecates something and plt.something
|
|
301
301
|
starts issuing warnings. Now all of those become errors
|
|
302
302
|
*unless* the `mpl()` context mgr has a chance to filter them out
|
|
@@ -215,7 +215,7 @@ Smoothing filters
|
|
|
215
215
|
corresponds to convolution with a Gaussian kernel. An order of 1, 2,
|
|
216
216
|
or 3 corresponds to convolution with the first, second, or third
|
|
217
217
|
derivatives of a Gaussian. Higher-order derivatives are not
|
|
218
|
-
implemented.
|
|
218
|
+
implemented.
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
|
|
@@ -232,7 +232,7 @@ Smoothing filters
|
|
|
232
232
|
number, to specify the same order for all axes, or a sequence of
|
|
233
233
|
numbers to specify a different order for each axis. The example below
|
|
234
234
|
shows the filter applied on test data with different values of *sigma*.
|
|
235
|
-
The *order* parameter is kept at 0.
|
|
235
|
+
The *order* parameter is kept at 0.
|
|
236
236
|
|
|
237
237
|
.. plot:: tutorial/examples/gaussian_filter_plot1.py
|
|
238
238
|
:align: center
|
|
@@ -86,8 +86,8 @@ class TestSkiplist:
|
|
|
86
86
|
assert sorted(names) == sorted(wanted_names)
|
|
87
87
|
|
|
88
88
|
def test_get_doctests_strategy_None(self):
|
|
89
|
-
# Add a skiplist: strategy=None skips listed items
|
|
90
|
-
base = finder_cases.__name__
|
|
89
|
+
# Add a skiplist: strategy=None skips listed items
|
|
90
|
+
base = finder_cases.__name__
|
|
91
91
|
skips = [base + '.func', base + '.Klass.meth_2']
|
|
92
92
|
config = DTConfig(skiplist=skips)
|
|
93
93
|
|
|
@@ -102,8 +102,8 @@ class TestSkiplist:
|
|
|
102
102
|
assert sorted(names) == sorted(wanted_names)
|
|
103
103
|
|
|
104
104
|
def test_get_doctests_strategy_api(self):
|
|
105
|
-
# Add a skiplist: strategy='api' skips listed items
|
|
106
|
-
base = finder_cases.__name__
|
|
105
|
+
# Add a skiplist: strategy='api' skips listed items
|
|
106
|
+
base = finder_cases.__name__
|
|
107
107
|
skips = [base + '.func', base + '.Klass.meth_2']
|
|
108
108
|
config = DTConfig(skiplist=skips)
|
|
109
109
|
|
|
@@ -120,8 +120,8 @@ class TestSkiplist:
|
|
|
120
120
|
assert sorted(names) == sorted(wanted_names)
|
|
121
121
|
|
|
122
122
|
def test_get_doctests_strategy_list(self):
|
|
123
|
-
# Add a skiplist: strategy=<list> skips listed items
|
|
124
|
-
base = finder_cases.__name__
|
|
123
|
+
# Add a skiplist: strategy=<list> skips listed items
|
|
124
|
+
base = finder_cases.__name__
|
|
125
125
|
skips = [base + '.func', base + '.Klass.meth_2']
|
|
126
126
|
config = DTConfig(skiplist=skips)
|
|
127
127
|
|
|
@@ -217,6 +217,3 @@ def test_private_superclasses_2():
|
|
|
217
217
|
|
|
218
218
|
assert len(tests) == len(expected_names)
|
|
219
219
|
assert names == set(expected_names)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
@@ -39,7 +39,7 @@ def test_failure_cases(pytester):
|
|
|
39
39
|
result = pytester.inline_run(python_file, "--doctest-modules")
|
|
40
40
|
assert result.ret == pytest.ExitCode.TESTS_FAILED
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
@pytest.mark.skipif(not HAVE_MATPLOTLIB, reason='need matplotlib')
|
|
44
44
|
def test_stopword_cases(pytester):
|
|
45
45
|
"""Test that pytest uses the DTParser for doctests."""
|
|
@@ -90,4 +90,3 @@ def test_alt_checker(pytester):
|
|
|
90
90
|
# run all tests with pytest
|
|
91
91
|
result = pytester.inline_run(f, '--doctest-modules')
|
|
92
92
|
assert result.ret == pytest.ExitCode.TESTS_FAILED
|
|
93
|
-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import doctest
|
|
2
|
+
from sys import version_info
|
|
2
3
|
|
|
3
4
|
try:
|
|
4
5
|
import matplotlib.pyplot as plt # noqa
|
|
@@ -57,7 +58,8 @@ class TestSyntaxErrors:
|
|
|
57
58
|
lineno=0)
|
|
58
59
|
runner = DebugDTRunner()
|
|
59
60
|
runner.run(test)
|
|
60
|
-
|
|
61
|
+
stats = (0, 1, 1) if version_info >= (3, 13) else (0, 0)
|
|
62
|
+
assert runner.get_history() == {'none : +SKIP': stats}
|
|
61
63
|
|
|
62
64
|
def test_invalid_python_pseudocode(self):
|
|
63
65
|
# Marking a test as pseudocode is equivalent to a +SKIP:
|
|
@@ -74,7 +76,8 @@ class TestSyntaxErrors:
|
|
|
74
76
|
lineno=0)
|
|
75
77
|
runner = DebugDTRunner()
|
|
76
78
|
runner.run(test)
|
|
77
|
-
|
|
79
|
+
stats = (0, 1, 1) if version_info >= (3, 13) else (0, 0)
|
|
80
|
+
assert runner.get_history() == {'none : pseudocode': stats}
|
|
78
81
|
|
|
79
82
|
|
|
80
83
|
class TestPseudocodeMarkers:
|
|
@@ -125,7 +128,8 @@ class TestStopwords:
|
|
|
125
128
|
runner.run(test)
|
|
126
129
|
|
|
127
130
|
# one example tried, of which zero failed
|
|
128
|
-
|
|
131
|
+
stats = (0, 1, 0) if version_info >= (3, 13) else (0, 1)
|
|
132
|
+
assert runner.get_history() == {'stopwords_bogus_output': stats}
|
|
129
133
|
|
|
130
134
|
|
|
131
135
|
class TestMayVary:
|
|
@@ -148,7 +152,8 @@ class TestMayVary:
|
|
|
148
152
|
runner.run(test)
|
|
149
153
|
|
|
150
154
|
# one example tried, of which zero failed
|
|
151
|
-
|
|
155
|
+
stats = (0, 1, 0) if version_info >= (3, 13) else (0, 1)
|
|
156
|
+
assert runner.get_history() == {'may_vary_markers': stats}
|
|
152
157
|
|
|
153
158
|
def test_may_vary_source(self):
|
|
154
159
|
# The marker needs to be added to the example output, not source.
|
|
@@ -164,7 +169,8 @@ class TestMayVary:
|
|
|
164
169
|
runner.run(test)
|
|
165
170
|
|
|
166
171
|
# one example tried, of which zero failed
|
|
167
|
-
|
|
172
|
+
stats = (0, 1, 0) if version_info >= (3, 13) else (0, 1)
|
|
173
|
+
assert runner.get_history() == {'may_vary_source': stats}
|
|
168
174
|
|
|
169
175
|
def test_may_vary_syntax_error(self):
|
|
170
176
|
# `# may vary` markers do not mask syntax errors, unlike `# doctest: +SKIP`
|
|
@@ -193,7 +199,7 @@ below
|
|
|
193
199
|
|
|
194
200
|
Note how the block above will fail doctesting unless the second line is
|
|
195
201
|
skipped. A standard solution is to add a +SKIP marker to every line, but this
|
|
196
|
-
is ugly and we skip the whole block instead.
|
|
202
|
+
is ugly and we skip the whole block instead.
|
|
197
203
|
|
|
198
204
|
Once the block is over, we get back to usual doctests, which are not skipped
|
|
199
205
|
|
|
@@ -216,4 +222,3 @@ def test_SKIPBLOCK():
|
|
|
216
222
|
assert test.examples[0].options[SKIP] is True
|
|
217
223
|
assert test.examples[1].options[SKIP] is True
|
|
218
224
|
assert test.examples[2].options == {} # not skipped
|
|
219
|
-
|
scipy_doctest/util.py
CHANGED
|
@@ -260,9 +260,9 @@ modules = []
|
|
|
260
260
|
def generate_log(module, test):
|
|
261
261
|
"""
|
|
262
262
|
Generate a log of the doctested items.
|
|
263
|
-
|
|
263
|
+
|
|
264
264
|
This function logs the items being doctested to a file named 'doctest.log'.
|
|
265
|
-
|
|
265
|
+
|
|
266
266
|
Args:
|
|
267
267
|
module (module): The module being doctested.
|
|
268
268
|
test (str): The name of the doctest item.
|
|
@@ -276,4 +276,3 @@ def generate_log(module, test):
|
|
|
276
276
|
LOGFILE.write(f"{test}\n")
|
|
277
277
|
except AttributeError:
|
|
278
278
|
LOGFILE.write(f"{test}\n")
|
|
279
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: scipy_doctest
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.1
|
|
4
4
|
Summary: Configurable, whitespace-insensitive, floating-point-aware doctest helpers.
|
|
5
5
|
Maintainer-email: SciPy developers <scipy-dev@python.org>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -10,20 +10,36 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Framework :: Pytest
|
|
13
|
+
License-File: LICENSE
|
|
13
14
|
Requires-Dist: numpy>=1.19.5
|
|
14
15
|
Requires-Dist: pytest
|
|
15
|
-
Requires-Dist:
|
|
16
|
+
Requires-Dist: furo==2024.8.6 ; extra == "doc"
|
|
17
|
+
Requires-Dist: myst-parser==4.0.0 ; extra == "doc"
|
|
18
|
+
Requires-Dist: sphinx==8.1.3 ; extra == "doc"
|
|
19
|
+
Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
|
|
20
|
+
Requires-Dist: scipy <= 1.14.1 ; extra == "test"
|
|
16
21
|
Requires-Dist: matplotlib ; extra == "test"
|
|
17
22
|
Project-URL: Home, https://github.com/scipy/scipy_doctest
|
|
23
|
+
Provides-Extra: doc
|
|
18
24
|
Provides-Extra: test
|
|
19
25
|
|
|
20
26
|
# Floating-point aware, human readable, numpy-compatible doctesting.
|
|
21
27
|
|
|
28
|
+
[![PyPI version][pypi-version]][pypi-link]
|
|
29
|
+
[![Conda-Forge][conda-badge]][conda-link]
|
|
30
|
+
|
|
31
|
+
<!-- prettier-ignore-start -->
|
|
32
|
+
[conda-badge]: https://img.shields.io/conda/vn/conda-forge/scipy-doctest
|
|
33
|
+
[conda-link]: https://anaconda.org/conda-forge/scipy-doctest
|
|
34
|
+
[pypi-link]: https://pypi.org/project/scipy-doctest/
|
|
35
|
+
[pypi-version]: https://img.shields.io/pypi/v/scipy-doctest
|
|
36
|
+
<!-- prettier-ignore-end -->
|
|
37
|
+
|
|
22
38
|
## TL;DR
|
|
23
39
|
|
|
24
40
|
This project extends the standard library `doctest` module to allow flexibility
|
|
25
41
|
and easy customization of finding, parsing and checking code examples in
|
|
26
|
-
documentation.
|
|
42
|
+
documentation.
|
|
27
43
|
|
|
28
44
|
Can be used either as drop-in `doctest` replacement or through the `pytest`
|
|
29
45
|
integration. Uses a floating-point aware doctest checker by default.
|
|
@@ -31,7 +47,7 @@ integration. Uses a floating-point aware doctest checker by default.
|
|
|
31
47
|
## Motivation and scope
|
|
32
48
|
|
|
33
49
|
Having examples in the documentation is great. Having wrong examples in the
|
|
34
|
-
documentation is not that great however.
|
|
50
|
+
documentation is not that great however.
|
|
35
51
|
|
|
36
52
|
The standard library `doctest` module is great for making sure that docstring
|
|
37
53
|
examples are correct. However, the `doctest` module is limited in several
|
|
@@ -46,7 +62,7 @@ This looks reasonably clear but does not work, in three different ways.
|
|
|
46
62
|
_First_, `1/3` is not equal to 0.333 because floating-point arithmetic.
|
|
47
63
|
_Second_, `numpy` adds whitespace to its output, this whitespace confuses the
|
|
48
64
|
`doctest`, which is whitespace-sensitive. Therefore, we added a magic directive,
|
|
49
|
-
|
|
65
|
+
`+SKIP` to avoid a doctest error. _Third_, the example is actually
|
|
50
66
|
wrong---notice `0.669` which is not equal to `2/3` to three sig figs. The error
|
|
51
67
|
went unnoticed by the doctester also because of the `+SKIP` directive.
|
|
52
68
|
|
|
@@ -56,44 +72,44 @@ a human reader, and should not be present in the documentation.
|
|
|
56
72
|
This package defines modified doctesting routines which fix these deficiencies.
|
|
57
73
|
Its main features are
|
|
58
74
|
|
|
59
|
-
-
|
|
75
|
+
- _Doctesting is floating-point aware._ In a nutshell, the core check is
|
|
60
76
|
`np.allclose(want, got, atol=..., rtol=...)`, with user-controllable abs
|
|
61
77
|
and relative tolerances. In the example above (_sans_ `# doctest: +SKIP`),
|
|
62
78
|
`want` is the desired output, `array([0.333, 0.669, 1])` and `got` is the
|
|
63
79
|
actual output from numpy: `array([0.33333333, 0.66666667, 1. ])`.
|
|
64
80
|
|
|
65
|
-
-
|
|
81
|
+
- _Human-readable skip markers._ Consider
|
|
66
82
|
```
|
|
67
83
|
>>> np.random.randint(100)
|
|
68
84
|
42 # may vary
|
|
69
85
|
```
|
|
70
|
-
Note that the markers (by default, `"# may vary"` and `"# random"`) can be applied
|
|
71
|
-
to either an example's output, or its source.
|
|
86
|
+
Note that the markers (by default, `"# may vary"` and `"# random"`) can be applied
|
|
87
|
+
to either an example's output, or its source.
|
|
72
88
|
|
|
73
89
|
Also note a difference with respect to the standard `# doctest: +SKIP`: the latter
|
|
74
90
|
skips the example entirely, while these additional markers only skip checking
|
|
75
91
|
the output. Thus the example source needs to be valid python code still.
|
|
76
92
|
|
|
77
|
-
- A user-configurable list of
|
|
93
|
+
- A user-configurable list of _stopwords_. If an example contains a stopword,
|
|
78
94
|
it is checked to be valid python, but the output is not checked. This can
|
|
79
95
|
be useful e.g. for not littering the documentation with the output of
|
|
80
96
|
`import matplotlib.pyplot as plt; plt.xlim([2.3, 4.5])`.
|
|
81
97
|
|
|
82
|
-
- A user-configurable list of
|
|
98
|
+
- A user-configurable list of _pseudocode_ markers. If an example contains one
|
|
83
99
|
of these markers, it is considered pseudocode and is not checked.
|
|
84
100
|
This is useful for `from example import some_functions` and similar stanzas.
|
|
85
101
|
|
|
86
102
|
- A `# doctest: +SKIPBLOCK` option flag to skip whole blocks of pseudocode. Here
|
|
87
103
|
a 'block' is a sequence of doctest examples without any intervening text.
|
|
88
104
|
|
|
89
|
-
-
|
|
105
|
+
- _Doctest discovery_ is somewhat more flexible then the standard library
|
|
90
106
|
`doctest` module. Specifically, one can use `testmod(module, strategy='api')`
|
|
91
107
|
to only examine public objects of a module. This is helpful for complex
|
|
92
108
|
packages, with non-trivial internal file structure. Alternatively, the default
|
|
93
109
|
value of `strategy=None` is equivalent to the standard `doctest` module
|
|
94
110
|
behavior.
|
|
95
111
|
|
|
96
|
-
-
|
|
112
|
+
- _User configuration_. Essentially all aspects of the behavior are user
|
|
97
113
|
configurable via a `DTConfig` instance attributes. See the `DTConfig`
|
|
98
114
|
docstring for details.
|
|
99
115
|
|
|
@@ -124,7 +140,7 @@ pip install scipy-doctest
|
|
|
124
140
|
|
|
125
141
|
2. **Register or load the plugin**
|
|
126
142
|
|
|
127
|
-
Next, you need to register or load the pytest plugin within your test module or `conftest.py` file.
|
|
143
|
+
Next, you need to register or load the pytest plugin within your test module or `conftest.py` file.
|
|
128
144
|
|
|
129
145
|
To do this, add the following line of code:
|
|
130
146
|
|
|
@@ -136,14 +152,16 @@ pytest_plugins = "scipy_doctest"
|
|
|
136
152
|
|
|
137
153
|
Check out the [pytest documentation](https://docs.pytest.org/en/stable/how-to/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file) for more information on requiring/loading plugins in a test module or `conftest.py` file.
|
|
138
154
|
|
|
139
|
-
3. **Run doctests**
|
|
155
|
+
3. **Run doctests**
|
|
140
156
|
|
|
141
157
|
Once the plugin is registered, run the doctests by executing the following command:
|
|
142
158
|
|
|
143
159
|
```bash
|
|
144
160
|
$ python -m pytest --doctest-modules
|
|
145
161
|
```
|
|
162
|
+
|
|
146
163
|
or
|
|
164
|
+
|
|
147
165
|
```bash
|
|
148
166
|
$ pytest --pyargs <your-package> --doctest-modules
|
|
149
167
|
```
|
|
@@ -155,10 +173,9 @@ use the command flag
|
|
|
155
173
|
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
156
174
|
```
|
|
157
175
|
|
|
158
|
-
See [More fine-grained control](
|
|
176
|
+
See [More fine-grained control](#more-fine-grained-control) section
|
|
159
177
|
for details on how to customize the behavior.
|
|
160
178
|
|
|
161
|
-
|
|
162
179
|
### Basic usage
|
|
163
180
|
|
|
164
181
|
The use of `pytest` is optional, and you can use the `doctest` layer API.
|
|
@@ -171,6 +188,7 @@ For example,
|
|
|
171
188
|
>>> res
|
|
172
189
|
TestResults(failed=0, attempted=764)
|
|
173
190
|
```
|
|
191
|
+
|
|
174
192
|
The second return value, `hist` is a dict which maps the names of the objects
|
|
175
193
|
to the numbers of failures and attempts for individual examples.
|
|
176
194
|
|
|
@@ -178,10 +196,10 @@ For more details, see the `testmod` docstring. Other useful functions are
|
|
|
178
196
|
`find_doctests`, `run_docstring_examples` and `testfile` (the latter two mimic
|
|
179
197
|
the behavior of the eponymous functions of the `doctest` module).
|
|
180
198
|
|
|
181
|
-
|
|
182
199
|
### Command-line interface
|
|
183
200
|
|
|
184
201
|
There is a basic CLI, which also mimics that of the `doctest` module:
|
|
202
|
+
|
|
185
203
|
```
|
|
186
204
|
$ python -m scipy_doctest foo.py
|
|
187
205
|
```
|
|
@@ -190,28 +208,30 @@ Note that, just like `$ python -m doctest foo.py`, this may
|
|
|
190
208
|
fail if `foo.py` is a part of a package due to package imports.
|
|
191
209
|
|
|
192
210
|
Text files can also be CLI-checked:
|
|
211
|
+
|
|
193
212
|
```
|
|
194
213
|
$ python -m scipy_doctest bar.rst
|
|
195
214
|
```
|
|
196
215
|
|
|
197
216
|
Notice that the command-line usage only uses the default `DTConfig` settings.
|
|
198
217
|
|
|
218
|
+
(more-fine-grained-control)=
|
|
199
219
|
|
|
200
220
|
## More fine-grained control
|
|
201
221
|
|
|
202
222
|
More fine-grained control of the functionality is available via the following
|
|
203
223
|
classes
|
|
204
224
|
|
|
205
|
-
|
|
|
206
|
-
|
|
207
|
-
| `DTChecker` | `DocTestChecker`
|
|
208
|
-
| `DTParser` | `DocTestParser`
|
|
209
|
-
| `DTRunner` | `DocTestRunner`
|
|
210
|
-
| `DTFinder` | `DocTestFinder`
|
|
211
|
-
| `DTContext` |
|
|
225
|
+
| Class | `doctest` analog |
|
|
226
|
+
| ----------- | ---------------- |
|
|
227
|
+
| `DTChecker` | `DocTestChecker` |
|
|
228
|
+
| `DTParser` | `DocTestParser` |
|
|
229
|
+
| `DTRunner` | `DocTestRunner` |
|
|
230
|
+
| `DTFinder` | `DocTestFinder` |
|
|
231
|
+
| `DTContext` | -- |
|
|
212
232
|
|
|
213
233
|
The `DTContext` class is just a bag class which holds various configuration
|
|
214
|
-
settings as attributes.
|
|
234
|
+
settings as attributes. An instance of this class is passed around, so user
|
|
215
235
|
configuration is simply creating an instance, overriding an attribute and
|
|
216
236
|
passing the instance to `testmod` or constructors of `DT*` objects. Defaults
|
|
217
237
|
are provided, based on a long-term usage in SciPy.
|
|
@@ -220,7 +240,7 @@ See the [DTConfig docstring](https://github.com/scipy/scipy_doctest/blob/main/sc
|
|
|
220
240
|
for the full set of attributes that allow you to fine-tune your doctesting experience.
|
|
221
241
|
|
|
222
242
|
To set any of these attributes, create an instance of `DTConfig` and assign the attributes
|
|
223
|
-
in a usual way.
|
|
243
|
+
in a usual way.
|
|
224
244
|
|
|
225
245
|
If using the pytest plugin, it is convenient to use the default instance, which
|
|
226
246
|
is predefined in `scipy_doctest/conftest.py`. This instance will be automatically
|
|
@@ -260,14 +280,12 @@ dt_config.skiplist = {
|
|
|
260
280
|
|
|
261
281
|
If you don't set these attributes, the [default settings](https://github.com/scipy/scipy_doctest/blob/58ff06a837b7bff1dbac6560013fc6fd07952ae2/scipy_doctest/impl.py#L94) of the attributes are used.
|
|
262
282
|
|
|
263
|
-
|
|
264
283
|
#### Alternative Checkers
|
|
265
284
|
|
|
266
285
|
By default, we use the floating-point aware `DTChecker`. If you want to use an
|
|
267
286
|
alternative checker, all you need to do is to define the corresponding class,
|
|
268
287
|
and add an attribute to the `DTConfig` instance. For example,
|
|
269
288
|
|
|
270
|
-
|
|
271
289
|
```
|
|
272
290
|
class VanillaOutputChecker(doctest.OutputChecker):
|
|
273
291
|
"""doctest.OutputChecker to drop in for DTChecker.
|
|
@@ -290,10 +308,8 @@ See [a pytest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_do
|
|
|
290
308
|
and [a doctest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/tests/test_runner.py#L94)
|
|
291
309
|
for more details.
|
|
292
310
|
|
|
293
|
-
|
|
294
311
|
### NumPy and SciPy wrappers
|
|
295
312
|
|
|
296
|
-
|
|
297
313
|
NumPy wraps `scipy-doctest` with the `spin` command
|
|
298
314
|
|
|
299
315
|
```
|
|
@@ -307,13 +323,11 @@ $ python dev.py smoke-docs # check docstrings
|
|
|
307
323
|
$ python dev.py smoke-tutorials # ReST user guide tutorials
|
|
308
324
|
```
|
|
309
325
|
|
|
310
|
-
|
|
311
|
-
|
|
312
326
|
## Rough edges and sharp bits
|
|
313
327
|
|
|
314
328
|
Here is a (non-exhaustive) list of possible gotchas:
|
|
315
329
|
|
|
316
|
-
-
|
|
330
|
+
- _In-place development builds_.
|
|
317
331
|
|
|
318
332
|
Some tools (looking at you `meson-python`) simulate in-place builds with a
|
|
319
333
|
`build-install` directory. If this directory is located under the project root,
|
|
@@ -329,12 +343,15 @@ $ pytest build-install/lib/python3.10/site-packages/scipy/ --doctest-modules
|
|
|
329
343
|
|
|
330
344
|
instead of `$ pytest --pyargs scipy`.
|
|
331
345
|
|
|
332
|
-
If
|
|
346
|
+
If you use actual editable installs, of the `pip install --no-build-isolation -e .` variety, you may
|
|
347
|
+
need to add `--import-mode=importlib` to the `pytest` invocation.
|
|
348
|
+
|
|
349
|
+
If push really comes to shove, you may try using the magic env variable:
|
|
333
350
|
` PY_IGNORE_IMPORTMISMATCH=1 pytest ...`,
|
|
334
351
|
however the need usually indicates an issue with the package itself.
|
|
335
352
|
(see [gh-107](https://github.com/scipy/scipy_doctest/pull/107) for an example).
|
|
336
353
|
|
|
337
|
-
-
|
|
354
|
+
- _Optional dependencies are not that optional_
|
|
338
355
|
|
|
339
356
|
If your package contains optional dependencies, doctests do not know about them
|
|
340
357
|
being optional. So you either guard the imports in doctests (yikes!), or
|
|
@@ -353,11 +370,11 @@ Note that installed packages are no different:
|
|
|
353
370
|
$ pytest --pyargs scipy --doctest-modules --ignore=/path/to/installed/scipy/_lib
|
|
354
371
|
```
|
|
355
372
|
|
|
356
|
-
-
|
|
373
|
+
- _Doctest collection strategies_
|
|
357
374
|
|
|
358
375
|
The default collection strategy follows `doctest` module and `pytest`. This leads
|
|
359
376
|
to duplicates if your package has the split between public and \_private modules,
|
|
360
|
-
where
|
|
377
|
+
where public modules re-export things from private ones. The solution is to
|
|
361
378
|
use `$ pytest --doctest-collect=api` CLI switch: with this, only public
|
|
362
379
|
objects will be collected.
|
|
363
380
|
|
|
@@ -375,8 +392,7 @@ leads to
|
|
|
375
392
|
- `scipy.linalg._basic.det`, collected from `scipy/linalg/_basic.py`, is private.
|
|
376
393
|
- `scipy.linalg.det`, collected from `scipy/linalg/__init__.py`, is public.
|
|
377
394
|
|
|
378
|
-
|
|
379
|
-
- *`pytest`'s assertion rewriting*
|
|
395
|
+
- _`pytest`'s assertion rewriting_
|
|
380
396
|
|
|
381
397
|
In some rare cases you may need to either explicitly register the `scipy_doctest`
|
|
382
398
|
package with the `pytest` assertion rewriting machinery, or ask it to avoid rewriting
|
|
@@ -388,7 +404,6 @@ In general, rewriting assertions is not very useful for doctests, as the
|
|
|
388
404
|
output on error is fixed by the doctest machinery anyway. Therefore, we believe
|
|
389
405
|
adding `--assert=plain` is reasonable.
|
|
390
406
|
|
|
391
|
-
|
|
392
407
|
## Prior art and related work
|
|
393
408
|
|
|
394
409
|
- `pytest` provides some limited floating-point aware `NumericLiteralChecker`.
|
|
@@ -409,15 +424,14 @@ adding `--assert=plain` is reasonable.
|
|
|
409
424
|
to be not easy to reason about, work with, and extend to other projects.
|
|
410
425
|
|
|
411
426
|
This project is mainly the core functionality of the modified
|
|
412
|
-
`refguide-check` doctesting, extracted to a separate package.
|
|
427
|
+
`refguide-check` doctesting, extracted to a separate package.
|
|
413
428
|
We believe having it separate simplifies both addressing the needs of these
|
|
414
429
|
two packages, and potential adoption by other projects.
|
|
415
430
|
|
|
416
|
-
|
|
417
431
|
### Bug reports, feature requests and contributions
|
|
418
432
|
|
|
419
433
|
This package is work in progress. Contributions are most welcome!
|
|
420
434
|
Please don't hesitate to open an issue in the tracker or send a pull request.
|
|
421
435
|
|
|
422
|
-
The current location of the issue tracker is https://github.com/scipy/scipy_doctest
|
|
436
|
+
The current location of the issue tracker is <https://github.com/scipy/scipy_doctest>.
|
|
423
437
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
scipy_doctest/__init__.py,sha256=aatYgtIlyr2M-BiFgUpQ5aqy-1bE3VIhnVE2GE-jI0I,650
|
|
2
|
+
scipy_doctest/__main__.py,sha256=H8jTO13GlOLzexbgu7lHMJW1y3_NhLOSArARFZ5WS7o,90
|
|
3
|
+
scipy_doctest/conftest.py,sha256=rOkdrpmq95vzvMGMqm88c4HTrbpxLOvANtEuiD6suCg,52
|
|
4
|
+
scipy_doctest/frontend.py,sha256=wl0-8I6epZ49TwxKVSuZbizS6J_pxea99z0vAv2EdbY,19126
|
|
5
|
+
scipy_doctest/impl.py,sha256=kp8W2p7mTvQR-TwYc2EXA3JiXo1Z9t98XDzFsoOUVSc,26480
|
|
6
|
+
scipy_doctest/plugin.py,sha256=8g2Jyoa69ezQ22RrVBcD42HGNZ6z3oqaPITBDHzJSiY,12852
|
|
7
|
+
scipy_doctest/util.py,sha256=NQaJWfpUCqB8khIRJnTZz8ktKFqd0xT2sipFlv4RIhA,8048
|
|
8
|
+
scipy_doctest/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
scipy_doctest/tests/failure_cases.py,sha256=ReSRSugjKDNoWO5M4h-vwCbX_7BwkktlwEjFiGWI3F4,732
|
|
10
|
+
scipy_doctest/tests/failure_cases_2.py,sha256=p2Qg_eKh35-gavFS25hRNkYW22w8kwNRlAg0Vrq4MtM,783
|
|
11
|
+
scipy_doctest/tests/finder_cases.py,sha256=-aWcIyKsW1GAOEhuqEYUVYGLYsVi2FU8HgZG2x7Nmyc,870
|
|
12
|
+
scipy_doctest/tests/finder_cases_2.py,sha256=Z5pnvVQKk3Z8bWpnQyBSnM02qk-80R0doi7DYUbMBaU,306
|
|
13
|
+
scipy_doctest/tests/local_file.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
scipy_doctest/tests/local_file_cases.py,sha256=8gIEwIJOwwWo0ii2I2gMbcg_8ZhH5XoWqqRFWxnVpd8,912
|
|
15
|
+
scipy_doctest/tests/module_cases.py,sha256=kApmKDYImEHbgm-hiCSG7HpDzHhvwqOM8d1J9Dq7Gk4,5827
|
|
16
|
+
scipy_doctest/tests/octave_a.mat,sha256=lOfXBSOdMG7_kruTnOHjixXkPy3zSUt10A1FSVjfngI,288
|
|
17
|
+
scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst,sha256=gw5YfCNuq1I-Pwu2XUYlOFLg4Qk8E0n9A8APXmcBh1w,81592
|
|
18
|
+
scipy_doctest/tests/stopwords_cases.py,sha256=OEZkoFW3B9nHUCG_5adSkLI91avSwNjw-NeUS0D6Yz0,156
|
|
19
|
+
scipy_doctest/tests/test_finder.py,sha256=8upZaULq8LhhQQyM1eriKhg2I48eGXm3cSJ0s7SkXzw,7769
|
|
20
|
+
scipy_doctest/tests/test_parser.py,sha256=jF32p7PDrxKrdMdX91VTkkjiMhYrjCbPkxWq1RKMryA,922
|
|
21
|
+
scipy_doctest/tests/test_pytest_configuration.py,sha256=Fou_WthuWwh0q-o2zZD92HLPwbWOjxcJAV_s_ymbP20,2568
|
|
22
|
+
scipy_doctest/tests/test_runner.py,sha256=cXDY51lRP7fG-5C_6Y69e8fFsB6mLr0NWauH4sCOWHs,2935
|
|
23
|
+
scipy_doctest/tests/test_skipmarkers.py,sha256=dLcoy1F0e1GrLLAi2CqJQLapFB8uUBoqkTT-n5oFWbY,8182
|
|
24
|
+
scipy_doctest/tests/test_testfile.py,sha256=66ZHUpEGGg8MfQT8EKSZ8Zx5pV55gP__TZejGMYwBHA,733
|
|
25
|
+
scipy_doctest/tests/test_testmod.py,sha256=We1LBz3rKD5VDkcB8ssQehEr5aQRy2riBJwGwJWGgzs,5870
|
|
26
|
+
scipy_doctest-1.7.1.dist-info/entry_points.txt,sha256=dFda3z6PjFL7pEWokv_QmoLwE8X1HETCY1H60xopQ-s,47
|
|
27
|
+
scipy_doctest-1.7.1.dist-info/licenses/LICENSE,sha256=xH5PVX8bm8e1JxkmJ-e5FsZsOa7FsNOMfepmCvMoR9g,1523
|
|
28
|
+
scipy_doctest-1.7.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
29
|
+
scipy_doctest-1.7.1.dist-info/METADATA,sha256=Lq0C_omAgRLkCO_p0Aiel5wrlKE6XkMAzZjeZZAvdRI,15713
|
|
30
|
+
scipy_doctest-1.7.1.dist-info/RECORD,,
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
scipy_doctest/__init__.py,sha256=7yxrEzBJa5wOYwTNIVk07V_hvLLH-SFLEZGwDIAYurs,649
|
|
2
|
-
scipy_doctest/__main__.py,sha256=H8jTO13GlOLzexbgu7lHMJW1y3_NhLOSArARFZ5WS7o,90
|
|
3
|
-
scipy_doctest/conftest.py,sha256=5vZxzuH042urYIToiKWANDJHQPoGkfQI-ppQ_cuHgM0,53
|
|
4
|
-
scipy_doctest/frontend.py,sha256=g4CIPcQH-q1dWc6B0NgkeO5_lDVh2kX7oDHJDEJObLM,19124
|
|
5
|
-
scipy_doctest/impl.py,sha256=tEetxiCUcwz4XFmlDbeBx7ahZvYSQvOFwiaDbBZQEe0,24409
|
|
6
|
-
scipy_doctest/plugin.py,sha256=DzTPBCDIre5b2e8JLiTGw7d9zhCP9hYqXcxeKpFTtys,12900
|
|
7
|
-
scipy_doctest/util.py,sha256=R-pS9FSL5hQNmOA0nhRDLOL1riFVAoK-OhG70ilaKhw,8057
|
|
8
|
-
scipy_doctest/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
scipy_doctest/tests/failure_cases.py,sha256=ReSRSugjKDNoWO5M4h-vwCbX_7BwkktlwEjFiGWI3F4,732
|
|
10
|
-
scipy_doctest/tests/failure_cases_2.py,sha256=gupqwSICvzurGIiKVNJRJX9jkmtFR7_Rf3snWU-4Nac,784
|
|
11
|
-
scipy_doctest/tests/finder_cases.py,sha256=s4sq5HZ7m5mXEi1N8dbkgCRY6AxEGcirFRYhEyEG7rw,872
|
|
12
|
-
scipy_doctest/tests/finder_cases_2.py,sha256=Z5pnvVQKk3Z8bWpnQyBSnM02qk-80R0doi7DYUbMBaU,306
|
|
13
|
-
scipy_doctest/tests/local_file.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
scipy_doctest/tests/local_file_cases.py,sha256=xQeD809EoLxY-QIb0X7ZPl6Ecyme3zjnf95AHJ_8gp0,914
|
|
15
|
-
scipy_doctest/tests/module_cases.py,sha256=W3u97oMJ8yf0rqrsiQbB1yMMTkKAVz4gNpBLDhijHek,5860
|
|
16
|
-
scipy_doctest/tests/octave_a.mat,sha256=lOfXBSOdMG7_kruTnOHjixXkPy3zSUt10A1FSVjfngI,288
|
|
17
|
-
scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst,sha256=uCtH99RQM0hvDK2_5lgBO2hqtMUOUQhPKxWn4zwHjSs,81594
|
|
18
|
-
scipy_doctest/tests/stopwords_cases.py,sha256=OEZkoFW3B9nHUCG_5adSkLI91avSwNjw-NeUS0D6Yz0,156
|
|
19
|
-
scipy_doctest/tests/test_finder.py,sha256=lzxLj0QSSKYjy9Cp74d5jzl4BZ5pA667iXGd7xRxPkY,7781
|
|
20
|
-
scipy_doctest/tests/test_parser.py,sha256=cmK5kXqTWPUdSVor4bPu6yoikIukDIkVXjIjk1TTPI8,925
|
|
21
|
-
scipy_doctest/tests/test_pytest_configuration.py,sha256=2aesrRyHFdMcRMjARXJ5g8nMtaYC0gb_48Mh8U58RzE,2573
|
|
22
|
-
scipy_doctest/tests/test_runner.py,sha256=qP4u8ngbUK946HhMM6Py70hi0W0DcZGcCN258phhM7g,2936
|
|
23
|
-
scipy_doctest/tests/test_skipmarkers.py,sha256=C5U8BKF3Ti-nPWekt2yK6a3j3Tcg_pq-Z5IuiR-F9eU,7835
|
|
24
|
-
scipy_doctest/tests/test_testfile.py,sha256=66ZHUpEGGg8MfQT8EKSZ8Zx5pV55gP__TZejGMYwBHA,733
|
|
25
|
-
scipy_doctest/tests/test_testmod.py,sha256=PoOI0o2_dXjWDmDkUUKqcJiyZvh46YpjlmMxyW6PXyI,5874
|
|
26
|
-
scipy_doctest-1.6.dist-info/entry_points.txt,sha256=dFda3z6PjFL7pEWokv_QmoLwE8X1HETCY1H60xopQ-s,47
|
|
27
|
-
scipy_doctest-1.6.dist-info/LICENSE,sha256=xH5PVX8bm8e1JxkmJ-e5FsZsOa7FsNOMfepmCvMoR9g,1523
|
|
28
|
-
scipy_doctest-1.6.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
29
|
-
scipy_doctest-1.6.dist-info/METADATA,sha256=a7JvqLaDc-LQ0nsKN9X3gEa4MzQ7aNA8pH8zMqOMHNw,14867
|
|
30
|
-
scipy_doctest-1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|