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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pytest-devtools
3
- Version: 1.0.0
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>=9.0.2
24
- Requires-Dist: rich>=14.3.2
25
- Requires-Python: >=3.11
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
- Small pytest plugin that provides some niceties for writing and debugging tests.
31
+ [![Automated Tests](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml/badge.svg)](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml)
32
+ [![codecov](https://codecov.io/gh/natelandau/pytest-devtools/graph/badge.svg)](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
- - **Debug fixture** -- Pretty-print variables, paths, and data structures with Rich. Output appears only when tests fail (or always with `--print-debug`).
35
- - **ANSI-stripped capsys** -- Automatically strip ANSI escape sequences from captured output so assertions don't break on color codes.
36
- - **Visible whitespace** -- Replace invisible whitespace characters (tabs, trailing spaces, carriage returns, newlines) with Unicode symbols in assertion failure diffs.
37
- - **Terminal column width** -- Optionally set the `COLUMNS` environment variable so Rich and other terminal-aware libraries don't introduce unwanted line wraps.
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
- # Using uv
46
+ # uv
43
47
  uv add pytest-devtools
44
48
 
45
- # Using pip
49
+ # pip
46
50
  pip install pytest-devtools
47
51
  ```
48
52
 
49
- **Requirements:** Python 3.11+ and pytest 7.0+
53
+ **Requirements:** Python 3.10+ and pytest 7.0+.
50
54
 
51
- The plugin activates automatically once installed. No `conftest.py` changes are needed.
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 gives you a callable that pretty-prints any Python object using Rich. Output is collected during the test and flushed to stderr only when the test fails.
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
- On failure, stderr shows the Rich-formatted output between rule separators:
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 multiple arguments in a single call, and use `title` to label sections:
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
- Every option can be overridden on a per-call basis:
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` to `debug`:
120
+ When you pass a `pathlib.Path`:
117
121
 
118
- - **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`.
119
- - **Directory listing** (default: off) -- When enabled and the path is a directory, a Rich tree shows the full directory contents recursively.
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 take highest priority, then CLI flags, then INI settings:
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
- ## ANSI-Stripped capsys
161
+ ## Stripped `capsys` Output
158
162
 
159
- By default, `capsys.readouterr()` returns output with ANSI escape sequences removed. This makes assertions simpler when testing code that uses colored output (Rich, Click, Colorama, etc.).
163
+ The plugin overrides the built-in `capsys` fixture so that `readouterr()` returns post-processed strings. Two transformations are available:
160
164
 
161
- ### Basic Usage
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" # No ANSI codes to worry about
179
+ assert captured.out == "Hello, world!\n"
170
180
  ```
171
181
 
172
- ### Keeping ANSI Codes
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
- Or disable stripping globally with a CLI flag:
194
+ To turn stripping off globally:
187
195
 
188
196
  ```bash
189
197
  pytest --no-strip-ansi
190
198
  ```
191
199
 
192
- ### INI Option
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 = false
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 by whitespace, pytest's default diff is hard to read. This plugin replaces invisible characters with visible Unicode symbols in assertion failure output.
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
- Disable with the `--no-show-whitespace` CLI flag or in INI:
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
- > **Note:** Whitespace visibility only activates for `==` comparisons between strings, and only when the replacement actually changes the display. Non-string comparisons and strings without special whitespace are unaffected.
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 the terminal width at runtime. In test environments, the detected width is often very small, causing unwanted line wraps in captured output. This plugin can set the `COLUMNS` environment variable for every test to prevent this.
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
- This feature is **disabled by default**. Enable it with the `--columns` CLI flag or via INI options.
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 # Enable the feature
263
- columns = 180 # Value to set (default when enabled)
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
- All features can be configured via CLI flags, `pyproject.toml` INI options, or (for the debug fixture) per-call arguments.
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 | Enabled by default | Enable/Disable with |
273
- | ------------------ | ---------------------- | --------------------------------------------------- |
274
- | Debug fixture | Output on failure only | N/A (always available) |
275
- | ANSI stripping | Yes | `--no-strip-ansi` or `strip_ansi = false` |
276
- | Visible whitespace | Yes | `--no-show-whitespace` or `show_whitespace = false` |
277
- | Column width | No | `--columns=N` or `set_columns = true` |
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
+ [![Automated Tests](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml/badge.svg)](https://github.com/natelandau/pytest-devtools/actions/workflows/automated-tests.yml)
4
+ [![codecov](https://codecov.io/gh/natelandau/pytest-devtools/graph/badge.svg)](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