scipy-doctest 1.1__tar.gz → 1.3__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.1 → scipy_doctest-1.3}/PKG-INFO +133 -107
- {scipy_doctest-1.1 → scipy_doctest-1.3}/README.md +130 -104
- {scipy_doctest-1.1 → scipy_doctest-1.3}/pyproject.toml +1 -1
- scipy_doctest-1.3/scipy_doctest/__init__.py +22 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/frontend.py +5 -2
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/impl.py +43 -3
- scipy_doctest-1.3/scipy_doctest/tests/failure_cases.py +47 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/module_cases.py +42 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_testmod.py +18 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/util.py +2 -2
- scipy_doctest-1.1/scipy_doctest/__init__.py +0 -12
- scipy_doctest-1.1/scipy_doctest/tests/failure_cases.py +0 -17
- {scipy_doctest-1.1 → scipy_doctest-1.3}/LICENSE +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/__main__.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/conftest.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/plugin.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/__init__.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/failure_cases_2.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/finder_cases.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/local_file.txt +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/local_file_cases.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/octave_a.mat +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/stopwords_cases.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_finder.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_parser.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_pytest_configuration.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_runner.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_skipmarkers.py +0 -0
- {scipy_doctest-1.1 → scipy_doctest-1.3}/scipy_doctest/tests/test_testfile.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scipy_doctest
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.3
|
|
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
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -14,11 +14,20 @@ Requires-Dist: numpy>=1.19.5
|
|
|
14
14
|
Requires-Dist: pytest
|
|
15
15
|
Requires-Dist: scipy ; extra == "test"
|
|
16
16
|
Requires-Dist: matplotlib ; extra == "test"
|
|
17
|
-
Project-URL: Home, https://github.com/
|
|
17
|
+
Project-URL: Home, https://github.com/scipy/scipy_doctest
|
|
18
18
|
Provides-Extra: test
|
|
19
19
|
|
|
20
20
|
# Floating-point aware, human readable, numpy-compatible doctesting.
|
|
21
21
|
|
|
22
|
+
## TL;DR
|
|
23
|
+
|
|
24
|
+
This project extends the standard library `doctest` module to allow flexibility
|
|
25
|
+
and easy customization of finding, parsing and checking code examples in
|
|
26
|
+
documentation.
|
|
27
|
+
|
|
28
|
+
Can be used either as drop-in `doctest` replacement or through the `pytest`
|
|
29
|
+
integration. Uses a floating-point aware doctest checker by default.
|
|
30
|
+
|
|
22
31
|
## Motivation and scope
|
|
23
32
|
|
|
24
33
|
Having examples in the documentation is great. Having wrong examples in the
|
|
@@ -91,8 +100,8 @@ the output. Thus the example source needs to be valid python code still.
|
|
|
91
100
|
## Install and test
|
|
92
101
|
|
|
93
102
|
```
|
|
94
|
-
$ pip install -
|
|
95
|
-
$ pytest --pyargs
|
|
103
|
+
$ pip install scipy-doctest
|
|
104
|
+
$ pytest --pyargs scipy_doctest
|
|
96
105
|
```
|
|
97
106
|
|
|
98
107
|
## Usage
|
|
@@ -103,14 +112,61 @@ or nearly so.
|
|
|
103
112
|
|
|
104
113
|
The other layer is the `pytest` plugin.
|
|
105
114
|
|
|
115
|
+
### Run doctests via pytest
|
|
116
|
+
|
|
117
|
+
To run doctests on your package or project, follow these steps:
|
|
118
|
+
|
|
119
|
+
1. **Install the plugin**
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install scipy-doctest
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
2. **Register or load the plugin**
|
|
126
|
+
|
|
127
|
+
Next, you need to register or load the pytest plugin within your test module or `conftest.py` file.
|
|
128
|
+
|
|
129
|
+
To do this, add the following line of code:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# In your conftest.py file or test module
|
|
133
|
+
|
|
134
|
+
pytest_plugins = "scipy_doctest"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
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
|
+
|
|
139
|
+
3. **Run doctests**
|
|
140
|
+
|
|
141
|
+
Once the plugin is registered, run the doctests by executing the following command:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
$ python -m pytest --doctest-modules
|
|
145
|
+
```
|
|
146
|
+
or
|
|
147
|
+
```bash
|
|
148
|
+
$ pytest --pyargs <your-package> --doctest-modules
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
By default, all doctests are collected. To only collect public objects, `strategy="api"`,
|
|
152
|
+
use the command flag
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
See [More fine-grained control](https://github.com/scipy/scipy_doctest#More-fine-grained-control) section
|
|
159
|
+
for details on how to customize the behavior.
|
|
160
|
+
|
|
106
161
|
|
|
107
162
|
### Basic usage
|
|
108
163
|
|
|
164
|
+
The use of `pytest` is optional, and you can use the `doctest` layer API.
|
|
109
165
|
For example,
|
|
110
166
|
|
|
111
167
|
```
|
|
112
168
|
>>> from scipy import linalg
|
|
113
|
-
>>> from
|
|
169
|
+
>>> from scipy_doctest import testmod
|
|
114
170
|
>>> res, hist = testmod(linalg, strategy='api')
|
|
115
171
|
>>> res
|
|
116
172
|
TestResults(failed=0, attempted=764)
|
|
@@ -122,11 +178,12 @@ For more details, see the `testmod` docstring. Other useful functions are
|
|
|
122
178
|
`find_doctests`, `run_docstring_examples` and `testfile` (the latter two mimic
|
|
123
179
|
the behavior of the eponymous functions of the `doctest` module).
|
|
124
180
|
|
|
125
|
-
|
|
181
|
+
|
|
182
|
+
### Command-line interface
|
|
126
183
|
|
|
127
184
|
There is a basic CLI, which also mimics that of the `doctest` module:
|
|
128
185
|
```
|
|
129
|
-
$ python -m
|
|
186
|
+
$ python -m scipy_doctest foo.py
|
|
130
187
|
```
|
|
131
188
|
|
|
132
189
|
Note that, just like `$ python -m doctest foo.py`, this may
|
|
@@ -134,11 +191,13 @@ fail if `foo.py` is a part of a package due to package imports.
|
|
|
134
191
|
|
|
135
192
|
Text files can also be CLI-checked:
|
|
136
193
|
```
|
|
137
|
-
$ python -m
|
|
194
|
+
$ python -m scipy_doctest bar.rst
|
|
138
195
|
```
|
|
139
196
|
|
|
197
|
+
Notice that the command-line usage only uses the default `DTConfig` settings.
|
|
198
|
+
|
|
140
199
|
|
|
141
|
-
|
|
200
|
+
## More fine-grained control
|
|
142
201
|
|
|
143
202
|
More fine-grained control of the functionality is available via the following
|
|
144
203
|
classes
|
|
@@ -157,134 +216,104 @@ configuration is simply creating an instance, overriding an attribute and
|
|
|
157
216
|
passing the instance to `testmod` or constructors of `DT*` objects. Defaults
|
|
158
217
|
are provided, based on a long-term usage in SciPy.
|
|
159
218
|
|
|
219
|
+
See the [DTConfig docstring](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/impl.py#L24)
|
|
220
|
+
for the full set of attributes that allow you to fine-tune your doctesting experience.
|
|
160
221
|
|
|
161
|
-
|
|
222
|
+
To set any of these attributes, create an instance of `DTConfig` and assign the attributes
|
|
223
|
+
in a usual way.
|
|
162
224
|
|
|
163
|
-
|
|
225
|
+
If using the pytest plugin, it is convenient to use the default instance, which
|
|
226
|
+
is predefined in `scipy_doctest/conftest.py`. This instance will be automatically
|
|
227
|
+
passed around via an
|
|
228
|
+
[attribute of pytest's `Config` object](https://github.com/scipy/scipy_doctest/blob/58ff06a837b7bff1dbac6560013fc6fd07952ae2/scipy_doctest/plugin.py#L39).
|
|
164
229
|
|
|
165
|
-
|
|
230
|
+
### Examples
|
|
166
231
|
|
|
167
|
-
|
|
168
|
-
|
|
232
|
+
```
|
|
233
|
+
dt_config = DTConfig()
|
|
234
|
+
```
|
|
169
235
|
|
|
170
|
-
|
|
236
|
+
or, if using pytest,
|
|
171
237
|
|
|
172
|
-
```
|
|
173
|
-
|
|
238
|
+
```python
|
|
239
|
+
from scipy_doctest.conftest import dt_config # a DTConfig instance with default settings
|
|
174
240
|
```
|
|
175
241
|
|
|
176
|
-
|
|
242
|
+
and then
|
|
177
243
|
|
|
178
|
-
|
|
179
|
-
|
|
244
|
+
```
|
|
245
|
+
dt_config.rndm_markers = {'# unintialized'}
|
|
180
246
|
|
|
181
|
-
|
|
247
|
+
dt_config.stopwords = {'plt.', 'plt.hist', 'plt.show'}
|
|
182
248
|
|
|
183
|
-
|
|
249
|
+
dt_config.local_resources = {
|
|
250
|
+
'scipy_doctest.tests.local_file_cases.local_files': ['scipy_doctest/tests/local_file.txt'],
|
|
251
|
+
'scipy_doctest.tests.local_file_cases.sio': ['scipy_doctest/tests/octave_a.mat']
|
|
252
|
+
}
|
|
184
253
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
254
|
+
dt_config.skiplist = {
|
|
255
|
+
'scipy.special.sinc',
|
|
256
|
+
'scipy.misc.who',
|
|
257
|
+
'scipy.optimize.show_options'
|
|
258
|
+
}
|
|
188
259
|
```
|
|
189
260
|
|
|
190
|
-
|
|
261
|
+
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.
|
|
191
262
|
|
|
192
|
-
```bash
|
|
193
|
-
python dev.py test --doctests -s cluster
|
|
194
|
-
```
|
|
195
263
|
|
|
196
|
-
|
|
264
|
+
#### Alternative Checkers
|
|
197
265
|
|
|
198
|
-
|
|
266
|
+
By default, we use the floating-point aware `DTChecker`. If you want to use an
|
|
267
|
+
alternative checker, all you need to do is to define the corresponding class,
|
|
268
|
+
and add an attribute to the `DTConfig` instance. For example,
|
|
199
269
|
|
|
200
|
-
1. **Install the plugin**
|
|
201
270
|
|
|
202
|
-
```
|
|
203
|
-
|
|
271
|
+
```
|
|
272
|
+
class VanillaOutputChecker(doctest.OutputChecker):
|
|
273
|
+
"""doctest.OutputChecker to drop in for DTChecker.
|
|
274
|
+
|
|
275
|
+
LSP break: OutputChecker does not have __init__,
|
|
276
|
+
here we add it to agree with DTChecker.
|
|
277
|
+
"""
|
|
278
|
+
def __init__(self, config):
|
|
279
|
+
pass
|
|
204
280
|
```
|
|
205
281
|
|
|
206
|
-
|
|
282
|
+
and
|
|
207
283
|
|
|
208
|
-
|
|
284
|
+
```
|
|
285
|
+
dt_config = DTConfig()
|
|
286
|
+
dt_config.CheckerKlass = VanillaOutputChecker
|
|
287
|
+
```
|
|
209
288
|
|
|
210
|
-
|
|
289
|
+
See [a pytest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/tests/test_pytest_configuration.py#L63)
|
|
290
|
+
and [a doctest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/tests/test_runner.py#L94)
|
|
291
|
+
for more details.
|
|
211
292
|
|
|
212
|
-
```python
|
|
213
|
-
# In your conftest.py file or test module
|
|
214
293
|
|
|
215
|
-
|
|
216
|
-
```
|
|
294
|
+
### The SciPy Doctest Pytest Plugin
|
|
217
295
|
|
|
218
|
-
|
|
296
|
+
The pytest plugin enables the use of `scipy_doctest` tools to perform doctests.
|
|
219
297
|
|
|
220
|
-
|
|
298
|
+
Follow the given instructions to utilize the pytest plugin for doctesting.
|
|
221
299
|
|
|
222
|
-
|
|
300
|
+
### NumPy and SciPy wrappers
|
|
223
301
|
|
|
224
|
-
4. **Run doctests**
|
|
225
302
|
|
|
226
|
-
|
|
303
|
+
NumPy wraps `scipy-doctest` with the `spin` command
|
|
227
304
|
|
|
228
|
-
```bash
|
|
229
|
-
$ python -m pytest --doctest-modules
|
|
230
305
|
```
|
|
231
|
-
|
|
232
|
-
```bash
|
|
233
|
-
$ pytest --pyargs <your-package> --doctest-modules
|
|
306
|
+
$ spin check-docs
|
|
234
307
|
```
|
|
235
308
|
|
|
236
|
-
|
|
237
|
-
use the command flag
|
|
309
|
+
SciPy wraps `scipy-doctest` with custom `dev.py` commands:
|
|
238
310
|
|
|
239
|
-
```bash
|
|
240
|
-
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
241
311
|
```
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
[DTConfig](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/impl.py#L16) offers a variety of attributes that allow you to fine-tune your doctesting experience.
|
|
246
|
-
|
|
247
|
-
These attributes include:
|
|
248
|
-
1. **default_namespace (dict):** Defines the namespace in which examples are executed.
|
|
249
|
-
2. **check_namespace (dict):** Specifies the namespace for conducting checks.
|
|
250
|
-
3. **rndm_markers (set):** Provides additional directives which act like `# doctest: + SKIP`.
|
|
251
|
-
4. **atol (float) and rtol (float):** Sets absolute and relative tolerances for validating doctest examples.
|
|
252
|
-
Specifically, it governs the check using `np.allclose(want, got, atol=atol, rtol=rtol)`.
|
|
253
|
-
5. **optionflags (int):** These are doctest option flags.
|
|
254
|
-
The default setting includes `NORMALIZE_WHITESPACE` | `ELLIPSIS` | `IGNORE_EXCEPTION_DETAIL`.
|
|
255
|
-
6. **stopwords (set):** If an example contains any of these stopwords, the output is not checked (though the source's validity is still assessed).
|
|
256
|
-
7. **pseudocode (list):** Lists strings that, when found in an example, result in no doctesting. This resembles the `# doctest +SKIP` directive and is useful for pseudocode blocks or similar cases.
|
|
257
|
-
8. **skiplist (set):** A list of names of objects with docstrings known to fail doctesting and are intentionally excluded from testing.
|
|
258
|
-
9. **user_context_mgr:** A context manager for running tests.
|
|
259
|
-
Typically, it is entered for each DocTest (especially in API documentation), ensuring proper testing isolation.
|
|
260
|
-
10. **local_resources (dict):** Specifies local files needed for specific tests. The format is `{test.name: list-of-files}`. File paths are relative to the path of `test.filename`.
|
|
261
|
-
11. **parse_namedtuples (bool):** Determines whether to perform a literal comparison (e.g., `TTestResult(pvalue=0.9, statistic=42)`) or extract and compare the tuple values (e.g., `(0.9, 42)`). The default is `True`.
|
|
262
|
-
12. **nameerror_after_exception (bool):** Controls whether subsequent examples in the same test, after one has failed, may raise spurious NameErrors. Set to `True` if you want to observe these errors or if your test is expected to raise NameErrors. The default is `False`.
|
|
263
|
-
|
|
264
|
-
To set any of these attributes, create an instance of `DTConfig` called `dt_config`.
|
|
265
|
-
This instance is already set as an [attribute of pytest's `Config` object](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/plugin.py#L27).
|
|
266
|
-
|
|
267
|
-
**Example:**
|
|
268
|
-
|
|
269
|
-
```python
|
|
270
|
-
dt_config = DTConfig()
|
|
271
|
-
dt_config.stopwords = {'plt.', '.hist', '.show'}
|
|
272
|
-
dt_config.local_resources = {
|
|
273
|
-
'scpdt.tests.local_file_cases.local_files': ['scpdt/tests/local_file.txt'],
|
|
274
|
-
'scpdt.tests.local_file_cases.sio': ['scpdt/tests/octave_a.mat']
|
|
275
|
-
}
|
|
276
|
-
dt_config.skiplist = {
|
|
277
|
-
'scipy.special.sinc',
|
|
278
|
-
'scipy.misc.who',
|
|
279
|
-
'scipy.optimize.show_options'
|
|
280
|
-
}
|
|
312
|
+
$ python dev.py smoke-docs # check docstrings
|
|
313
|
+
$ python dev.py smoke-tutorials # ReST user guide tutorials
|
|
281
314
|
```
|
|
282
315
|
|
|
283
|
-
If you don't set these attributes, the [default settings](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/impl.py#L73) of the attributes are used.
|
|
284
|
-
|
|
285
|
-
By following these steps, you will be able to effectively use the Scpdt pytest plugin for doctests in your Python projects.
|
|
286
316
|
|
|
287
|
-
Happy testing!
|
|
288
317
|
|
|
289
318
|
## Rough edges and sharp bits
|
|
290
319
|
|
|
@@ -309,7 +338,7 @@ instead of `$ pytest --pyargs scipy`.
|
|
|
309
338
|
If push comes to shove, you may try using the magic env variable:
|
|
310
339
|
` PY_IGNORE_IMPORTMISMATCH=1 pytest ...`,
|
|
311
340
|
however the need usually indicates an issue with the package itself.
|
|
312
|
-
(see [gh-107](https://github.com/
|
|
341
|
+
(see [gh-107](https://github.com/scipy/scipy_doctest/pull/107) for an example).
|
|
313
342
|
|
|
314
343
|
- *Optional dependencies are not that optional*
|
|
315
344
|
|
|
@@ -318,7 +347,7 @@ being optional. So you either guard the imports in doctests (yikes!), or
|
|
|
318
347
|
the collections fails if dependencies are not available.
|
|
319
348
|
|
|
320
349
|
The solution is to explicitly `--ignore` the paths to modules with optionals.
|
|
321
|
-
(or use `DTConfig.pytest_extra_ignore` list):
|
|
350
|
+
(or, equivalently, use `DTConfig.pytest_extra_ignore` list):
|
|
322
351
|
|
|
323
352
|
```
|
|
324
353
|
$ pytest --ignore=/build-install/lib/scipy/python3.10/site-packages/scipy/_lib ...
|
|
@@ -362,20 +391,17 @@ leads to
|
|
|
362
391
|
differences are: (i) `pytest-doctestplus` is more sensitive to formatting,
|
|
363
392
|
including whitespace---thus if numpy tweaks its output formatting, doctests
|
|
364
393
|
may start failing; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
365
|
-
directives
|
|
366
|
-
coupled to `pytest`. It thus needs to follow `pytest` releases, and
|
|
367
|
-
some maintenance work may be required to adapt when `pytest` publishes a new
|
|
368
|
-
release.
|
|
394
|
+
directives.
|
|
369
395
|
|
|
370
396
|
This project takes a different approach: in addition to plugging into `pytest`,
|
|
371
397
|
we closely follow the `doctest` API and implementation, which are naturally
|
|
372
398
|
way more stable then `pytest`.
|
|
373
399
|
|
|
374
|
-
- `NumPy` and `SciPy`
|
|
400
|
+
- `NumPy` and `SciPy` were using modified doctesting in their `refguide-check` utilities.
|
|
375
401
|
These utilities are tightly coupled to their libraries, and have been reported
|
|
376
402
|
to be not easy to reason about, work with, and extend to other projects.
|
|
377
403
|
|
|
378
|
-
This project is
|
|
404
|
+
This project is mainly the core functionality of the modified
|
|
379
405
|
`refguide-check` doctesting, extracted to a separate package.
|
|
380
406
|
We believe having it separate simplifies both addressing the needs of these
|
|
381
407
|
two packages, and potential adoption by other projects.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Floating-point aware, human readable, numpy-compatible doctesting.
|
|
2
2
|
|
|
3
|
+
## TL;DR
|
|
4
|
+
|
|
5
|
+
This project extends the standard library `doctest` module to allow flexibility
|
|
6
|
+
and easy customization of finding, parsing and checking code examples in
|
|
7
|
+
documentation.
|
|
8
|
+
|
|
9
|
+
Can be used either as drop-in `doctest` replacement or through the `pytest`
|
|
10
|
+
integration. Uses a floating-point aware doctest checker by default.
|
|
11
|
+
|
|
3
12
|
## Motivation and scope
|
|
4
13
|
|
|
5
14
|
Having examples in the documentation is great. Having wrong examples in the
|
|
@@ -72,8 +81,8 @@ the output. Thus the example source needs to be valid python code still.
|
|
|
72
81
|
## Install and test
|
|
73
82
|
|
|
74
83
|
```
|
|
75
|
-
$ pip install -
|
|
76
|
-
$ pytest --pyargs
|
|
84
|
+
$ pip install scipy-doctest
|
|
85
|
+
$ pytest --pyargs scipy_doctest
|
|
77
86
|
```
|
|
78
87
|
|
|
79
88
|
## Usage
|
|
@@ -84,14 +93,61 @@ or nearly so.
|
|
|
84
93
|
|
|
85
94
|
The other layer is the `pytest` plugin.
|
|
86
95
|
|
|
96
|
+
### Run doctests via pytest
|
|
97
|
+
|
|
98
|
+
To run doctests on your package or project, follow these steps:
|
|
99
|
+
|
|
100
|
+
1. **Install the plugin**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install scipy-doctest
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
2. **Register or load the plugin**
|
|
107
|
+
|
|
108
|
+
Next, you need to register or load the pytest plugin within your test module or `conftest.py` file.
|
|
109
|
+
|
|
110
|
+
To do this, add the following line of code:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# In your conftest.py file or test module
|
|
114
|
+
|
|
115
|
+
pytest_plugins = "scipy_doctest"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
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.
|
|
119
|
+
|
|
120
|
+
3. **Run doctests**
|
|
121
|
+
|
|
122
|
+
Once the plugin is registered, run the doctests by executing the following command:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
$ python -m pytest --doctest-modules
|
|
126
|
+
```
|
|
127
|
+
or
|
|
128
|
+
```bash
|
|
129
|
+
$ pytest --pyargs <your-package> --doctest-modules
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
By default, all doctests are collected. To only collect public objects, `strategy="api"`,
|
|
133
|
+
use the command flag
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
See [More fine-grained control](https://github.com/scipy/scipy_doctest#More-fine-grained-control) section
|
|
140
|
+
for details on how to customize the behavior.
|
|
141
|
+
|
|
87
142
|
|
|
88
143
|
### Basic usage
|
|
89
144
|
|
|
145
|
+
The use of `pytest` is optional, and you can use the `doctest` layer API.
|
|
90
146
|
For example,
|
|
91
147
|
|
|
92
148
|
```
|
|
93
149
|
>>> from scipy import linalg
|
|
94
|
-
>>> from
|
|
150
|
+
>>> from scipy_doctest import testmod
|
|
95
151
|
>>> res, hist = testmod(linalg, strategy='api')
|
|
96
152
|
>>> res
|
|
97
153
|
TestResults(failed=0, attempted=764)
|
|
@@ -103,11 +159,12 @@ For more details, see the `testmod` docstring. Other useful functions are
|
|
|
103
159
|
`find_doctests`, `run_docstring_examples` and `testfile` (the latter two mimic
|
|
104
160
|
the behavior of the eponymous functions of the `doctest` module).
|
|
105
161
|
|
|
106
|
-
|
|
162
|
+
|
|
163
|
+
### Command-line interface
|
|
107
164
|
|
|
108
165
|
There is a basic CLI, which also mimics that of the `doctest` module:
|
|
109
166
|
```
|
|
110
|
-
$ python -m
|
|
167
|
+
$ python -m scipy_doctest foo.py
|
|
111
168
|
```
|
|
112
169
|
|
|
113
170
|
Note that, just like `$ python -m doctest foo.py`, this may
|
|
@@ -115,11 +172,13 @@ fail if `foo.py` is a part of a package due to package imports.
|
|
|
115
172
|
|
|
116
173
|
Text files can also be CLI-checked:
|
|
117
174
|
```
|
|
118
|
-
$ python -m
|
|
175
|
+
$ python -m scipy_doctest bar.rst
|
|
119
176
|
```
|
|
120
177
|
|
|
178
|
+
Notice that the command-line usage only uses the default `DTConfig` settings.
|
|
179
|
+
|
|
121
180
|
|
|
122
|
-
|
|
181
|
+
## More fine-grained control
|
|
123
182
|
|
|
124
183
|
More fine-grained control of the functionality is available via the following
|
|
125
184
|
classes
|
|
@@ -138,134 +197,104 @@ configuration is simply creating an instance, overriding an attribute and
|
|
|
138
197
|
passing the instance to `testmod` or constructors of `DT*` objects. Defaults
|
|
139
198
|
are provided, based on a long-term usage in SciPy.
|
|
140
199
|
|
|
200
|
+
See the [DTConfig docstring](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/impl.py#L24)
|
|
201
|
+
for the full set of attributes that allow you to fine-tune your doctesting experience.
|
|
141
202
|
|
|
142
|
-
|
|
203
|
+
To set any of these attributes, create an instance of `DTConfig` and assign the attributes
|
|
204
|
+
in a usual way.
|
|
143
205
|
|
|
144
|
-
|
|
206
|
+
If using the pytest plugin, it is convenient to use the default instance, which
|
|
207
|
+
is predefined in `scipy_doctest/conftest.py`. This instance will be automatically
|
|
208
|
+
passed around via an
|
|
209
|
+
[attribute of pytest's `Config` object](https://github.com/scipy/scipy_doctest/blob/58ff06a837b7bff1dbac6560013fc6fd07952ae2/scipy_doctest/plugin.py#L39).
|
|
145
210
|
|
|
146
|
-
|
|
211
|
+
### Examples
|
|
147
212
|
|
|
148
|
-
|
|
149
|
-
|
|
213
|
+
```
|
|
214
|
+
dt_config = DTConfig()
|
|
215
|
+
```
|
|
150
216
|
|
|
151
|
-
|
|
217
|
+
or, if using pytest,
|
|
152
218
|
|
|
153
|
-
```
|
|
154
|
-
|
|
219
|
+
```python
|
|
220
|
+
from scipy_doctest.conftest import dt_config # a DTConfig instance with default settings
|
|
155
221
|
```
|
|
156
222
|
|
|
157
|
-
|
|
223
|
+
and then
|
|
158
224
|
|
|
159
|
-
|
|
160
|
-
|
|
225
|
+
```
|
|
226
|
+
dt_config.rndm_markers = {'# unintialized'}
|
|
161
227
|
|
|
162
|
-
|
|
228
|
+
dt_config.stopwords = {'plt.', 'plt.hist', 'plt.show'}
|
|
163
229
|
|
|
164
|
-
|
|
230
|
+
dt_config.local_resources = {
|
|
231
|
+
'scipy_doctest.tests.local_file_cases.local_files': ['scipy_doctest/tests/local_file.txt'],
|
|
232
|
+
'scipy_doctest.tests.local_file_cases.sio': ['scipy_doctest/tests/octave_a.mat']
|
|
233
|
+
}
|
|
165
234
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
235
|
+
dt_config.skiplist = {
|
|
236
|
+
'scipy.special.sinc',
|
|
237
|
+
'scipy.misc.who',
|
|
238
|
+
'scipy.optimize.show_options'
|
|
239
|
+
}
|
|
169
240
|
```
|
|
170
241
|
|
|
171
|
-
|
|
242
|
+
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.
|
|
172
243
|
|
|
173
|
-
```bash
|
|
174
|
-
python dev.py test --doctests -s cluster
|
|
175
|
-
```
|
|
176
244
|
|
|
177
|
-
|
|
245
|
+
#### Alternative Checkers
|
|
178
246
|
|
|
179
|
-
|
|
247
|
+
By default, we use the floating-point aware `DTChecker`. If you want to use an
|
|
248
|
+
alternative checker, all you need to do is to define the corresponding class,
|
|
249
|
+
and add an attribute to the `DTConfig` instance. For example,
|
|
180
250
|
|
|
181
|
-
1. **Install the plugin**
|
|
182
251
|
|
|
183
|
-
```
|
|
184
|
-
|
|
252
|
+
```
|
|
253
|
+
class VanillaOutputChecker(doctest.OutputChecker):
|
|
254
|
+
"""doctest.OutputChecker to drop in for DTChecker.
|
|
255
|
+
|
|
256
|
+
LSP break: OutputChecker does not have __init__,
|
|
257
|
+
here we add it to agree with DTChecker.
|
|
258
|
+
"""
|
|
259
|
+
def __init__(self, config):
|
|
260
|
+
pass
|
|
185
261
|
```
|
|
186
262
|
|
|
187
|
-
|
|
263
|
+
and
|
|
188
264
|
|
|
189
|
-
|
|
265
|
+
```
|
|
266
|
+
dt_config = DTConfig()
|
|
267
|
+
dt_config.CheckerKlass = VanillaOutputChecker
|
|
268
|
+
```
|
|
190
269
|
|
|
191
|
-
|
|
270
|
+
See [a pytest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/tests/test_pytest_configuration.py#L63)
|
|
271
|
+
and [a doctest example](https://github.com/scipy/scipy_doctest/blob/main/scipy_doctest/tests/test_runner.py#L94)
|
|
272
|
+
for more details.
|
|
192
273
|
|
|
193
|
-
```python
|
|
194
|
-
# In your conftest.py file or test module
|
|
195
274
|
|
|
196
|
-
|
|
197
|
-
```
|
|
275
|
+
### The SciPy Doctest Pytest Plugin
|
|
198
276
|
|
|
199
|
-
|
|
277
|
+
The pytest plugin enables the use of `scipy_doctest` tools to perform doctests.
|
|
200
278
|
|
|
201
|
-
|
|
279
|
+
Follow the given instructions to utilize the pytest plugin for doctesting.
|
|
202
280
|
|
|
203
|
-
|
|
281
|
+
### NumPy and SciPy wrappers
|
|
204
282
|
|
|
205
|
-
4. **Run doctests**
|
|
206
283
|
|
|
207
|
-
|
|
284
|
+
NumPy wraps `scipy-doctest` with the `spin` command
|
|
208
285
|
|
|
209
|
-
```bash
|
|
210
|
-
$ python -m pytest --doctest-modules
|
|
211
286
|
```
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
$ pytest --pyargs <your-package> --doctest-modules
|
|
287
|
+
$ spin check-docs
|
|
215
288
|
```
|
|
216
289
|
|
|
217
|
-
|
|
218
|
-
use the command flag
|
|
290
|
+
SciPy wraps `scipy-doctest` with custom `dev.py` commands:
|
|
219
291
|
|
|
220
|
-
```bash
|
|
221
|
-
$ pytest --pyargs <your-package> --doctest-modules --doctest-collect=api
|
|
222
292
|
```
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
[DTConfig](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/impl.py#L16) offers a variety of attributes that allow you to fine-tune your doctesting experience.
|
|
227
|
-
|
|
228
|
-
These attributes include:
|
|
229
|
-
1. **default_namespace (dict):** Defines the namespace in which examples are executed.
|
|
230
|
-
2. **check_namespace (dict):** Specifies the namespace for conducting checks.
|
|
231
|
-
3. **rndm_markers (set):** Provides additional directives which act like `# doctest: + SKIP`.
|
|
232
|
-
4. **atol (float) and rtol (float):** Sets absolute and relative tolerances for validating doctest examples.
|
|
233
|
-
Specifically, it governs the check using `np.allclose(want, got, atol=atol, rtol=rtol)`.
|
|
234
|
-
5. **optionflags (int):** These are doctest option flags.
|
|
235
|
-
The default setting includes `NORMALIZE_WHITESPACE` | `ELLIPSIS` | `IGNORE_EXCEPTION_DETAIL`.
|
|
236
|
-
6. **stopwords (set):** If an example contains any of these stopwords, the output is not checked (though the source's validity is still assessed).
|
|
237
|
-
7. **pseudocode (list):** Lists strings that, when found in an example, result in no doctesting. This resembles the `# doctest +SKIP` directive and is useful for pseudocode blocks or similar cases.
|
|
238
|
-
8. **skiplist (set):** A list of names of objects with docstrings known to fail doctesting and are intentionally excluded from testing.
|
|
239
|
-
9. **user_context_mgr:** A context manager for running tests.
|
|
240
|
-
Typically, it is entered for each DocTest (especially in API documentation), ensuring proper testing isolation.
|
|
241
|
-
10. **local_resources (dict):** Specifies local files needed for specific tests. The format is `{test.name: list-of-files}`. File paths are relative to the path of `test.filename`.
|
|
242
|
-
11. **parse_namedtuples (bool):** Determines whether to perform a literal comparison (e.g., `TTestResult(pvalue=0.9, statistic=42)`) or extract and compare the tuple values (e.g., `(0.9, 42)`). The default is `True`.
|
|
243
|
-
12. **nameerror_after_exception (bool):** Controls whether subsequent examples in the same test, after one has failed, may raise spurious NameErrors. Set to `True` if you want to observe these errors or if your test is expected to raise NameErrors. The default is `False`.
|
|
244
|
-
|
|
245
|
-
To set any of these attributes, create an instance of `DTConfig` called `dt_config`.
|
|
246
|
-
This instance is already set as an [attribute of pytest's `Config` object](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/plugin.py#L27).
|
|
247
|
-
|
|
248
|
-
**Example:**
|
|
249
|
-
|
|
250
|
-
```python
|
|
251
|
-
dt_config = DTConfig()
|
|
252
|
-
dt_config.stopwords = {'plt.', '.hist', '.show'}
|
|
253
|
-
dt_config.local_resources = {
|
|
254
|
-
'scpdt.tests.local_file_cases.local_files': ['scpdt/tests/local_file.txt'],
|
|
255
|
-
'scpdt.tests.local_file_cases.sio': ['scpdt/tests/octave_a.mat']
|
|
256
|
-
}
|
|
257
|
-
dt_config.skiplist = {
|
|
258
|
-
'scipy.special.sinc',
|
|
259
|
-
'scipy.misc.who',
|
|
260
|
-
'scipy.optimize.show_options'
|
|
261
|
-
}
|
|
293
|
+
$ python dev.py smoke-docs # check docstrings
|
|
294
|
+
$ python dev.py smoke-tutorials # ReST user guide tutorials
|
|
262
295
|
```
|
|
263
296
|
|
|
264
|
-
If you don't set these attributes, the [default settings](https://github.com/ev-br/scpdt/blob/671083d65b54111770cee71c9bc790ac652d59ab/scpdt/impl.py#L73) of the attributes are used.
|
|
265
|
-
|
|
266
|
-
By following these steps, you will be able to effectively use the Scpdt pytest plugin for doctests in your Python projects.
|
|
267
297
|
|
|
268
|
-
Happy testing!
|
|
269
298
|
|
|
270
299
|
## Rough edges and sharp bits
|
|
271
300
|
|
|
@@ -290,7 +319,7 @@ instead of `$ pytest --pyargs scipy`.
|
|
|
290
319
|
If push comes to shove, you may try using the magic env variable:
|
|
291
320
|
` PY_IGNORE_IMPORTMISMATCH=1 pytest ...`,
|
|
292
321
|
however the need usually indicates an issue with the package itself.
|
|
293
|
-
(see [gh-107](https://github.com/
|
|
322
|
+
(see [gh-107](https://github.com/scipy/scipy_doctest/pull/107) for an example).
|
|
294
323
|
|
|
295
324
|
- *Optional dependencies are not that optional*
|
|
296
325
|
|
|
@@ -299,7 +328,7 @@ being optional. So you either guard the imports in doctests (yikes!), or
|
|
|
299
328
|
the collections fails if dependencies are not available.
|
|
300
329
|
|
|
301
330
|
The solution is to explicitly `--ignore` the paths to modules with optionals.
|
|
302
|
-
(or use `DTConfig.pytest_extra_ignore` list):
|
|
331
|
+
(or, equivalently, use `DTConfig.pytest_extra_ignore` list):
|
|
303
332
|
|
|
304
333
|
```
|
|
305
334
|
$ pytest --ignore=/build-install/lib/scipy/python3.10/site-packages/scipy/_lib ...
|
|
@@ -343,20 +372,17 @@ leads to
|
|
|
343
372
|
differences are: (i) `pytest-doctestplus` is more sensitive to formatting,
|
|
344
373
|
including whitespace---thus if numpy tweaks its output formatting, doctests
|
|
345
374
|
may start failing; (ii) there is still a need for `# doctest: +FLOAT_CMP`
|
|
346
|
-
directives
|
|
347
|
-
coupled to `pytest`. It thus needs to follow `pytest` releases, and
|
|
348
|
-
some maintenance work may be required to adapt when `pytest` publishes a new
|
|
349
|
-
release.
|
|
375
|
+
directives.
|
|
350
376
|
|
|
351
377
|
This project takes a different approach: in addition to plugging into `pytest`,
|
|
352
378
|
we closely follow the `doctest` API and implementation, which are naturally
|
|
353
379
|
way more stable then `pytest`.
|
|
354
380
|
|
|
355
|
-
- `NumPy` and `SciPy`
|
|
381
|
+
- `NumPy` and `SciPy` were using modified doctesting in their `refguide-check` utilities.
|
|
356
382
|
These utilities are tightly coupled to their libraries, and have been reported
|
|
357
383
|
to be not easy to reason about, work with, and extend to other projects.
|
|
358
384
|
|
|
359
|
-
This project is
|
|
385
|
+
This project is mainly the core functionality of the modified
|
|
360
386
|
`refguide-check` doctesting, extracted to a separate package.
|
|
361
387
|
We believe having it separate simplifies both addressing the needs of these
|
|
362
388
|
two packages, and potential adoption by other projects.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configurable, whitespace-insensitive, floating-point-aware doctest helpers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__version__ = "1.3"
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
# register internal modules with pytest; obscure errors galore otherwise
|
|
10
|
+
import pytest
|
|
11
|
+
pytest.register_assert_rewrite(
|
|
12
|
+
"scipy_doctest.conftest", "scipy_doctest.impl", "scipy_doctest.util",
|
|
13
|
+
"scipy_doctest.frontend", "scipy_doctest.plugin"
|
|
14
|
+
)
|
|
15
|
+
except ModuleNotFoundError:
|
|
16
|
+
# pytest is optional, so nothing to do
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
from .impl import DTChecker, DTFinder, DTParser, DTRunner, DebugDTRunner, DTConfig # noqa
|
|
21
|
+
from .frontend import testmod, testfile, find_doctests, run_docstring_examples # noqa
|
|
22
|
+
|
|
@@ -70,8 +70,11 @@ def find_doctests(module, strategy=None,
|
|
|
70
70
|
return tests
|
|
71
71
|
|
|
72
72
|
if strategy == "api":
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
|
|
74
|
+
with config.user_context_mgr():
|
|
75
|
+
# user_context_mgr may want to e.g. filter warnings on imports?
|
|
76
|
+
(items, names), failures = get_public_objects(module,
|
|
77
|
+
skiplist=config.skiplist)
|
|
75
78
|
if failures:
|
|
76
79
|
mesg = "\n".join([_[2] for _ in failures])
|
|
77
80
|
raise ValueError(mesg)
|
|
@@ -2,6 +2,7 @@ import re
|
|
|
2
2
|
import warnings
|
|
3
3
|
import doctest
|
|
4
4
|
from doctest import NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL
|
|
5
|
+
from itertools import zip_longest
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
|
|
@@ -90,6 +91,14 @@ class DTConfig:
|
|
|
90
91
|
adding `# may vary` to the outputs of all examples.
|
|
91
92
|
Each key is a doctest name to skip, and the corresponding value is
|
|
92
93
|
a string. If not empty, the string value is used as the skip reason.
|
|
94
|
+
CheckerKlass : object, optional
|
|
95
|
+
The class for the Checker object. Must mimic the ``DTChecker`` API:
|
|
96
|
+
subclass the `doctest.OutputChecker` and make the constructor signature
|
|
97
|
+
read ``__init__(self, config=None)``, where `config` is a ``DTConfig``
|
|
98
|
+
instance.
|
|
99
|
+
This class will be instantiated by ``DTRunner``.
|
|
100
|
+
Defaults to `DTChecker`.
|
|
101
|
+
|
|
93
102
|
"""
|
|
94
103
|
def __init__(self, *, # DTChecker configuration
|
|
95
104
|
CheckerKlass=None,
|
|
@@ -219,6 +228,30 @@ def try_convert_namedtuple(got):
|
|
|
219
228
|
return got_again
|
|
220
229
|
|
|
221
230
|
|
|
231
|
+
def try_convert_printed_array(got):
|
|
232
|
+
"""Printed arrays: reinsert commas.
|
|
233
|
+
"""
|
|
234
|
+
# a minimal version is `s_got = ", ".join(got[1:-1].split())`
|
|
235
|
+
# but it fails if there's a space after the opening bracket: "[ 0 1 2 ]"
|
|
236
|
+
# For 2D arrays, split into rows, drop spurious entries, then reassemble.
|
|
237
|
+
if not got.startswith('['):
|
|
238
|
+
return got
|
|
239
|
+
|
|
240
|
+
g1 = got[1:-1] # strip outer "[...]"-s
|
|
241
|
+
rows = [x for x in g1.split("[") if x]
|
|
242
|
+
rows2 = [", ".join(row.split()) for row in rows]
|
|
243
|
+
|
|
244
|
+
if got.startswith("[["):
|
|
245
|
+
# was a 2D array, restore the opening brackets in rows; XXX clean up
|
|
246
|
+
rows3 = ["[" + row for row in rows2]
|
|
247
|
+
else:
|
|
248
|
+
rows3 = rows2
|
|
249
|
+
|
|
250
|
+
# add back the outer brackets
|
|
251
|
+
s_got = "[" + ", ".join(rows3) + "]"
|
|
252
|
+
return s_got
|
|
253
|
+
|
|
254
|
+
|
|
222
255
|
def has_masked(got):
|
|
223
256
|
return 'masked_array' in got and '--' in got
|
|
224
257
|
|
|
@@ -280,8 +313,9 @@ class DTChecker(doctest.OutputChecker):
|
|
|
280
313
|
cond = (s_want.startswith("[") and s_want.endswith("]") and
|
|
281
314
|
s_got.startswith("[") and s_got.endswith("]"))
|
|
282
315
|
if cond:
|
|
283
|
-
s_want =
|
|
284
|
-
s_got =
|
|
316
|
+
s_want = try_convert_printed_array(s_want)
|
|
317
|
+
s_got = try_convert_printed_array(s_got)
|
|
318
|
+
|
|
285
319
|
return self.check_output(s_want, s_got, optionflags)
|
|
286
320
|
|
|
287
321
|
#handle array abbreviation for n-dimensional arrays, n >= 1
|
|
@@ -322,13 +356,19 @@ class DTChecker(doctest.OutputChecker):
|
|
|
322
356
|
else:
|
|
323
357
|
return self.check_output(want_again, got_again, optionflags)
|
|
324
358
|
|
|
359
|
+
# Validate data type if list or tuple
|
|
360
|
+
is_list_or_tuple = (isinstance(a_want, (list, tuple)) and
|
|
361
|
+
isinstance(a_got, (list, tuple)))
|
|
362
|
+
if is_list_or_tuple and type(a_want) is not type(a_got):
|
|
363
|
+
return False
|
|
364
|
+
|
|
325
365
|
# ... and defer to numpy
|
|
326
366
|
try:
|
|
327
367
|
return self._do_check(a_want, a_got)
|
|
328
368
|
except Exception:
|
|
329
369
|
# heterog tuple, eg (1, np.array([1., 2.]))
|
|
330
370
|
try:
|
|
331
|
-
return all(self._do_check(w, g) for w, g in
|
|
371
|
+
return all(self._do_check(w, g) for w, g in zip_longest(a_want, a_got))
|
|
332
372
|
except (TypeError, ValueError):
|
|
333
373
|
return False
|
|
334
374
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
__all__ = ['func9', 'func10', 'iterable_length_1', 'iterable_length_2',
|
|
2
|
+
'tuple_and_list_1', 'tuple_and_list_2']
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def func9():
|
|
6
|
+
"""
|
|
7
|
+
Wrong output.
|
|
8
|
+
>>> import numpy as np
|
|
9
|
+
>>> np.array([1, 2, 3])
|
|
10
|
+
array([2, 3, 4])
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def func10():
|
|
15
|
+
"""
|
|
16
|
+
NameError
|
|
17
|
+
>>> import numpy as np
|
|
18
|
+
>>> np.arraY([1, 2, 3])
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def iterable_length_1():
|
|
23
|
+
"""
|
|
24
|
+
>>> [1, 2, 3]
|
|
25
|
+
[1, 2, 3, 4]
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def iterable_length_2():
|
|
30
|
+
"""
|
|
31
|
+
>>> [1, 2, 3]
|
|
32
|
+
[1, 2]
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def tuple_and_list_1():
|
|
37
|
+
"""
|
|
38
|
+
>>> [0, 1, 2]
|
|
39
|
+
(0, 1, 2)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def tuple_and_list_2():
|
|
44
|
+
"""
|
|
45
|
+
>>> (0, 1, 2)
|
|
46
|
+
[0, 1, 2]
|
|
47
|
+
"""
|
|
@@ -44,6 +44,32 @@ def func3():
|
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def func_printed_arrays():
|
|
48
|
+
"""
|
|
49
|
+
Check various ways handling of printed arrays can go wrong.
|
|
50
|
+
|
|
51
|
+
>>> import numpy as np
|
|
52
|
+
>>> a = np.arange(8).reshape(2, 4) / 3
|
|
53
|
+
>>> print(a) # numpy 1.26.4
|
|
54
|
+
[[0. 0.33333333 0.66666667 1. ]
|
|
55
|
+
[1.33333333 1.66666667 2. 2.33333333]]
|
|
56
|
+
|
|
57
|
+
>>> print(a) # add spaces (older repr?)
|
|
58
|
+
[[ 0. 0.33333333 0.66666667 1. ]
|
|
59
|
+
[ 1.33333333 1.66666667 2. 2.33333333 ]]
|
|
60
|
+
|
|
61
|
+
Also check 1D arrays
|
|
62
|
+
>>> a1 = np.arange(3)
|
|
63
|
+
>>> print(a1)
|
|
64
|
+
[0 1 2]
|
|
65
|
+
>>> print(a1)
|
|
66
|
+
[ 0 1 2]
|
|
67
|
+
>>> print(a1)
|
|
68
|
+
[ 0 1 2 ]
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
47
73
|
def func4():
|
|
48
74
|
"""
|
|
49
75
|
Test `# may vary` markers : these should not break doctests (but the code
|
|
@@ -190,3 +216,19 @@ def test_cmplx_nan():
|
|
|
190
216
|
>>> 1j*np.complex128(np.nan)
|
|
191
217
|
np.complex128(nan+nanj)
|
|
192
218
|
"""
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def array_and_list_1():
|
|
222
|
+
"""
|
|
223
|
+
>>> import numpy as np
|
|
224
|
+
>>> np.array([1, 2, 3])
|
|
225
|
+
[1, 2, 3]
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def array_and_list_2():
|
|
230
|
+
"""
|
|
231
|
+
>>> import numpy as np
|
|
232
|
+
>>> [1, 2, 3]
|
|
233
|
+
array([1, 2, 3])
|
|
234
|
+
"""
|
|
@@ -99,6 +99,24 @@ def test_user_context():
|
|
|
99
99
|
config=config)
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def test_wrong_lengths():
|
|
103
|
+
config = DTConfig()
|
|
104
|
+
res, _ = _testmod(failure_cases,
|
|
105
|
+
strategy=[failure_cases.iterable_length_1,
|
|
106
|
+
failure_cases.iterable_length_2],
|
|
107
|
+
config=config)
|
|
108
|
+
assert res.failed == 2
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_tuple_and_list():
|
|
112
|
+
config = DTConfig()
|
|
113
|
+
res, _ = _testmod(failure_cases,
|
|
114
|
+
strategy=[failure_cases.tuple_and_list_1,
|
|
115
|
+
failure_cases.tuple_and_list_2],
|
|
116
|
+
config=config)
|
|
117
|
+
assert res.failed == 2
|
|
118
|
+
|
|
119
|
+
|
|
102
120
|
class TestLocalFiles:
|
|
103
121
|
def test_local_files(self):
|
|
104
122
|
# A doctest tries to open a local file. Test that it works
|
|
@@ -98,7 +98,7 @@ def numpy_rndm_state():
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
@contextmanager
|
|
101
|
-
def noop_context_mgr(test):
|
|
101
|
+
def noop_context_mgr(test=None):
|
|
102
102
|
"""Do nothing.
|
|
103
103
|
|
|
104
104
|
This is a stub context manager to serve as a default for
|
|
@@ -117,7 +117,7 @@ def np_errstate():
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
@contextmanager
|
|
120
|
-
def warnings_errors(test):
|
|
120
|
+
def warnings_errors(test=None):
|
|
121
121
|
"""Temporarily turn all warnings to errors."""
|
|
122
122
|
with warnings.catch_warnings():
|
|
123
123
|
warnings.simplefilter('error', Warning)
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Doctests on steroids.
|
|
3
|
-
|
|
4
|
-
Whitespace-insensitive, numpy-aware, floating-point-aware doctest helpers.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
__version__ = "1.1"
|
|
9
|
-
|
|
10
|
-
from .impl import DTChecker, DTFinder, DTParser, DTRunner, DebugDTRunner, DTConfig # noqa
|
|
11
|
-
from .frontend import testmod, testfile, find_doctests, run_docstring_examples # noqa
|
|
12
|
-
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
__all__ = ['func9', 'func10']
|
|
2
|
-
|
|
3
|
-
def func9():
|
|
4
|
-
"""
|
|
5
|
-
Wrong output.
|
|
6
|
-
>>> import numpy as np
|
|
7
|
-
>>> np.array([1, 2, 3])
|
|
8
|
-
array([2, 3, 4])
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def func10():
|
|
13
|
-
"""
|
|
14
|
-
NameError
|
|
15
|
-
>>> import numpy as np
|
|
16
|
-
>>> np.arraY([1, 2, 3])
|
|
17
|
-
"""
|
|
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.1 → scipy_doctest-1.3}/scipy_doctest/tests/scipy_ndimage_tutorial_clone.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|