pytest-devtools 1.0.0__tar.gz → 1.2.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.
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/PKG-INFO +89 -47
- pytest_devtools-1.2.0/README.md +299 -0
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/pyproject.toml +45 -14
- pytest_devtools-1.2.0/src/devtools/_options.py +38 -0
- pytest_devtools-1.2.0/src/devtools/capsys_strip.py +196 -0
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/src/devtools/debug_fixture.py +7 -32
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/src/devtools/plugin.py +1 -1
- pytest_devtools-1.0.0/README.md +0 -258
- pytest_devtools-1.0.0/src/devtools/capsys_strip.py +0 -138
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/LICENSE +0 -0
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/src/devtools/__init__.py +0 -0
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/src/devtools/columns.py +0 -0
- {pytest_devtools-1.0.0 → pytest_devtools-1.2.0}/src/devtools/whitespace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pytest-devtools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Pytest plugin providing debug fixtures, ANSI-stripped capsys, whitespace-visible assertions, and terminal column management.
|
|
5
5
|
Author: Nathaniel Landau
|
|
6
6
|
Author-email: Nathaniel Landau <github@natelandau.com>
|
|
@@ -15,44 +15,48 @@ License: MIT License
|
|
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
16
16
|
Classifier: Framework :: Pytest
|
|
17
17
|
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Classifier: Topic :: Software Development :: Testing
|
|
23
|
-
Requires-Dist: pytest>=
|
|
24
|
-
Requires-Dist: rich>=
|
|
25
|
-
Requires-Python: >=3.
|
|
24
|
+
Requires-Dist: pytest>=7
|
|
25
|
+
Requires-Dist: rich>=15.0.0
|
|
26
|
+
Requires-Python: >=3.10
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
|
|
28
29
|
# pytest-devtools
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
[](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml)
|
|
32
|
+
[](https://codecov.io/gh/natelandau/pytest-devtools)
|
|
33
|
+
|
|
34
|
+
A pytest plugin that smooths over a few common annoyances when writing and debugging tests.
|
|
31
35
|
|
|
32
36
|
## Features
|
|
33
37
|
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
+
- **Debug fixture**: pretty-prints variables, paths, and data structures with [Rich](https://rich.readthedocs.io/), and only shows the output when a test fails.
|
|
39
|
+
- **Stripped `capsys` output**: removes ANSI escape codes (and optionally the `tmp_path` prefix) from captured stdout/stderr so assertions stay readable.
|
|
40
|
+
- **Visible whitespace in diffs**: replaces tabs, trailing spaces, carriage returns, and newlines with Unicode symbols when an assertion fails.
|
|
41
|
+
- **Terminal column width control**: sets `COLUMNS` for every test so libraries that auto-wrap (Rich, Click, etc.) produce stable output.
|
|
38
42
|
|
|
39
43
|
## Installation
|
|
40
44
|
|
|
41
45
|
```bash
|
|
42
|
-
#
|
|
46
|
+
# uv
|
|
43
47
|
uv add pytest-devtools
|
|
44
48
|
|
|
45
|
-
#
|
|
49
|
+
# pip
|
|
46
50
|
pip install pytest-devtools
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
**Requirements:** Python 3.
|
|
53
|
+
**Requirements:** Python 3.10+ and pytest 7.0+.
|
|
50
54
|
|
|
51
|
-
The plugin
|
|
55
|
+
The plugin registers itself through the `pytest11` entry point, so no `conftest.py` changes are needed.
|
|
52
56
|
|
|
53
57
|
## Debug Fixture
|
|
54
58
|
|
|
55
|
-
The `debug` fixture
|
|
59
|
+
The `debug` fixture is a callable that pretty-prints any Python object using Rich. Output is buffered during the test and written to stderr only if the test fails (or always, with `--print-debug`).
|
|
56
60
|
|
|
57
61
|
### Basic Usage
|
|
58
62
|
|
|
@@ -68,7 +72,7 @@ def test_user_creation(debug, tmp_path):
|
|
|
68
72
|
assert user["name"] == "Alice"
|
|
69
73
|
```
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
When the test fails, stderr shows the buffered output between rule separators:
|
|
72
76
|
|
|
73
77
|
```
|
|
74
78
|
──────────────────────────── Debug ─────────────────────────────
|
|
@@ -78,7 +82,7 @@ On failure, stderr shows the Rich-formatted output between rule separators:
|
|
|
78
82
|
|
|
79
83
|
### Multiple Values and Titles
|
|
80
84
|
|
|
81
|
-
Pass
|
|
85
|
+
Pass several arguments in a single call, and use `title` to label the section:
|
|
82
86
|
|
|
83
87
|
```python
|
|
84
88
|
def test_transform(debug):
|
|
@@ -89,7 +93,7 @@ def test_transform(debug):
|
|
|
89
93
|
|
|
90
94
|
### Per-Call Options
|
|
91
95
|
|
|
92
|
-
|
|
96
|
+
Override any option on a single call:
|
|
93
97
|
|
|
94
98
|
```python
|
|
95
99
|
def test_deep_structure(debug, tmp_path):
|
|
@@ -113,10 +117,10 @@ def test_deep_structure(debug, tmp_path):
|
|
|
113
117
|
|
|
114
118
|
### Path Handling
|
|
115
119
|
|
|
116
|
-
When you pass a `pathlib.Path
|
|
120
|
+
When you pass a `pathlib.Path`:
|
|
117
121
|
|
|
118
|
-
-
|
|
119
|
-
-
|
|
122
|
+
- `tmp_path` stripping (default: on). If the path is inside `tmp_path`, only the relative portion is shown. A path like `/var/folders/.../pytest-1234/test_foo0/subdir/file.txt` displays as `subdir/file.txt`.
|
|
123
|
+
- Directory listing (default: off). When enabled and the path is a directory, a Rich tree shows the directory contents recursively.
|
|
120
124
|
|
|
121
125
|
### CLI Options
|
|
122
126
|
|
|
@@ -148,30 +152,34 @@ debug_show_type = false
|
|
|
148
152
|
|
|
149
153
|
### Option Precedence
|
|
150
154
|
|
|
151
|
-
Per-call arguments
|
|
155
|
+
Per-call arguments win, then CLI flags, then INI settings:
|
|
152
156
|
|
|
153
157
|
```
|
|
154
158
|
per-call override > CLI flag > INI option > built-in default
|
|
155
159
|
```
|
|
156
160
|
|
|
157
|
-
##
|
|
161
|
+
## Stripped `capsys` Output
|
|
158
162
|
|
|
159
|
-
|
|
163
|
+
The plugin overrides the built-in `capsys` fixture so that `readouterr()` returns post-processed strings. Two transformations are available:
|
|
160
164
|
|
|
161
|
-
|
|
165
|
+
- ANSI escape stripping (default: on)
|
|
166
|
+
- `tmp_path` prefix stripping (default: off, opt-in)
|
|
167
|
+
|
|
168
|
+
Both can be disabled or enabled independently, and they compose when both are active.
|
|
169
|
+
|
|
170
|
+
### ANSI Escape Stripping
|
|
171
|
+
|
|
172
|
+
Tests that exercise code printing colored output (Rich, Click, Colorama, etc.) usually don't care about the escape codes. By default, they're removed before you see the captured string:
|
|
162
173
|
|
|
163
174
|
```python
|
|
164
175
|
def test_greeting(capsys):
|
|
165
|
-
# Imagine this function uses Rich to print colored output
|
|
166
176
|
print("\x1b[32mHello, world!\x1b[0m")
|
|
167
177
|
|
|
168
178
|
captured = capsys.readouterr()
|
|
169
|
-
assert captured.out == "Hello, world!\n"
|
|
179
|
+
assert captured.out == "Hello, world!\n"
|
|
170
180
|
```
|
|
171
181
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
For tests that need to verify color output, disable stripping per-test with a marker:
|
|
182
|
+
To keep the codes for a single test, mark it with `@pytest.mark.keep_ansi`:
|
|
175
183
|
|
|
176
184
|
```python
|
|
177
185
|
import pytest
|
|
@@ -183,22 +191,54 @@ def test_color_codes(capsys):
|
|
|
183
191
|
assert "\x1b[32m" in captured.out
|
|
184
192
|
```
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
To turn stripping off globally:
|
|
187
195
|
|
|
188
196
|
```bash
|
|
189
197
|
pytest --no-strip-ansi
|
|
190
198
|
```
|
|
191
199
|
|
|
192
|
-
###
|
|
200
|
+
### `tmp_path` Stripping
|
|
201
|
+
|
|
202
|
+
Code that prints a `tmp_path`-rooted file produces output like `/var/folders/.../pytest-1234/test_foo0/file.txt`, which is awkward to assert on. Opt in to capsys `tmp_path` stripping to collapse those prefixes to their relative portion:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
def test_writes_log(capsys, tmp_path):
|
|
206
|
+
log = tmp_path / "app.log"
|
|
207
|
+
print(f"wrote {log}")
|
|
208
|
+
|
|
209
|
+
captured = capsys.readouterr()
|
|
210
|
+
assert captured.out == "wrote app.log\n"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Enable it for one run:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pytest --capsys-strip-tmp-path
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Or globally in `pyproject.toml`:
|
|
220
|
+
|
|
221
|
+
```toml
|
|
222
|
+
[tool.pytest.ini_options]
|
|
223
|
+
capsys_strip_tmp_path = true
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`--no-capsys-strip-tmp-path` overrides the INI setting for a single run. Stripping applies to both `captured.out` and `captured.err`.
|
|
227
|
+
|
|
228
|
+
> [!NOTE]
|
|
229
|
+
> When both transformations are active, ANSI codes are stripped first, then `tmp_path` prefixes. The order matters only if your output mixes the two, but the combined result is what you'd expect.
|
|
230
|
+
|
|
231
|
+
### INI Options
|
|
193
232
|
|
|
194
233
|
```toml
|
|
195
234
|
[tool.pytest.ini_options]
|
|
196
|
-
strip_ansi =
|
|
235
|
+
strip_ansi = true # default: true
|
|
236
|
+
capsys_strip_tmp_path = false # default: false
|
|
197
237
|
```
|
|
198
238
|
|
|
199
239
|
## Visible Whitespace in Assertions
|
|
200
240
|
|
|
201
|
-
When two strings differ only
|
|
241
|
+
When two strings differ only in whitespace, pytest's default diff is hard to read. This plugin replaces invisible characters with visible Unicode symbols in the assertion failure message.
|
|
202
242
|
|
|
203
243
|
### Symbol Reference
|
|
204
244
|
|
|
@@ -230,20 +270,21 @@ Whitespace-visible comparison:
|
|
|
230
270
|
|
|
231
271
|
### Disabling
|
|
232
272
|
|
|
233
|
-
|
|
273
|
+
Use the `--no-show-whitespace` CLI flag, or set the INI option:
|
|
234
274
|
|
|
235
275
|
```toml
|
|
236
276
|
[tool.pytest.ini_options]
|
|
237
277
|
show_whitespace = false
|
|
238
278
|
```
|
|
239
279
|
|
|
240
|
-
>
|
|
280
|
+
> [!NOTE]
|
|
281
|
+
> Whitespace visibility activates only for `==` comparisons between strings, and only when the replacement actually changes how the string displays. Non-string comparisons and strings without notable whitespace are unaffected.
|
|
241
282
|
|
|
242
283
|
## Terminal Column Width
|
|
243
284
|
|
|
244
|
-
Many terminal-aware libraries (Rich, Click, etc.) detect
|
|
285
|
+
Many terminal-aware libraries (Rich, Click, etc.) detect terminal width at runtime. In test environments, the detected width is often very small, which causes unwanted line wraps in captured output. This plugin can set the `COLUMNS` environment variable for every test to keep output stable.
|
|
245
286
|
|
|
246
|
-
|
|
287
|
+
The feature is **disabled by default**. Enable it with the `--columns` CLI flag or via INI options.
|
|
247
288
|
|
|
248
289
|
### CLI Option
|
|
249
290
|
|
|
@@ -255,26 +296,27 @@ pytest --columns=180
|
|
|
255
296
|
|
|
256
297
|
### INI Options
|
|
257
298
|
|
|
258
|
-
Enable permanently in `pyproject.toml`:
|
|
299
|
+
Enable it permanently in `pyproject.toml`:
|
|
259
300
|
|
|
260
301
|
```toml
|
|
261
302
|
[tool.pytest.ini_options]
|
|
262
|
-
set_columns = true #
|
|
263
|
-
columns = 180 #
|
|
303
|
+
set_columns = true # turn the feature on
|
|
304
|
+
columns = 180 # value to set when enabled
|
|
264
305
|
```
|
|
265
306
|
|
|
266
307
|
The `--columns` CLI flag overrides the INI `columns` value when both are present.
|
|
267
308
|
|
|
268
309
|
## Configuration Summary
|
|
269
310
|
|
|
270
|
-
|
|
311
|
+
Every feature is configurable through CLI flags and `pyproject.toml` INI options. The debug fixture additionally supports per-call arguments.
|
|
271
312
|
|
|
272
|
-
| Feature
|
|
273
|
-
|
|
|
274
|
-
| Debug fixture
|
|
275
|
-
| ANSI stripping
|
|
276
|
-
|
|
|
277
|
-
|
|
|
313
|
+
| Feature | Default | Toggle with |
|
|
314
|
+
| ---------------------- | --------------------- | ----------------------------------------------------------------- |
|
|
315
|
+
| Debug fixture | Output on failure | Always available; `--print-debug` to also show on success |
|
|
316
|
+
| ANSI stripping | On | `--no-strip-ansi` or `strip_ansi = false` |
|
|
317
|
+
| `tmp_path` in `capsys` | Off | `--capsys-strip-tmp-path` or `capsys_strip_tmp_path = true` |
|
|
318
|
+
| Visible whitespace | On | `--no-show-whitespace` or `show_whitespace = false` |
|
|
319
|
+
| Column width | Off | `--columns=N` or `set_columns = true` |
|
|
278
320
|
|
|
279
321
|
## AI Policy
|
|
280
322
|
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# pytest-devtools
|
|
2
|
+
|
|
3
|
+
[](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml)
|
|
4
|
+
[](https://codecov.io/gh/natelandau/pytest-devtools)
|
|
5
|
+
|
|
6
|
+
A pytest plugin that smooths over a few common annoyances when writing and debugging tests.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Debug fixture**: pretty-prints variables, paths, and data structures with [Rich](https://rich.readthedocs.io/), and only shows the output when a test fails.
|
|
11
|
+
- **Stripped `capsys` output**: removes ANSI escape codes (and optionally the `tmp_path` prefix) from captured stdout/stderr so assertions stay readable.
|
|
12
|
+
- **Visible whitespace in diffs**: replaces tabs, trailing spaces, carriage returns, and newlines with Unicode symbols when an assertion fails.
|
|
13
|
+
- **Terminal column width control**: sets `COLUMNS` for every test so libraries that auto-wrap (Rich, Click, etc.) produce stable output.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# uv
|
|
19
|
+
uv add pytest-devtools
|
|
20
|
+
|
|
21
|
+
# pip
|
|
22
|
+
pip install pytest-devtools
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Requirements:** Python 3.10+ and pytest 7.0+.
|
|
26
|
+
|
|
27
|
+
The plugin registers itself through the `pytest11` entry point, so no `conftest.py` changes are needed.
|
|
28
|
+
|
|
29
|
+
## Debug Fixture
|
|
30
|
+
|
|
31
|
+
The `debug` fixture is a callable that pretty-prints any Python object using Rich. Output is buffered during the test and written to stderr only if the test fails (or always, with `--print-debug`).
|
|
32
|
+
|
|
33
|
+
### Basic Usage
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
def test_user_creation(debug, tmp_path):
|
|
37
|
+
user = {"name": "Alice", "roles": ["admin", "editor"]}
|
|
38
|
+
debug(user)
|
|
39
|
+
|
|
40
|
+
config_path = tmp_path / "config.toml"
|
|
41
|
+
config_path.write_text("[settings]\nverbose = true")
|
|
42
|
+
debug(config_path)
|
|
43
|
+
|
|
44
|
+
assert user["name"] == "Alice"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When the test fails, stderr shows the buffered output between rule separators:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
──────────────────────────── Debug ─────────────────────────────
|
|
51
|
+
{'name': 'Alice', 'roles': ['admin', 'editor']}
|
|
52
|
+
──────────────────────────── Debug ─────────────────────────────
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Multiple Values and Titles
|
|
56
|
+
|
|
57
|
+
Pass several arguments in a single call, and use `title` to label the section:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
def test_transform(debug):
|
|
61
|
+
before = [1, 2, 3]
|
|
62
|
+
after = [x * 2 for x in before]
|
|
63
|
+
debug(before, after, title="Transform")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Per-Call Options
|
|
67
|
+
|
|
68
|
+
Override any option on a single call:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
def test_deep_structure(debug, tmp_path):
|
|
72
|
+
nested = {"a": {"b": {"c": {"d": "deep"}}}}
|
|
73
|
+
|
|
74
|
+
# Limit nesting depth
|
|
75
|
+
debug(nested, max_depth=2)
|
|
76
|
+
|
|
77
|
+
# Limit collection length
|
|
78
|
+
debug(list(range(100)), max_length=5)
|
|
79
|
+
|
|
80
|
+
# Show type annotations
|
|
81
|
+
debug(nested, show_type=True)
|
|
82
|
+
|
|
83
|
+
# Show directory tree for Path objects
|
|
84
|
+
debug(tmp_path, list_dir_contents=True)
|
|
85
|
+
|
|
86
|
+
# Disable tmp_path prefix stripping
|
|
87
|
+
debug(tmp_path / "output.txt", strip_tmp_path=False)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Path Handling
|
|
91
|
+
|
|
92
|
+
When you pass a `pathlib.Path`:
|
|
93
|
+
|
|
94
|
+
- `tmp_path` stripping (default: on). If the path is inside `tmp_path`, only the relative portion is shown. A path like `/var/folders/.../pytest-1234/test_foo0/subdir/file.txt` displays as `subdir/file.txt`.
|
|
95
|
+
- Directory listing (default: off). When enabled and the path is a directory, a Rich tree shows the directory contents recursively.
|
|
96
|
+
|
|
97
|
+
### CLI Options
|
|
98
|
+
|
|
99
|
+
| Flag | Description |
|
|
100
|
+
| ------------------------------ | --------------------------------------------------- |
|
|
101
|
+
| `--print-debug` | Always show debug output, even on passing tests |
|
|
102
|
+
| `--debug-strip-tmp-path` | Strip `tmp_path` prefix from Path objects (default) |
|
|
103
|
+
| `--no-debug-strip-tmp-path` | Show full absolute paths |
|
|
104
|
+
| `--debug-list-dir-contents` | Show directory tree for Path directories |
|
|
105
|
+
| `--no-debug-list-dir-contents` | Don't list directory contents (default) |
|
|
106
|
+
| `--debug-max-depth=N` | Limit nesting depth in pretty-printed output |
|
|
107
|
+
| `--debug-max-length=N` | Limit collection length in pretty-printed output |
|
|
108
|
+
| `--debug-show-type` | Show type annotations above each value |
|
|
109
|
+
| `--no-debug-show-type` | Don't show type annotations (default) |
|
|
110
|
+
|
|
111
|
+
### INI Options
|
|
112
|
+
|
|
113
|
+
Add these to `pyproject.toml` under `[tool.pytest.ini_options]`:
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[tool.pytest.ini_options]
|
|
117
|
+
print_debug = true
|
|
118
|
+
debug_strip_tmp_path = true
|
|
119
|
+
debug_list_dir_contents = false
|
|
120
|
+
debug_max_depth = 4
|
|
121
|
+
debug_max_length = 20
|
|
122
|
+
debug_show_type = false
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Option Precedence
|
|
126
|
+
|
|
127
|
+
Per-call arguments win, then CLI flags, then INI settings:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
per-call override > CLI flag > INI option > built-in default
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Stripped `capsys` Output
|
|
134
|
+
|
|
135
|
+
The plugin overrides the built-in `capsys` fixture so that `readouterr()` returns post-processed strings. Two transformations are available:
|
|
136
|
+
|
|
137
|
+
- ANSI escape stripping (default: on)
|
|
138
|
+
- `tmp_path` prefix stripping (default: off, opt-in)
|
|
139
|
+
|
|
140
|
+
Both can be disabled or enabled independently, and they compose when both are active.
|
|
141
|
+
|
|
142
|
+
### ANSI Escape Stripping
|
|
143
|
+
|
|
144
|
+
Tests that exercise code printing colored output (Rich, Click, Colorama, etc.) usually don't care about the escape codes. By default, they're removed before you see the captured string:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
def test_greeting(capsys):
|
|
148
|
+
print("\x1b[32mHello, world!\x1b[0m")
|
|
149
|
+
|
|
150
|
+
captured = capsys.readouterr()
|
|
151
|
+
assert captured.out == "Hello, world!\n"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
To keep the codes for a single test, mark it with `@pytest.mark.keep_ansi`:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import pytest
|
|
158
|
+
|
|
159
|
+
@pytest.mark.keep_ansi
|
|
160
|
+
def test_color_codes(capsys):
|
|
161
|
+
print("\x1b[32mgreen\x1b[0m")
|
|
162
|
+
captured = capsys.readouterr()
|
|
163
|
+
assert "\x1b[32m" in captured.out
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
To turn stripping off globally:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pytest --no-strip-ansi
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `tmp_path` Stripping
|
|
173
|
+
|
|
174
|
+
Code that prints a `tmp_path`-rooted file produces output like `/var/folders/.../pytest-1234/test_foo0/file.txt`, which is awkward to assert on. Opt in to capsys `tmp_path` stripping to collapse those prefixes to their relative portion:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
def test_writes_log(capsys, tmp_path):
|
|
178
|
+
log = tmp_path / "app.log"
|
|
179
|
+
print(f"wrote {log}")
|
|
180
|
+
|
|
181
|
+
captured = capsys.readouterr()
|
|
182
|
+
assert captured.out == "wrote app.log\n"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Enable it for one run:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pytest --capsys-strip-tmp-path
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Or globally in `pyproject.toml`:
|
|
192
|
+
|
|
193
|
+
```toml
|
|
194
|
+
[tool.pytest.ini_options]
|
|
195
|
+
capsys_strip_tmp_path = true
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`--no-capsys-strip-tmp-path` overrides the INI setting for a single run. Stripping applies to both `captured.out` and `captured.err`.
|
|
199
|
+
|
|
200
|
+
> [!NOTE]
|
|
201
|
+
> When both transformations are active, ANSI codes are stripped first, then `tmp_path` prefixes. The order matters only if your output mixes the two, but the combined result is what you'd expect.
|
|
202
|
+
|
|
203
|
+
### INI Options
|
|
204
|
+
|
|
205
|
+
```toml
|
|
206
|
+
[tool.pytest.ini_options]
|
|
207
|
+
strip_ansi = true # default: true
|
|
208
|
+
capsys_strip_tmp_path = false # default: false
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Visible Whitespace in Assertions
|
|
212
|
+
|
|
213
|
+
When two strings differ only in whitespace, pytest's default diff is hard to read. This plugin replaces invisible characters with visible Unicode symbols in the assertion failure message.
|
|
214
|
+
|
|
215
|
+
### Symbol Reference
|
|
216
|
+
|
|
217
|
+
| Character | Symbol | Name |
|
|
218
|
+
| ---------------------- | ------ | ---------------- |
|
|
219
|
+
| Trailing space | `·` | Middle dot |
|
|
220
|
+
| Tab (`\t`) | `→` | Rightwards arrow |
|
|
221
|
+
| Carriage return (`\r`) | `←` | Leftwards arrow |
|
|
222
|
+
| Newline (`\n`) | `↵` | Return symbol |
|
|
223
|
+
|
|
224
|
+
### Example Output
|
|
225
|
+
|
|
226
|
+
For a test like:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
def test_output():
|
|
230
|
+
assert "hello " == "hello"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The failure message shows:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
AssertionError: 'hello·' == 'hello'
|
|
237
|
+
|
|
238
|
+
Whitespace-visible comparison:
|
|
239
|
+
Left: 'hello·'
|
|
240
|
+
Right: 'hello'
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Disabling
|
|
244
|
+
|
|
245
|
+
Use the `--no-show-whitespace` CLI flag, or set the INI option:
|
|
246
|
+
|
|
247
|
+
```toml
|
|
248
|
+
[tool.pytest.ini_options]
|
|
249
|
+
show_whitespace = false
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
> [!NOTE]
|
|
253
|
+
> Whitespace visibility activates only for `==` comparisons between strings, and only when the replacement actually changes how the string displays. Non-string comparisons and strings without notable whitespace are unaffected.
|
|
254
|
+
|
|
255
|
+
## Terminal Column Width
|
|
256
|
+
|
|
257
|
+
Many terminal-aware libraries (Rich, Click, etc.) detect terminal width at runtime. In test environments, the detected width is often very small, which causes unwanted line wraps in captured output. This plugin can set the `COLUMNS` environment variable for every test to keep output stable.
|
|
258
|
+
|
|
259
|
+
The feature is **disabled by default**. Enable it with the `--columns` CLI flag or via INI options.
|
|
260
|
+
|
|
261
|
+
### CLI Option
|
|
262
|
+
|
|
263
|
+
Set `COLUMNS` for a single run:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
pytest --columns=180
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### INI Options
|
|
270
|
+
|
|
271
|
+
Enable it permanently in `pyproject.toml`:
|
|
272
|
+
|
|
273
|
+
```toml
|
|
274
|
+
[tool.pytest.ini_options]
|
|
275
|
+
set_columns = true # turn the feature on
|
|
276
|
+
columns = 180 # value to set when enabled
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The `--columns` CLI flag overrides the INI `columns` value when both are present.
|
|
280
|
+
|
|
281
|
+
## Configuration Summary
|
|
282
|
+
|
|
283
|
+
Every feature is configurable through CLI flags and `pyproject.toml` INI options. The debug fixture additionally supports per-call arguments.
|
|
284
|
+
|
|
285
|
+
| Feature | Default | Toggle with |
|
|
286
|
+
| ---------------------- | --------------------- | ----------------------------------------------------------------- |
|
|
287
|
+
| Debug fixture | Output on failure | Always available; `--print-debug` to also show on success |
|
|
288
|
+
| ANSI stripping | On | `--no-strip-ansi` or `strip_ansi = false` |
|
|
289
|
+
| `tmp_path` in `capsys` | Off | `--capsys-strip-tmp-path` or `capsys_strip_tmp_path = true` |
|
|
290
|
+
| Visible whitespace | On | `--no-show-whitespace` or `show_whitespace = false` |
|
|
291
|
+
| Column width | Off | `--columns=N` or `set_columns = true` |
|
|
292
|
+
|
|
293
|
+
## AI Policy
|
|
294
|
+
|
|
295
|
+
All AI generated content is and always will be meticulously reviewed and approved by the author.
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
MIT
|