scipy-doctest 1.8.0__tar.gz → 2.0.0__tar.gz
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-1.8.0 → scipy_doctest-2.0.0}/PKG-INFO +52 -32
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/README.md +51 -31
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/__init__.py +1 -1
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/impl.py +16 -5
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/plugin.py +25 -14
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/failure_cases.py +22 -2
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/module_cases.py +20 -6
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_testmod.py +13 -2
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/util.py +18 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/LICENSE +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/pyproject.toml +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/__main__.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/conftest.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/frontend.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/__init__.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/failure_cases_2.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/finder_cases.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/finder_cases_2.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/local_file.txt +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/local_file_cases.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/octave_a.mat +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/stopwords_cases.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_finder.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_parser.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_pytest_configuration.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_runner.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_skipmarkers.py +0 -0
- {scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_testfile.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scipy_doctest
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
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
|
|
@@ -176,21 +176,20 @@ $ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
|
176
176
|
See [More fine-grained control](#more-fine-grained-control) section
|
|
177
177
|
for details on how to customize the behavior.
|
|
178
178
|
|
|
179
|
-
**
|
|
180
|
-
'regular' unit tests. This
|
|
181
|
-
both doctests and unit tests.
|
|
182
|
-
The behavior will change in a future version: `scipy-doctest==2.0` **will change the
|
|
183
|
-
default** to align with the vanilla `pytest`.
|
|
179
|
+
**NOTE** In versions 1.x, `pytest --doctest-modules` was only collecting doctests, and
|
|
180
|
+
skipped 'regular' unit tests. This differed from the vanilla `pytest` behavior, which
|
|
181
|
+
collects both doctests and unit tests.
|
|
184
182
|
|
|
185
|
-
|
|
183
|
+
The behavior was changed in version 2.0: from `scipy-doctest==2.0` the default is
|
|
184
|
+
changed to align with the vanilla `pytest`.
|
|
185
|
+
|
|
186
|
+
To retain the previous behavior and skip 'regular' unit tests, use the
|
|
187
|
+
`--doctest-only-doctests` CLI option:
|
|
186
188
|
|
|
187
189
|
```
|
|
188
|
-
$ pytest --doctest-modules --doctest-only-doctests
|
|
190
|
+
$ pytest --doctest-modules --doctest-only-doctests=true
|
|
189
191
|
```
|
|
190
192
|
|
|
191
|
-
is unambiguous and will behave identically in the current version 1.8 and upcoming
|
|
192
|
-
versions of `scipy-doctest`.
|
|
193
|
-
|
|
194
193
|
|
|
195
194
|
### Basic usage
|
|
196
195
|
|
|
@@ -343,7 +342,7 @@ $ spin smoke-tutorials # ReST user guide tutorials
|
|
|
343
342
|
|
|
344
343
|
Here is a (non-exhaustive) list of possible gotchas:
|
|
345
344
|
|
|
346
|
-
|
|
345
|
+
#### _In-place development builds_.
|
|
347
346
|
|
|
348
347
|
Some tools (looking at you `meson-python`) simulate in-place builds with a
|
|
349
348
|
`build-install` directory. If this directory is located under the project root,
|
|
@@ -367,10 +366,10 @@ If push really comes to shove, you may try using the magic env variable:
|
|
|
367
366
|
however the need usually indicates an issue with the package itself.
|
|
368
367
|
(see [gh-107](https://github.com/scipy/scipy_doctest/pull/107) for an example).
|
|
369
368
|
|
|
370
|
-
|
|
369
|
+
#### _Optional dependencies are not that optional_
|
|
371
370
|
|
|
372
371
|
If your package contains optional dependencies, doctests do not know about them
|
|
373
|
-
being optional. So you either guard the imports in doctests (yikes!), or
|
|
372
|
+
being optional. So you either guard the imports in doctests themselves (yikes!), or
|
|
374
373
|
the collections fails if dependencies are not available.
|
|
375
374
|
|
|
376
375
|
The solution is to explicitly `--ignore` the paths to modules with optionals.
|
|
@@ -386,7 +385,7 @@ Note that installed packages are no different:
|
|
|
386
385
|
$ pytest --pyargs scipy --doctest-modules --ignore=/path/to/installed/scipy/_lib
|
|
387
386
|
```
|
|
388
387
|
|
|
389
|
-
|
|
388
|
+
#### _Doctest collection strategies_
|
|
390
389
|
|
|
391
390
|
The default collection strategy follows `doctest` module and `pytest`. This leads
|
|
392
391
|
to duplicates if your package has the split between public and \_private modules,
|
|
@@ -396,19 +395,19 @@ objects will be collected.
|
|
|
396
395
|
|
|
397
396
|
The decision on what is public is as follows: an object is public iff
|
|
398
397
|
|
|
399
|
-
- it is included into the `__all__` list of a public module;
|
|
400
|
-
- the name of the object does not have a leading underscore;
|
|
401
|
-
- the name of the module from which the object is collected does not have
|
|
398
|
+
- it is included into the `__all__` list of a public module;
|
|
399
|
+
- the name of the object does not have a leading underscore;
|
|
400
|
+
- the name of the module from which the object is collected does not have
|
|
402
401
|
a leading underscore.
|
|
403
402
|
|
|
404
403
|
Consider an example: `scipy.linalg.det` is defined in `scipy/linalg/_basic.py`,
|
|
405
404
|
so it is collected twice, from `_basic.py` and from `__init__.py`. The rule above
|
|
406
405
|
leads to
|
|
407
406
|
|
|
408
|
-
- `scipy.linalg._basic.det`, collected from `scipy/linalg/_basic.py`, is private.
|
|
409
|
-
- `scipy.linalg.det`, collected from `scipy/linalg/__init__.py`, is public.
|
|
407
|
+
- `scipy.linalg._basic.det`, collected from `scipy/linalg/_basic.py`, is private.
|
|
408
|
+
- `scipy.linalg.det`, collected from `scipy/linalg/__init__.py`, is public.
|
|
410
409
|
|
|
411
|
-
|
|
410
|
+
#### _`pytest`'s assertion rewriting_
|
|
412
411
|
|
|
413
412
|
In some rare cases you may need to either explicitly register the `scipy_doctest`
|
|
414
413
|
package with the `pytest` assertion rewriting machinery, or ask it to avoid rewriting
|
|
@@ -420,6 +419,21 @@ In general, rewriting assertions is not very useful for doctests, as the
|
|
|
420
419
|
output on error is fixed by the doctest machinery anyway. Therefore, we believe
|
|
421
420
|
adding `--assert=plain` is reasonable.
|
|
422
421
|
|
|
422
|
+
#### _Mixing strings and numbers_
|
|
423
|
+
|
|
424
|
+
Generally, we aim to handle mixtures of strings and numeric data. Deeply nested data
|
|
425
|
+
structures, however, may cause the checker to fall back to the vanilla `doctest` literal
|
|
426
|
+
checking. For instance, `["value", 1/3]` will use the floating-point aware checker, and
|
|
427
|
+
so will `{"value": 1/3, "other value": 2/3}` or `[(1, 2), (3, 4)]`; Meanwhile, nested
|
|
428
|
+
dictionaries, `{"a": dict(value=1/3)}`, or lists of tuples with mixed entries,
|
|
429
|
+
`[("a", 1/3), ("b", 2/3)]`, will currently fall back to vanilla `doctest` literal
|
|
430
|
+
comparisons.
|
|
431
|
+
|
|
432
|
+
We stress that no matter how tricky or deeply nested the output is, the worst case
|
|
433
|
+
scenario is that the floating-point aware checker is not used. If you have a case where
|
|
434
|
+
`doctest` works correctly and `scipy_doctest` does not, please report it as a bug.
|
|
435
|
+
|
|
436
|
+
|
|
423
437
|
## Prior art and related work
|
|
424
438
|
|
|
425
439
|
- `pytest` provides some limited floating-point aware `NumericLiteralChecker`.
|
|
@@ -427,24 +441,30 @@ adding `--assert=plain` is reasonable.
|
|
|
427
441
|
- `pytest-doctestplus` plugin from the `AstroPy` project has similar goals.
|
|
428
442
|
The package is well established and widely used. From a user perspective, main
|
|
429
443
|
differences are: (i) `pytest-doctestplus` is more sensitive to formatting,
|
|
430
|
-
including whitespace
|
|
431
|
-
may start failing; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
444
|
+
including whitespace; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
432
445
|
directives.
|
|
433
446
|
|
|
434
|
-
This project takes a different approach:
|
|
435
|
-
|
|
436
|
-
|
|
447
|
+
This project takes a slightly different approach: we strive to make numeric comparisons
|
|
448
|
+
whitespace insensitive and automatic, without a need for explicit markup. For rare cases
|
|
449
|
+
which require additional configuration, we either keep it in the tool (thus out of
|
|
450
|
+
reader-visible docstrings), or provide human-readable markers (hence `# may vary`
|
|
451
|
+
not `# doctest +SKIP`).
|
|
452
|
+
Furthermore, in addition to plugging into `pytest`, we provide an API layer which closely
|
|
453
|
+
follows the `doctest` API. Essentially all aspects of doctesting are user-configurable.
|
|
454
|
+
|
|
455
|
+
- `xdoctest` package relies on a deeper rewrite of the standard-library `doctest`
|
|
456
|
+
functionality, and uses an AST-based analysis to parse code examples out of docstrings.
|
|
437
457
|
|
|
438
458
|
- `NumPy` and `SciPy` were using modified doctesting in their `refguide-check` utilities.
|
|
439
|
-
These utilities
|
|
459
|
+
These utilities were tightly coupled to their libraries, and have been reported
|
|
440
460
|
to be not easy to reason about, work with, and extend to other projects.
|
|
441
461
|
|
|
442
|
-
This project is mainly the core functionality of the modified
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
462
|
+
This project is mainly the core functionality of the modified `refguide-check` doctesting,
|
|
463
|
+
extracted to a separate package. We believe having it separate simplifies both
|
|
464
|
+
addressing the needs of these two packages, and adoption by other projects.
|
|
465
|
+
|
|
446
466
|
|
|
447
|
-
|
|
467
|
+
## Bug reports, feature requests and contributions
|
|
448
468
|
|
|
449
469
|
This package is work in progress. Contributions are most welcome!
|
|
450
470
|
Please don't hesitate to open an issue in the tracker or send a pull request.
|
|
@@ -151,21 +151,20 @@ $ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
|
151
151
|
See [More fine-grained control](#more-fine-grained-control) section
|
|
152
152
|
for details on how to customize the behavior.
|
|
153
153
|
|
|
154
|
-
**
|
|
155
|
-
'regular' unit tests. This
|
|
156
|
-
both doctests and unit tests.
|
|
157
|
-
The behavior will change in a future version: `scipy-doctest==2.0` **will change the
|
|
158
|
-
default** to align with the vanilla `pytest`.
|
|
154
|
+
**NOTE** In versions 1.x, `pytest --doctest-modules` was only collecting doctests, and
|
|
155
|
+
skipped 'regular' unit tests. This differed from the vanilla `pytest` behavior, which
|
|
156
|
+
collects both doctests and unit tests.
|
|
159
157
|
|
|
160
|
-
|
|
158
|
+
The behavior was changed in version 2.0: from `scipy-doctest==2.0` the default is
|
|
159
|
+
changed to align with the vanilla `pytest`.
|
|
160
|
+
|
|
161
|
+
To retain the previous behavior and skip 'regular' unit tests, use the
|
|
162
|
+
`--doctest-only-doctests` CLI option:
|
|
161
163
|
|
|
162
164
|
```
|
|
163
|
-
$ pytest --doctest-modules --doctest-only-doctests
|
|
165
|
+
$ pytest --doctest-modules --doctest-only-doctests=true
|
|
164
166
|
```
|
|
165
167
|
|
|
166
|
-
is unambiguous and will behave identically in the current version 1.8 and upcoming
|
|
167
|
-
versions of `scipy-doctest`.
|
|
168
|
-
|
|
169
168
|
|
|
170
169
|
### Basic usage
|
|
171
170
|
|
|
@@ -318,7 +317,7 @@ $ spin smoke-tutorials # ReST user guide tutorials
|
|
|
318
317
|
|
|
319
318
|
Here is a (non-exhaustive) list of possible gotchas:
|
|
320
319
|
|
|
321
|
-
|
|
320
|
+
#### _In-place development builds_.
|
|
322
321
|
|
|
323
322
|
Some tools (looking at you `meson-python`) simulate in-place builds with a
|
|
324
323
|
`build-install` directory. If this directory is located under the project root,
|
|
@@ -342,10 +341,10 @@ If push really comes to shove, you may try using the magic env variable:
|
|
|
342
341
|
however the need usually indicates an issue with the package itself.
|
|
343
342
|
(see [gh-107](https://github.com/scipy/scipy_doctest/pull/107) for an example).
|
|
344
343
|
|
|
345
|
-
|
|
344
|
+
#### _Optional dependencies are not that optional_
|
|
346
345
|
|
|
347
346
|
If your package contains optional dependencies, doctests do not know about them
|
|
348
|
-
being optional. So you either guard the imports in doctests (yikes!), or
|
|
347
|
+
being optional. So you either guard the imports in doctests themselves (yikes!), or
|
|
349
348
|
the collections fails if dependencies are not available.
|
|
350
349
|
|
|
351
350
|
The solution is to explicitly `--ignore` the paths to modules with optionals.
|
|
@@ -361,7 +360,7 @@ Note that installed packages are no different:
|
|
|
361
360
|
$ pytest --pyargs scipy --doctest-modules --ignore=/path/to/installed/scipy/_lib
|
|
362
361
|
```
|
|
363
362
|
|
|
364
|
-
|
|
363
|
+
#### _Doctest collection strategies_
|
|
365
364
|
|
|
366
365
|
The default collection strategy follows `doctest` module and `pytest`. This leads
|
|
367
366
|
to duplicates if your package has the split between public and \_private modules,
|
|
@@ -371,19 +370,19 @@ objects will be collected.
|
|
|
371
370
|
|
|
372
371
|
The decision on what is public is as follows: an object is public iff
|
|
373
372
|
|
|
374
|
-
- it is included into the `__all__` list of a public module;
|
|
375
|
-
- the name of the object does not have a leading underscore;
|
|
376
|
-
- the name of the module from which the object is collected does not have
|
|
373
|
+
- it is included into the `__all__` list of a public module;
|
|
374
|
+
- the name of the object does not have a leading underscore;
|
|
375
|
+
- the name of the module from which the object is collected does not have
|
|
377
376
|
a leading underscore.
|
|
378
377
|
|
|
379
378
|
Consider an example: `scipy.linalg.det` is defined in `scipy/linalg/_basic.py`,
|
|
380
379
|
so it is collected twice, from `_basic.py` and from `__init__.py`. The rule above
|
|
381
380
|
leads to
|
|
382
381
|
|
|
383
|
-
- `scipy.linalg._basic.det`, collected from `scipy/linalg/_basic.py`, is private.
|
|
384
|
-
- `scipy.linalg.det`, collected from `scipy/linalg/__init__.py`, is public.
|
|
382
|
+
- `scipy.linalg._basic.det`, collected from `scipy/linalg/_basic.py`, is private.
|
|
383
|
+
- `scipy.linalg.det`, collected from `scipy/linalg/__init__.py`, is public.
|
|
385
384
|
|
|
386
|
-
|
|
385
|
+
#### _`pytest`'s assertion rewriting_
|
|
387
386
|
|
|
388
387
|
In some rare cases you may need to either explicitly register the `scipy_doctest`
|
|
389
388
|
package with the `pytest` assertion rewriting machinery, or ask it to avoid rewriting
|
|
@@ -395,6 +394,21 @@ In general, rewriting assertions is not very useful for doctests, as the
|
|
|
395
394
|
output on error is fixed by the doctest machinery anyway. Therefore, we believe
|
|
396
395
|
adding `--assert=plain` is reasonable.
|
|
397
396
|
|
|
397
|
+
#### _Mixing strings and numbers_
|
|
398
|
+
|
|
399
|
+
Generally, we aim to handle mixtures of strings and numeric data. Deeply nested data
|
|
400
|
+
structures, however, may cause the checker to fall back to the vanilla `doctest` literal
|
|
401
|
+
checking. For instance, `["value", 1/3]` will use the floating-point aware checker, and
|
|
402
|
+
so will `{"value": 1/3, "other value": 2/3}` or `[(1, 2), (3, 4)]`; Meanwhile, nested
|
|
403
|
+
dictionaries, `{"a": dict(value=1/3)}`, or lists of tuples with mixed entries,
|
|
404
|
+
`[("a", 1/3), ("b", 2/3)]`, will currently fall back to vanilla `doctest` literal
|
|
405
|
+
comparisons.
|
|
406
|
+
|
|
407
|
+
We stress that no matter how tricky or deeply nested the output is, the worst case
|
|
408
|
+
scenario is that the floating-point aware checker is not used. If you have a case where
|
|
409
|
+
`doctest` works correctly and `scipy_doctest` does not, please report it as a bug.
|
|
410
|
+
|
|
411
|
+
|
|
398
412
|
## Prior art and related work
|
|
399
413
|
|
|
400
414
|
- `pytest` provides some limited floating-point aware `NumericLiteralChecker`.
|
|
@@ -402,24 +416,30 @@ adding `--assert=plain` is reasonable.
|
|
|
402
416
|
- `pytest-doctestplus` plugin from the `AstroPy` project has similar goals.
|
|
403
417
|
The package is well established and widely used. From a user perspective, main
|
|
404
418
|
differences are: (i) `pytest-doctestplus` is more sensitive to formatting,
|
|
405
|
-
including whitespace
|
|
406
|
-
may start failing; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
419
|
+
including whitespace; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
407
420
|
directives.
|
|
408
421
|
|
|
409
|
-
This project takes a different approach:
|
|
410
|
-
|
|
411
|
-
|
|
422
|
+
This project takes a slightly different approach: we strive to make numeric comparisons
|
|
423
|
+
whitespace insensitive and automatic, without a need for explicit markup. For rare cases
|
|
424
|
+
which require additional configuration, we either keep it in the tool (thus out of
|
|
425
|
+
reader-visible docstrings), or provide human-readable markers (hence `# may vary`
|
|
426
|
+
not `# doctest +SKIP`).
|
|
427
|
+
Furthermore, in addition to plugging into `pytest`, we provide an API layer which closely
|
|
428
|
+
follows the `doctest` API. Essentially all aspects of doctesting are user-configurable.
|
|
429
|
+
|
|
430
|
+
- `xdoctest` package relies on a deeper rewrite of the standard-library `doctest`
|
|
431
|
+
functionality, and uses an AST-based analysis to parse code examples out of docstrings.
|
|
412
432
|
|
|
413
433
|
- `NumPy` and `SciPy` were using modified doctesting in their `refguide-check` utilities.
|
|
414
|
-
These utilities
|
|
434
|
+
These utilities were tightly coupled to their libraries, and have been reported
|
|
415
435
|
to be not easy to reason about, work with, and extend to other projects.
|
|
416
436
|
|
|
417
|
-
This project is mainly the core functionality of the modified
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
437
|
+
This project is mainly the core functionality of the modified `refguide-check` doctesting,
|
|
438
|
+
extracted to a separate package. We believe having it separate simplifies both
|
|
439
|
+
addressing the needs of these two packages, and adoption by other projects.
|
|
440
|
+
|
|
421
441
|
|
|
422
|
-
|
|
442
|
+
## Bug reports, feature requests and contributions
|
|
423
443
|
|
|
424
444
|
This package is work in progress. Contributions are most welcome!
|
|
425
445
|
Please don't hesitate to open an issue in the tracker or send a pull request.
|
|
@@ -79,7 +79,7 @@ class DTConfig:
|
|
|
79
79
|
Default is False.
|
|
80
80
|
pytest_extra_ignore : list
|
|
81
81
|
A list of names/modules to ignore when run under pytest plugin. This is
|
|
82
|
-
equivalent to using
|
|
82
|
+
equivalent to using ``--ignore=...`` cmdline switch.
|
|
83
83
|
pytest_extra_skip : dict
|
|
84
84
|
Names/modules to skip when run under pytest plugin. This is
|
|
85
85
|
equivalent to decorating the doctest with `@pytest.mark.skip` or adding
|
|
@@ -92,6 +92,12 @@ class DTConfig:
|
|
|
92
92
|
adding `# may vary` to the outputs of all examples.
|
|
93
93
|
Each key is a doctest name to skip, and the corresponding value is
|
|
94
94
|
a string. If not empty, the string value is used as the skip reason.
|
|
95
|
+
pytest_extra_requires : dict
|
|
96
|
+
Paths or functions to conditionally ignore unless requirements are met.
|
|
97
|
+
The format is ``{path/or/glob/pattern: requirement(s), full.func.name: requirement(s)}``,
|
|
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.
|
|
95
101
|
CheckerKlass : object, optional
|
|
96
102
|
The class for the Checker object. Must mimic the ``DTChecker`` API:
|
|
97
103
|
subclass the `doctest.OutputChecker` and make the constructor signature
|
|
@@ -125,6 +131,7 @@ class DTConfig:
|
|
|
125
131
|
pytest_extra_ignore=None,
|
|
126
132
|
pytest_extra_skip=None,
|
|
127
133
|
pytest_extra_xfail=None,
|
|
134
|
+
pytest_extra_requires=None,
|
|
128
135
|
):
|
|
129
136
|
### DTChecker configuration ###
|
|
130
137
|
self.CheckerKlass = CheckerKlass or DTChecker
|
|
@@ -217,6 +224,7 @@ class DTConfig:
|
|
|
217
224
|
self.pytest_extra_ignore = pytest_extra_ignore or []
|
|
218
225
|
self.pytest_extra_skip = pytest_extra_skip or {}
|
|
219
226
|
self.pytest_extra_xfail = pytest_extra_xfail or {}
|
|
227
|
+
self.pytest_extra_requires = pytest_extra_requires or {}
|
|
220
228
|
|
|
221
229
|
|
|
222
230
|
def try_convert_namedtuple(got):
|
|
@@ -422,10 +430,13 @@ class DTChecker(doctest.OutputChecker):
|
|
|
422
430
|
return False
|
|
423
431
|
|
|
424
432
|
if want_is_dict:
|
|
425
|
-
#
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
433
|
+
# split each dict into a pair of lists of keys and values, and retry
|
|
434
|
+
want_keys, want_values = f"{list(a_want.keys())}", f"{list(a_want.values())}"
|
|
435
|
+
got_keys, got_values = f"{list(a_got.keys())}", f"{list(a_got.values())}"
|
|
436
|
+
|
|
437
|
+
keys_match = self.check_output(want_keys, got_keys, optionflags)
|
|
438
|
+
values_match = self.check_output(want_values, got_values, optionflags)
|
|
439
|
+
return keys_match and values_match
|
|
429
440
|
|
|
430
441
|
# ... and defer to numpy
|
|
431
442
|
strict = self.config.strict_check
|
|
@@ -13,7 +13,7 @@ from _pytest.pathlib import import_path
|
|
|
13
13
|
|
|
14
14
|
from .impl import DTParser, DebugDTRunner
|
|
15
15
|
from .conftest import dt_config
|
|
16
|
-
from .util import np_errstate, matplotlib_make_nongui, temp_cwd
|
|
16
|
+
from .util import np_errstate, matplotlib_make_nongui, temp_cwd, is_req_satisfied
|
|
17
17
|
from .frontend import find_doctests
|
|
18
18
|
|
|
19
19
|
|
|
@@ -33,20 +33,20 @@ def pytest_addoption(parser):
|
|
|
33
33
|
# https://github.com/pytest-dev/pytest/discussions/13435
|
|
34
34
|
#
|
|
35
35
|
# Therefore, we add a new option, --doctest-only-doctests,
|
|
36
|
-
# which
|
|
36
|
+
# which was `true` by default in versions 1.x.
|
|
37
37
|
#
|
|
38
|
-
# In v2.0,
|
|
38
|
+
# In v2.0, the default is `false`, so that
|
|
39
39
|
#
|
|
40
40
|
# $ pytest --doctest-modules
|
|
41
41
|
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
42
|
+
# runs both doctests and unit tests, and the way to use the previous behavior
|
|
43
|
+
# (only run doctests, skip unit tests) is
|
|
44
44
|
#
|
|
45
45
|
# $ pytest --doctest-modules --doctest-only-doctests=true
|
|
46
46
|
#
|
|
47
47
|
group.addoption("--doctest-only-doctests",
|
|
48
48
|
action="store",
|
|
49
|
-
default="
|
|
49
|
+
default="false",
|
|
50
50
|
help="Whether to only collect doctests, or also collect unit tests, too.",
|
|
51
51
|
choices=("true", "false"),
|
|
52
52
|
dest="doctest_only_doctests"
|
|
@@ -71,9 +71,9 @@ def pytest_ignore_collect(collection_path, config):
|
|
|
71
71
|
This function is used to exclude the 'tests' directory and test modules when
|
|
72
72
|
the `--doctest-modules` option is used.
|
|
73
73
|
"""
|
|
74
|
-
# XXX:
|
|
75
|
-
# (consistent with vanilla pytest), and the way to retain the
|
|
76
|
-
#
|
|
74
|
+
# XXX: From v2.0, --doctest-modules means "run both doctests and unit tests",
|
|
75
|
+
# (consistent with vanilla pytest), and the way to retain the 1.x behavior
|
|
76
|
+
# is to add --doctest-only-doctests=true to the CLI command
|
|
77
77
|
if (
|
|
78
78
|
config.getoption("--doctest-modules") and
|
|
79
79
|
config.getoption("--doctest-only-doctests") == 'true'
|
|
@@ -82,10 +82,16 @@ def pytest_ignore_collect(collection_path, config):
|
|
|
82
82
|
if "tests" in path_str or "test_" in path_str:
|
|
83
83
|
return True
|
|
84
84
|
|
|
85
|
+
fnmatch_ex = _pytest.pathlib.fnmatch_ex
|
|
86
|
+
|
|
85
87
|
for entry in config.dt_config.pytest_extra_ignore:
|
|
86
|
-
if entry
|
|
88
|
+
if fnmatch_ex(entry, collection_path):
|
|
87
89
|
return True
|
|
88
90
|
|
|
91
|
+
for entry, reqs in config.dt_config.pytest_extra_requires.items():
|
|
92
|
+
if fnmatch_ex(entry, collection_path):
|
|
93
|
+
return not is_req_satisfied(reqs)
|
|
94
|
+
|
|
89
95
|
|
|
90
96
|
def is_private(item):
|
|
91
97
|
"""Decide if an DocTestItem `item` is private.
|
|
@@ -110,21 +116,26 @@ def _maybe_add_markers(item, config):
|
|
|
110
116
|
dt_config = config.dt_config
|
|
111
117
|
|
|
112
118
|
extra_skip = dt_config.pytest_extra_skip
|
|
113
|
-
skip_it
|
|
114
|
-
if skip_it:
|
|
119
|
+
if skip_it := item.name in extra_skip:
|
|
115
120
|
reason = extra_skip[item.name] or ''
|
|
116
121
|
item.add_marker(
|
|
117
122
|
pytest.mark.skip(reason=reason)
|
|
118
123
|
)
|
|
119
124
|
|
|
120
125
|
extra_xfail = dt_config.pytest_extra_xfail
|
|
121
|
-
fail_it
|
|
122
|
-
if fail_it:
|
|
126
|
+
if fail_it := item.name in extra_xfail:
|
|
123
127
|
reason = extra_xfail[item.name] or ''
|
|
124
128
|
item.add_marker(
|
|
125
129
|
pytest.mark.xfail(reason=reason)
|
|
126
130
|
)
|
|
127
131
|
|
|
132
|
+
extra_requires = dt_config.pytest_extra_requires
|
|
133
|
+
if req_str := extra_requires.get(item.name, None):
|
|
134
|
+
if not is_req_satisfied(req_str):
|
|
135
|
+
item.add_marker(
|
|
136
|
+
pytest.mark.skip(reason=f"requires {req_str}")
|
|
137
|
+
)
|
|
138
|
+
|
|
128
139
|
|
|
129
140
|
def pytest_collection_modifyitems(config, items):
|
|
130
141
|
"""
|
|
@@ -86,7 +86,7 @@ def dict_wrong_values_np():
|
|
|
86
86
|
"""
|
|
87
87
|
>>> import numpy as np
|
|
88
88
|
>>> dict(a=1, b=np.arange(3)/3)
|
|
89
|
-
{'a': 1, 'b': array([0, 0.335, 0.
|
|
89
|
+
{'a': 1, 'b': array([0, 0.335, 0.69])}
|
|
90
90
|
"""
|
|
91
91
|
|
|
92
92
|
|
|
@@ -94,5 +94,25 @@ def dict_nested_wrong_values_np():
|
|
|
94
94
|
"""
|
|
95
95
|
>>> import numpy as np
|
|
96
96
|
>>> dict(a=1, b=dict(blurb=np.arange(3)/3))
|
|
97
|
-
{'a': 1, 'b': {'blurb': array([0, 0.335, 0.
|
|
97
|
+
{'a': 1, 'b': {'blurb': array([0, 0.335, 0.69])}}
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# This is an XFAIL
|
|
102
|
+
# Currently, checking nested dicts falls back to vanilla doctest
|
|
103
|
+
# When this is fixed, move this case to module_cases.py
|
|
104
|
+
def dict_nested_needs_numeric_comparison():
|
|
105
|
+
"""
|
|
106
|
+
>>> import numpy as np
|
|
107
|
+
>>> dict(a=1, b=dict(blurb=np.arange(3)/3))
|
|
108
|
+
{'a': 1, 'b': {'blurb': array([0, 0.333, 0.667])}}
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# This is an XFAIL
|
|
113
|
+
# Nested sequences which contain strings fall back to vanilla doctest
|
|
114
|
+
def list_of_tuples():
|
|
115
|
+
"""
|
|
116
|
+
>>> [('a', 1/3), ('b', 2/3)]
|
|
117
|
+
[('a', 0.333), ('b', 0.667)]
|
|
98
118
|
"""
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
'func7', 'manip_printoptions', 'array_abbreviation'
|
|
4
|
-
]
|
|
5
|
-
|
|
1
|
+
""" A collection of test cases.
|
|
2
|
+
"""
|
|
6
3
|
|
|
7
4
|
def func():
|
|
8
5
|
"""
|
|
@@ -249,14 +246,31 @@ def two_dicts():
|
|
|
249
246
|
>>> import numpy as np
|
|
250
247
|
>>> dict(a=0, b=1)
|
|
251
248
|
{'a': 0, 'b': 1}
|
|
249
|
+
>>> {'a': 1/3, 'b': 2/3}
|
|
250
|
+
{'a': 0.333, 'b': 0.667}
|
|
252
251
|
>>> {'a': 0., 'b': np.arange(3) / 3 }
|
|
253
|
-
{'a': 0.0, 'b': array([0, 0.
|
|
252
|
+
{'a': 0.0, 'b': array([0, 0.333, 0.667])}
|
|
254
253
|
"""
|
|
255
254
|
|
|
256
255
|
def nested_dicts():
|
|
256
|
+
# Note that we need to give all digits: checking nested dictionaries
|
|
257
|
+
# currently fall back to vanilla doctest.
|
|
258
|
+
# cf failure_cases.py::dict_nested_needs_numeric_comparison
|
|
257
259
|
"""
|
|
258
260
|
>>> import numpy as np
|
|
259
261
|
>>> {'a': 1.0, 'b': dict(blurb=np.arange(3)/3)}
|
|
260
262
|
{'a': 1.0, 'b': {'blurb': array([0, 0.33333333, 0.66666667])}}
|
|
261
263
|
"""
|
|
262
264
|
|
|
265
|
+
|
|
266
|
+
def list_of_tuples_numeric():
|
|
267
|
+
# cf failure_cases.py::list_of_tuples --- string entries preclude numeric comparisons
|
|
268
|
+
"""
|
|
269
|
+
>>> [(1, 1/3), (2, 2/3)]
|
|
270
|
+
[(1, 0.333), (2, 0.667)]
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# This is used by test_testmod.py::test_public_object_discovery
|
|
275
|
+
# While in test we only need __all__ to be not empty, let's make it correct, too.
|
|
276
|
+
__all__ = [x for x in vars().keys() if not x.startswith("_")]
|
|
@@ -117,6 +117,15 @@ def test_tuple_and_list():
|
|
|
117
117
|
config=config)
|
|
118
118
|
assert res.failed == 2
|
|
119
119
|
|
|
120
|
+
|
|
121
|
+
def test_list_of_tuples():
|
|
122
|
+
config = DTConfig()
|
|
123
|
+
res, _ = _testmod(failure_cases,
|
|
124
|
+
strategy=[failure_cases.list_of_tuples],
|
|
125
|
+
config=config)
|
|
126
|
+
assert res.failed == 1
|
|
127
|
+
|
|
128
|
+
|
|
120
129
|
def test_dict():
|
|
121
130
|
config = DTConfig()
|
|
122
131
|
res, _ = _testmod(failure_cases,
|
|
@@ -125,9 +134,11 @@ def test_dict():
|
|
|
125
134
|
failure_cases.dict_wrong_keys,
|
|
126
135
|
failure_cases.dict_wrong_values,
|
|
127
136
|
failure_cases.dict_wrong_values_np,
|
|
128
|
-
failure_cases.dict_nested_wrong_values_np
|
|
137
|
+
failure_cases.dict_nested_wrong_values_np,
|
|
138
|
+
failure_cases.dict_nested_needs_numeric_comparison,
|
|
139
|
+
],
|
|
129
140
|
config=config)
|
|
130
|
-
assert res.failed ==
|
|
141
|
+
assert res.failed == 7
|
|
131
142
|
|
|
132
143
|
|
|
133
144
|
@pytest.mark.parametrize('strict, num_fails', [(True, 1), (False, 0)])
|
|
@@ -10,6 +10,10 @@ import tempfile
|
|
|
10
10
|
import inspect
|
|
11
11
|
from contextlib import contextmanager
|
|
12
12
|
|
|
13
|
+
from typing import Sequence
|
|
14
|
+
|
|
15
|
+
from importlib.metadata import version as get_version, PackageNotFoundError
|
|
16
|
+
from packaging.requirements import Requirement
|
|
13
17
|
|
|
14
18
|
@contextmanager
|
|
15
19
|
def matplotlib_make_nongui():
|
|
@@ -255,6 +259,20 @@ def get_public_objects(module, skiplist=None):
|
|
|
255
259
|
return (items, names), failures
|
|
256
260
|
|
|
257
261
|
|
|
262
|
+
def is_req_satisfied(req_strs: str | Sequence[str]) -> bool:
|
|
263
|
+
""" Check if all PEP 508-compliant requirement(s) are satisfied or not.
|
|
264
|
+
"""
|
|
265
|
+
req_strs = [req_strs] if isinstance(req_strs, str) else req_strs
|
|
266
|
+
reqs = [Requirement(req_str) for req_str in req_strs]
|
|
267
|
+
if any(req.marker is not None for req in reqs):
|
|
268
|
+
msg = r"Markers not supported in `pytest_extra_requires`"
|
|
269
|
+
raise NotImplementedError(msg)
|
|
270
|
+
try:
|
|
271
|
+
return all(get_version(req.name) in req.specifier for req in reqs)
|
|
272
|
+
except PackageNotFoundError:
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
|
|
258
276
|
# XXX: not used ATM
|
|
259
277
|
modules = []
|
|
260
278
|
def generate_log(module, test):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scipy_doctest-1.8.0 → scipy_doctest-2.0.0}/scipy_doctest/tests/test_pytest_configuration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|