pytest-devtools 1.1.0__tar.gz → 1.3.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.3.0/PKG-INFO +446 -0
- pytest_devtools-1.3.0/README.md +414 -0
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/pyproject.toml +44 -11
- pytest_devtools-1.3.0/src/devtools/_options.py +59 -0
- pytest_devtools-1.3.0/src/devtools/_text.py +66 -0
- pytest_devtools-1.3.0/src/devtools/capsys_strip.py +147 -0
- pytest_devtools-1.3.0/src/devtools/cli_runners.py +538 -0
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/src/devtools/columns.py +2 -2
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/src/devtools/debug_fixture.py +7 -32
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/src/devtools/plugin.py +5 -1
- pytest_devtools-1.1.0/PKG-INFO +0 -286
- pytest_devtools-1.1.0/README.md +0 -258
- pytest_devtools-1.1.0/src/devtools/capsys_strip.py +0 -138
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/LICENSE +0 -0
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/src/devtools/__init__.py +0 -0
- {pytest_devtools-1.1.0 → pytest_devtools-1.3.0}/src/devtools/whitespace.py +0 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pytest-devtools
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Pytest plugin providing debug fixtures, ANSI-stripped capsys, whitespace-visible assertions, terminal column management, and click/typer CLI runner fixtures.
|
|
5
|
+
Author: Nathaniel Landau
|
|
6
|
+
Author-email: Nathaniel Landau <github@natelandau.com>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 natelandau
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
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
|
+
Classifier: Framework :: Pytest
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Requires-Dist: pytest>=7
|
|
25
|
+
Requires-Dist: rich>=15.0.0
|
|
26
|
+
Requires-Dist: click>=8.2 ; extra == 'click'
|
|
27
|
+
Requires-Dist: typer>=0.25 ; extra == 'typer'
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Provides-Extra: click
|
|
30
|
+
Provides-Extra: typer
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# pytest-devtools
|
|
34
|
+
|
|
35
|
+
[](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml)
|
|
36
|
+
[](https://codecov.io/gh/natelandau/pytest-devtools)
|
|
37
|
+
|
|
38
|
+
A pytest plugin that smooths over a few common annoyances when writing and debugging tests.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Debug fixture**: pretty-prints variables, paths, and data structures with [Rich](https://rich.readthedocs.io/), and only shows the output when a test fails.
|
|
43
|
+
- **Stripped `capsys` output**: removes ANSI escape codes (and optionally the `tmp_path` prefix) from captured stdout/stderr so assertions stay readable.
|
|
44
|
+
- **Visible whitespace in diffs**: replaces tabs, trailing spaces, carriage returns, and newlines with Unicode symbols when an assertion fails.
|
|
45
|
+
- **Terminal column width control**: sets `COLUMNS` for every test so libraries that auto-wrap (Rich, Click, etc.) produce stable output.
|
|
46
|
+
- **CLI runner output post-processing**: `cli_runner` and `typer_runner` fixtures strip ANSI codes, `tmp_path` prefixes, and trailing whitespace from `click`/`typer` `CliRunner` results, the same way `capsys` does for regular captured output.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# uv
|
|
52
|
+
uv add pytest-devtools
|
|
53
|
+
|
|
54
|
+
# pip
|
|
55
|
+
pip install pytest-devtools
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Requirements:** Python 3.10+ and pytest 7.0+.
|
|
59
|
+
|
|
60
|
+
The plugin registers itself through the `pytest11` entry point, so no `conftest.py` changes are needed.
|
|
61
|
+
|
|
62
|
+
## Debug Fixture
|
|
63
|
+
|
|
64
|
+
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`).
|
|
65
|
+
|
|
66
|
+
### Basic Usage
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
def test_user_creation(debug, tmp_path):
|
|
70
|
+
user = {"name": "Alice", "roles": ["admin", "editor"]}
|
|
71
|
+
debug(user)
|
|
72
|
+
|
|
73
|
+
config_path = tmp_path / "config.toml"
|
|
74
|
+
config_path.write_text("[settings]\nverbose = true")
|
|
75
|
+
debug(config_path)
|
|
76
|
+
|
|
77
|
+
assert user["name"] == "Alice"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
When the test fails, stderr shows the buffered output between rule separators:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
──────────────────────────── Debug ─────────────────────────────
|
|
84
|
+
{'name': 'Alice', 'roles': ['admin', 'editor']}
|
|
85
|
+
──────────────────────────── Debug ─────────────────────────────
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Multiple Values and Titles
|
|
89
|
+
|
|
90
|
+
Pass several arguments in a single call, and use `title` to label the section:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
def test_transform(debug):
|
|
94
|
+
before = [1, 2, 3]
|
|
95
|
+
after = [x * 2 for x in before]
|
|
96
|
+
debug(before, after, title="Transform")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Per-Call Options
|
|
100
|
+
|
|
101
|
+
Override any option on a single call:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
def test_deep_structure(debug, tmp_path):
|
|
105
|
+
nested = {"a": {"b": {"c": {"d": "deep"}}}}
|
|
106
|
+
|
|
107
|
+
# Limit nesting depth
|
|
108
|
+
debug(nested, max_depth=2)
|
|
109
|
+
|
|
110
|
+
# Limit collection length
|
|
111
|
+
debug(list(range(100)), max_length=5)
|
|
112
|
+
|
|
113
|
+
# Show type annotations
|
|
114
|
+
debug(nested, show_type=True)
|
|
115
|
+
|
|
116
|
+
# Show directory tree for Path objects
|
|
117
|
+
debug(tmp_path, list_dir_contents=True)
|
|
118
|
+
|
|
119
|
+
# Disable tmp_path prefix stripping
|
|
120
|
+
debug(tmp_path / "output.txt", strip_tmp_path=False)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Path Handling
|
|
124
|
+
|
|
125
|
+
When you pass a `pathlib.Path`:
|
|
126
|
+
|
|
127
|
+
- `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`.
|
|
128
|
+
- Directory listing (default: off). When enabled and the path is a directory, a Rich tree shows the directory contents recursively.
|
|
129
|
+
|
|
130
|
+
### CLI Options
|
|
131
|
+
|
|
132
|
+
| Flag | Description |
|
|
133
|
+
| ------------------------------ | --------------------------------------------------- |
|
|
134
|
+
| `--print-debug` | Always show debug output, even on passing tests |
|
|
135
|
+
| `--debug-strip-tmp-path` | Strip `tmp_path` prefix from Path objects (default) |
|
|
136
|
+
| `--no-debug-strip-tmp-path` | Show full absolute paths |
|
|
137
|
+
| `--debug-list-dir-contents` | Show directory tree for Path directories |
|
|
138
|
+
| `--no-debug-list-dir-contents` | Don't list directory contents (default) |
|
|
139
|
+
| `--debug-max-depth=N` | Limit nesting depth in pretty-printed output |
|
|
140
|
+
| `--debug-max-length=N` | Limit collection length in pretty-printed output |
|
|
141
|
+
| `--debug-show-type` | Show type annotations above each value |
|
|
142
|
+
| `--no-debug-show-type` | Don't show type annotations (default) |
|
|
143
|
+
|
|
144
|
+
### INI Options
|
|
145
|
+
|
|
146
|
+
Add these to `pyproject.toml` under `[tool.pytest.ini_options]`:
|
|
147
|
+
|
|
148
|
+
```toml
|
|
149
|
+
[tool.pytest.ini_options]
|
|
150
|
+
print_debug = true
|
|
151
|
+
debug_strip_tmp_path = true
|
|
152
|
+
debug_list_dir_contents = false
|
|
153
|
+
debug_max_depth = 4
|
|
154
|
+
debug_max_length = 20
|
|
155
|
+
debug_show_type = false
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Option Precedence
|
|
159
|
+
|
|
160
|
+
Per-call arguments win, then CLI flags, then INI settings:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
per-call override > CLI flag > INI option > built-in default
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Stripped `capsys` Output
|
|
167
|
+
|
|
168
|
+
The plugin overrides the built-in `capsys` fixture so that `readouterr()` returns post-processed strings. Two transformations are available:
|
|
169
|
+
|
|
170
|
+
- ANSI escape stripping (default: on)
|
|
171
|
+
- `tmp_path` prefix stripping (default: off, opt-in)
|
|
172
|
+
|
|
173
|
+
Both can be disabled or enabled independently, and they compose when both are active.
|
|
174
|
+
|
|
175
|
+
### ANSI Escape Stripping
|
|
176
|
+
|
|
177
|
+
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:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
def test_greeting(capsys):
|
|
181
|
+
print("\x1b[32mHello, world!\x1b[0m")
|
|
182
|
+
|
|
183
|
+
captured = capsys.readouterr()
|
|
184
|
+
assert captured.out == "Hello, world!\n"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
To keep the codes for a single test, mark it with `@pytest.mark.keep_ansi`:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import pytest
|
|
191
|
+
|
|
192
|
+
@pytest.mark.keep_ansi
|
|
193
|
+
def test_color_codes(capsys):
|
|
194
|
+
print("\x1b[32mgreen\x1b[0m")
|
|
195
|
+
captured = capsys.readouterr()
|
|
196
|
+
assert "\x1b[32m" in captured.out
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
To turn stripping off globally:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
pytest --no-strip-ansi
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `tmp_path` Stripping
|
|
206
|
+
|
|
207
|
+
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:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
def test_writes_log(capsys, tmp_path):
|
|
211
|
+
log = tmp_path / "app.log"
|
|
212
|
+
print(f"wrote {log}")
|
|
213
|
+
|
|
214
|
+
captured = capsys.readouterr()
|
|
215
|
+
assert captured.out == "wrote app.log\n"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Enable it for one run:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pytest --capsys-strip-tmp-path
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Or globally in `pyproject.toml`:
|
|
225
|
+
|
|
226
|
+
```toml
|
|
227
|
+
[tool.pytest.ini_options]
|
|
228
|
+
capsys_strip_tmp_path = true
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`--no-capsys-strip-tmp-path` overrides the INI setting for a single run. Stripping applies to both `captured.out` and `captured.err`.
|
|
232
|
+
|
|
233
|
+
> [!NOTE]
|
|
234
|
+
> 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.
|
|
235
|
+
|
|
236
|
+
### INI Options
|
|
237
|
+
|
|
238
|
+
```toml
|
|
239
|
+
[tool.pytest.ini_options]
|
|
240
|
+
strip_ansi = true # default: true
|
|
241
|
+
capsys_strip_tmp_path = false # default: false
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## CLI Runner Output
|
|
245
|
+
|
|
246
|
+
`click.testing.CliRunner` and `typer.testing.CliRunner` replace `sys.stdout` and `sys.stderr` with their own in-memory buffers while a command runs. Nothing a CLI writes inside `invoke()` reaches pytest's capture machinery, so the `capsys` override above cannot see it and `result.output` arrives unprocessed.
|
|
247
|
+
|
|
248
|
+
The `cli_runner` and `typer_runner` fixtures are drop-in replacements that post-process the runner's own `Result`.
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# uv
|
|
252
|
+
uv add --dev "pytest-devtools[click]"
|
|
253
|
+
uv add --dev "pytest-devtools[typer]"
|
|
254
|
+
|
|
255
|
+
# pip
|
|
256
|
+
pip install "pytest-devtools[click]"
|
|
257
|
+
pip install "pytest-devtools[typer]"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Neither extra is strictly required. A project testing a CLI already depends on click or typer directly, and the fixtures activate whenever the library is importable. The extras exist to record the supported versions:
|
|
261
|
+
|
|
262
|
+
| Fixture | Requires |
|
|
263
|
+
| -------------- | ------------- |
|
|
264
|
+
| `cli_runner` | `click>=8.2` |
|
|
265
|
+
| `typer_runner` | `typer>=0.25` |
|
|
266
|
+
|
|
267
|
+
Those floors are where the runner exposes stdout and stderr as separate strings. Below them, `result.stderr` raises `ValueError: stderr not separately captured` and `result.output` silently returns the two streams mixed together. Both fixtures check the installed version at setup and fail with an explicit message rather than letting that surface later.
|
|
268
|
+
|
|
269
|
+
### click
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from myapp.cli import main
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_greeting(cli_runner):
|
|
276
|
+
result = cli_runner.invoke(main, ["--name", "world"])
|
|
277
|
+
|
|
278
|
+
assert result.exit_code == 0
|
|
279
|
+
assert result.output == "Hello world\n"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### typer
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from myapp.cli import app
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def test_greeting(typer_runner):
|
|
289
|
+
result = typer_runner.invoke(app, ["--name", "world"])
|
|
290
|
+
|
|
291
|
+
assert result.exit_code == 0
|
|
292
|
+
assert result.output == "Hello world\n"
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Both fixtures are real subclasses of their framework's runner, so `isolated_filesystem()` and every other runner method work as usual. Only `invoke()` changes.
|
|
296
|
+
|
|
297
|
+
When a transform is active, `invoke()` returns a transparent proxy over the framework's `Result`: `isinstance(result, click.testing.Result)` and a `-> Result` annotation both hold, but `type(result)` reports the proxy, not the framework's class.
|
|
298
|
+
|
|
299
|
+
### ANSI Escape Stripping
|
|
300
|
+
|
|
301
|
+
On by default, and controlled by the same settings as `capsys`: `--no-strip-ansi`, `strip_ansi = false`, and `@pytest.mark.keep_ansi` for a single test.
|
|
302
|
+
|
|
303
|
+
This matters most for CLIs that print through Rich. Click strips its own `echo()` styling inside the runner already, but a `Console(force_terminal=True)` writes escape codes straight past that.
|
|
304
|
+
|
|
305
|
+
### `tmp_path` Stripping
|
|
306
|
+
|
|
307
|
+
Off by default. Enable with `--cli-runner-strip-tmp-path` or `cli_runner_strip_tmp_path = true` to collapse paths under `tmp_path` to their relative portion:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
def test_writes_file(cli_runner, tmp_path):
|
|
311
|
+
result = cli_runner.invoke(main, ["--out", str(tmp_path / "sub" / "report.txt")])
|
|
312
|
+
|
|
313
|
+
assert "wrote sub/report.txt" in result.output
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Trailing Whitespace Stripping
|
|
317
|
+
|
|
318
|
+
Off by default. Enable with `--cli-runner-strip-trailing-whitespace` or `cli_runner_strip_trailing_whitespace = true`.
|
|
319
|
+
|
|
320
|
+
Typer renders help through a Rich panel, which pads every line out to the full terminal width. At a wide `--columns` setting that means lines of mostly blanks, which makes asserting on `--help` output impractical:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
def test_help(typer_runner):
|
|
324
|
+
result = typer_runner.invoke(app, ["--help"])
|
|
325
|
+
|
|
326
|
+
assert "Usage" in result.output
|
|
327
|
+
assert not any(line.endswith(" ") for line in result.output.split("\n"))
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Terminal Width
|
|
331
|
+
|
|
332
|
+
Click's help formatter caps at 80 columns and ignores the `COLUMNS` environment variable, so the `--columns` setting alone never reached help text. When the column width feature is enabled, both fixtures pass it to `invoke()` as the `terminal_width` context setting. An explicit `terminal_width` argument always wins:
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
def test_narrow_help(cli_runner):
|
|
336
|
+
result = cli_runner.invoke(main, ["--help"], terminal_width=40)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Runners You Construct Yourself
|
|
340
|
+
|
|
341
|
+
If runners come from your own conftest fixture and you would rather not switch to `cli_runner`, opt into patching instead. With `cli_runner_patch_result = true` or `--cli-runner-patch-result`, the same post-processing applies to every `Result` for the duration of each test, whoever built the runner:
|
|
342
|
+
|
|
343
|
+
```toml
|
|
344
|
+
[tool.pytest.ini_options]
|
|
345
|
+
cli_runner_patch_result = true
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Three caveats:
|
|
349
|
+
|
|
350
|
+
- Terminal width injection does not reach externally built runners. Only the `cli_runner` and `typer_runner` fixtures' own `invoke()` passes `terminal_width` through; a runner you construct yourself keeps whatever width it would normally use.
|
|
351
|
+
- Enabling patch mode changes what `cli_runner.invoke()` and `typer_runner.invoke()` return too: once the `Result` classes are patched, the fixtures' own `invoke()` skips its wrapping and returns the framework's `Result` directly instead of the proxy.
|
|
352
|
+
- The patch is undone by `monkeypatch`, whose finalizer runs after every function-scoped fixture's teardown but before any session- or module-scoped one. A session- or module-scoped fixture that reads a `Result` during its own teardown therefore sees the raw, unprocessed output.
|
|
353
|
+
|
|
354
|
+
## Visible Whitespace in Assertions
|
|
355
|
+
|
|
356
|
+
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.
|
|
357
|
+
|
|
358
|
+
### Symbol Reference
|
|
359
|
+
|
|
360
|
+
| Character | Symbol | Name |
|
|
361
|
+
| ---------------------- | ------ | ---------------- |
|
|
362
|
+
| Trailing space | `·` | Middle dot |
|
|
363
|
+
| Tab (`\t`) | `→` | Rightwards arrow |
|
|
364
|
+
| Carriage return (`\r`) | `←` | Leftwards arrow |
|
|
365
|
+
| Newline (`\n`) | `↵` | Return symbol |
|
|
366
|
+
|
|
367
|
+
### Example Output
|
|
368
|
+
|
|
369
|
+
For a test like:
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
def test_output():
|
|
373
|
+
assert "hello " == "hello"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The failure message shows:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
AssertionError: 'hello·' == 'hello'
|
|
380
|
+
|
|
381
|
+
Whitespace-visible comparison:
|
|
382
|
+
Left: 'hello·'
|
|
383
|
+
Right: 'hello'
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Disabling
|
|
387
|
+
|
|
388
|
+
Use the `--no-show-whitespace` CLI flag, or set the INI option:
|
|
389
|
+
|
|
390
|
+
```toml
|
|
391
|
+
[tool.pytest.ini_options]
|
|
392
|
+
show_whitespace = false
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
> [!NOTE]
|
|
396
|
+
> 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.
|
|
397
|
+
|
|
398
|
+
## Terminal Column Width
|
|
399
|
+
|
|
400
|
+
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.
|
|
401
|
+
|
|
402
|
+
The feature is **disabled by default**. Enable it with the `--columns` CLI flag or via INI options.
|
|
403
|
+
|
|
404
|
+
### CLI Option
|
|
405
|
+
|
|
406
|
+
Set `COLUMNS` for a single run:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
pytest --columns=180
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### INI Options
|
|
413
|
+
|
|
414
|
+
Enable it permanently in `pyproject.toml`:
|
|
415
|
+
|
|
416
|
+
```toml
|
|
417
|
+
[tool.pytest.ini_options]
|
|
418
|
+
set_columns = true # turn the feature on
|
|
419
|
+
columns = 180 # value to set when enabled
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
The `--columns` CLI flag overrides the INI `columns` value when both are present.
|
|
423
|
+
|
|
424
|
+
## Configuration Summary
|
|
425
|
+
|
|
426
|
+
Every feature is configurable through CLI flags and `pyproject.toml` INI options. The debug fixture additionally supports per-call arguments.
|
|
427
|
+
|
|
428
|
+
| Feature | Default | Toggle with |
|
|
429
|
+
| ---------------------- | --------------------- | ----------------------------------------------------------------- |
|
|
430
|
+
| Debug fixture | Output on failure | Always available; `--print-debug` to also show on success |
|
|
431
|
+
| ANSI stripping | On | `--no-strip-ansi` or `strip_ansi = false` |
|
|
432
|
+
| `tmp_path` in `capsys` | Off | `--capsys-strip-tmp-path` or `capsys_strip_tmp_path = true` |
|
|
433
|
+
| Visible whitespace | On | `--no-show-whitespace` or `show_whitespace = false` |
|
|
434
|
+
| Column width | Off | `--columns=N` or `set_columns = true` |
|
|
435
|
+
| CLI runner ANSI | On | Shares `--no-strip-ansi` / `strip_ansi = false` |
|
|
436
|
+
| `tmp_path` in runner | Off | `--cli-runner-strip-tmp-path` or `cli_runner_strip_tmp_path = true` |
|
|
437
|
+
| Runner trailing space | Off | `--cli-runner-strip-trailing-whitespace` or `cli_runner_strip_trailing_whitespace = true` |
|
|
438
|
+
| Patch external runners | Off | `--cli-runner-patch-result` or `cli_runner_patch_result = true` |
|
|
439
|
+
|
|
440
|
+
## AI Policy
|
|
441
|
+
|
|
442
|
+
All AI generated content is and always will be meticulously reviewed and approved by the author.
|
|
443
|
+
|
|
444
|
+
## License
|
|
445
|
+
|
|
446
|
+
MIT
|