Pytest plugin that changes the default look and feel of pytest with progress bar and enhanced visual output
npx @tessl/cli install tessl/pypi-pytest-sugar@1.1.00
# pytest-sugar
1
2
A pytest plugin that changes the default visual appearance of pytest test execution. It provides a progress bar during test runs, displays test failures instantly as they occur rather than at the end, improves overall test result formatting with colored output, and adds visual enhancements to make test output more readable and informative.
3
4
## Package Information
5
6
- **Package Name**: pytest-sugar
7
- **Package Type**: pytest plugin
8
- **Language**: Python
9
- **Installation**: `pip install pytest-sugar`
10
11
## Core Imports
12
13
pytest-sugar is automatically activated when installed via pytest's plugin system:
14
15
```python
16
# No explicit imports required - plugin auto-activates
17
# Test your code normally with pytest
18
```
19
20
For programmatic access to components:
21
22
```python
23
from pytest_sugar import SugarTerminalReporter, Theme, strip_colors
24
```
25
26
## Basic Usage
27
28
pytest-sugar works automatically once installed. Simply run your tests:
29
30
```bash
31
# Basic test execution with pytest-sugar enabled
32
pytest
33
34
# Verbose output (one test per line)
35
pytest --verbose
36
37
# Disable pytest-sugar
38
pytest -p no:sugar
39
40
# Force pytest-sugar in non-terminal environments
41
pytest --force-sugar
42
```
43
44
Configuration via config files:
45
46
```ini
47
# pytest-sugar.conf or ~/.pytest-sugar.conf
48
[theme]
49
success = green
50
fail = red
51
skipped = blue
52
progressbar = green
53
54
[sugar]
55
progressbar_length = 20%
56
```
57
58
## Architecture
59
60
pytest-sugar integrates with pytest's plugin architecture through:
61
62
- **Plugin Registration**: Automatic registration via `pytest11` entry point
63
- **Hook System**: Implements pytest's hook functions to customize test reporting
64
- **Terminal Reporter Replacement**: Replaces pytest's default `TerminalReporter` with `SugarTerminalReporter`
65
- **Theme System**: Configurable visual themes for colors and symbols
66
- **Progress Bar**: Real-time progress visualization during test execution
67
68
## Capabilities
69
70
### Plugin Configuration
71
72
Core plugin configuration and initialization functions that integrate with pytest's hook system.
73
74
```python { .api }
75
def pytest_addoption(parser: Parser) -> None:
76
"""
77
Add command-line options to pytest.
78
79
Adds pytest-sugar specific options:
80
--old-summary: Show full tracebacks instead of one-line summaries
81
--force-sugar: Force sugar output even when not in terminal
82
--sugar-trace-dir: Directory for Playwright trace files
83
--sugar-no-trace: Disable Playwright trace file detection
84
85
Parameters:
86
- parser: Parser - pytest argument parser
87
"""
88
89
def pytest_configure(config) -> None:
90
"""
91
Configure pytest-sugar plugin (trylast=True).
92
93
Replaces default terminal reporter with SugarTerminalReporter
94
when running in terminal or when forced.
95
96
Parameters:
97
- config: Config - pytest configuration object
98
"""
99
100
def pytest_sessionstart(session: Session) -> None:
101
"""
102
Called when pytest session starts.
103
104
Loads theme configuration from config files and initializes
105
global theme settings.
106
107
Parameters:
108
- session: Session - pytest session object
109
"""
110
```
111
112
### Test Collection and Progress Tracking
113
114
Functions that handle test collection completion and maintain test count for progress tracking.
115
116
```python { .api }
117
def pytest_collection_finish(session: Session) -> None:
118
"""
119
Called when test collection is finished.
120
121
Sets the total test count on the terminal reporter for
122
progress bar calculations.
123
124
Parameters:
125
- session: Session - pytest session object
126
"""
127
128
def pytest_deselected(items: Sequence[Item]) -> None:
129
"""
130
Called when tests are deselected.
131
132
Updates the test count to exclude deselected tests from
133
progress calculations.
134
135
Parameters:
136
- items: Sequence[Item] - deselected test items
137
"""
138
```
139
140
### Test Status Reporting
141
142
Customizes how test results are visually represented with colored symbols and status indicators.
143
144
```python { .api }
145
def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]:
146
"""
147
Customize test status reporting with colored symbols.
148
149
Returns custom status representation with colors and symbols
150
based on test outcome (passed, failed, skipped, etc.).
151
152
Parameters:
153
- report: BaseReport - test execution report
154
155
Returns:
156
Optional[Tuple[str, str, str]] - (category, letter, word) or None
157
"""
158
```
159
160
### Terminal Reporter
161
162
Enhanced terminal reporter that provides real-time progress visualization and improved test output formatting.
163
164
```python { .api }
165
class SugarTerminalReporter:
166
"""
167
Custom terminal reporter that replaces pytest's default TerminalReporter.
168
169
Provides enhanced visual output with progress bar, instant failure display,
170
and improved formatting.
171
"""
172
173
def __init__(self, config: Config, file: Union[TextIO, None] = None) -> None:
174
"""
175
Initialize the sugar terminal reporter.
176
177
Parameters:
178
- config: Config - pytest configuration object
179
- file: Union[TextIO, None] - output file stream (optional)
180
"""
181
182
def reset_tracked_lines(self) -> None:
183
"""Reset line tracking state for display management."""
184
185
def pytest_collectreport(self, report: CollectReport) -> None:
186
"""
187
Handle collection reports and display collection failures.
188
189
Parameters:
190
- report: CollectReport - collection report from pytest
191
"""
192
193
def pytest_sessionstart(self, session: Session) -> None:
194
"""
195
Handle session start and display header information.
196
197
Parameters:
198
- session: Session - pytest session object
199
"""
200
201
def write_fspath_result(self, nodeid: str, res, **markup: bool) -> None:
202
"""
203
Override to disable default file path result writing (no-op).
204
205
pytest-sugar handles its own file path and result display formatting,
206
so this method is overridden to prevent default behavior.
207
208
Parameters:
209
- nodeid: str - test node identifier
210
- res: test result
211
- **markup: bool - markup options (ignored)
212
"""
213
214
def insert_progress(self, report: Union[CollectReport, TestReport]) -> None:
215
"""
216
Insert progress bar into current terminal line.
217
218
Creates and displays the progress bar with current test completion
219
percentage and visual indicators for passed/failed tests.
220
221
Parameters:
222
- report: Union[CollectReport, TestReport] - test or collection report
223
"""
224
225
def overwrite(self, line: str, rel_line_num: int) -> None:
226
"""
227
Overwrite terminal line at relative position.
228
229
Parameters:
230
- line: str - text to write
231
- rel_line_num: int - relative line number offset
232
"""
233
234
def get_max_column_for_test_status(self) -> int:
235
"""
236
Get maximum column width available for test status display.
237
238
Returns:
239
int - maximum column width for test status
240
"""
241
242
def begin_new_line(self, report: Union[CollectReport, TestReport], print_filename: bool) -> None:
243
"""
244
Start a new display line for test output.
245
246
Parameters:
247
- report: Union[CollectReport, TestReport] - test or collection report
248
- print_filename: bool - whether to print the filename
249
"""
250
251
def reached_last_column_for_test_status(self, report: Union[CollectReport, TestReport]) -> bool:
252
"""
253
Check if the current line has reached maximum width.
254
255
Parameters:
256
- report: Union[CollectReport, TestReport] - test or collection report
257
258
Returns:
259
bool - True if line is at maximum width
260
"""
261
262
def report_key(self, report: Union[CollectReport, TestReport]) -> Any:
263
"""
264
Get key to identify which line the report should write to.
265
266
Parameters:
267
- report: Union[CollectReport, TestReport] - test or collection report
268
269
Returns:
270
Any - key for grouping reports by display line
271
"""
272
273
def pytest_runtest_logstart(self, nodeid, location) -> None:
274
"""
275
Handle test execution start logging (override to disable default behavior).
276
277
Prevents pytest's default location line printing since pytest-sugar
278
already displays module names and test names in verbose mode.
279
280
Parameters:
281
- nodeid: test node identifier
282
- location: test location information
283
"""
284
285
def pytest_runtest_logfinish(self, nodeid: str) -> None:
286
"""
287
Handle test execution finish logging (override to disable default progress).
288
289
Prevents pytest's default progress display since pytest-sugar
290
provides its own progress bar implementation.
291
292
Parameters:
293
- nodeid: str - test node identifier
294
"""
295
296
def pytest_runtest_logreport(self, report: TestReport) -> None:
297
"""
298
Handle test execution reports and update display.
299
300
Processes test results, updates progress bar, and displays
301
test status with appropriate symbols and colors.
302
303
Parameters:
304
- report: TestReport - test execution report
305
"""
306
307
def count(self, key: str, when: tuple = ("call",)) -> int:
308
"""
309
Count test results by status and execution phase.
310
311
Parameters:
312
- key: str - result status key (passed, failed, skipped, etc.)
313
- when: tuple - execution phases to include (default: ("call",))
314
315
Returns:
316
int - count of matching results
317
"""
318
319
def summary_stats(self) -> None:
320
"""Display final test result summary with counts and Playwright trace information."""
321
322
def _get_decoded_crashline(self, report: CollectReport) -> str:
323
"""
324
Get decoded crash line from test report.
325
326
Handles encoding issues when extracting crash line information
327
from test failure reports.
328
329
Parameters:
330
- report: CollectReport - collection or test report
331
332
Returns:
333
str - decoded crash line text
334
"""
335
336
def _get_lineno_from_report(self, report: CollectReport) -> int:
337
"""
338
Extract line number from test report.
339
340
Attempts multiple strategies to extract the line number where
341
a test failure occurred, including doctest and regular test failures.
342
343
Parameters:
344
- report: CollectReport - collection or test report
345
346
Returns:
347
int - line number where failure occurred
348
"""
349
350
def _find_playwright_trace(self, report: TestReport) -> Optional[str]:
351
"""
352
Find Playwright trace file associated with a specific test report.
353
354
Identifies the location of trace files using the test report's node ID
355
and configuration options. Provides commands to view traces for failed tests.
356
357
Parameters:
358
- report: TestReport - test report containing node ID and failure details
359
360
Returns:
361
Optional[str] - command to view Playwright trace or None if not found/disabled
362
"""
363
364
@staticmethod
365
def _convert_node_to_trace_name(nodeid: str) -> str:
366
"""
367
Convert pytest node ID to trace directory name format.
368
369
Transforms pytest node identifiers into the expected trace directory
370
naming convention used by Playwright.
371
372
Parameters:
373
- nodeid: str - pytest node identifier
374
375
Returns:
376
str - trace directory name
377
"""
378
379
def summary_failures(self) -> None:
380
"""Override to prevent default failure summary (handled via instant display)."""
381
382
def summary_errors(self) -> None:
383
"""Override to prevent default error summary (handled via instant display)."""
384
385
def print_failure(self, report: Union[CollectReport, TestReport]) -> None:
386
"""
387
Print test failure information immediately when failure occurs.
388
389
Parameters:
390
- report: Union[CollectReport, TestReport] - failed test report
391
"""
392
```
393
394
### Theme System
395
396
Configurable theme system for customizing visual appearance of test output.
397
398
```python { .api }
399
class Theme:
400
"""
401
Data class for customizing visual appearance and symbols.
402
403
Configurable through pytest-sugar.conf files.
404
"""
405
406
# Color configuration
407
header: Optional[str] = "magenta"
408
skipped: Optional[str] = "blue"
409
success: Optional[str] = "green"
410
warning: Optional[str] = "yellow"
411
fail: Optional[str] = "red"
412
error: Optional[str] = "red"
413
xfailed: Optional[str] = "green"
414
xpassed: Optional[str] = "red"
415
progressbar: Optional[str] = "green"
416
progressbar_fail: Optional[str] = "red"
417
progressbar_background: Optional[str] = "grey"
418
path: Optional[str] = "cyan"
419
name: Optional[str] = None
420
unknown: Optional[str] = "blue"
421
rerun: Optional[str] = "blue"
422
423
# Symbol configuration
424
symbol_passed: str = "✓"
425
symbol_skipped: str = "s"
426
symbol_failed: str = "⨯"
427
symbol_failed_not_call: str = "ₓ"
428
symbol_xfailed_skipped: str = "x"
429
symbol_xfailed_failed: str = "X"
430
symbol_unknown: str = "?"
431
symbol_rerun: Optional[str] = "R"
432
433
def __getitem__(self, x):
434
"""Access theme attributes by key for dynamic lookup."""
435
```
436
437
### Distributed Testing Support
438
439
Support for pytest-xdist distributed testing plugin.
440
441
```python { .api }
442
class DeferredXdistPlugin:
443
"""Plugin to handle pytest-xdist integration for distributed testing."""
444
445
def pytest_xdist_node_collection_finished(self, node, ids) -> None:
446
"""
447
Handle distributed collection completion.
448
449
Updates test count when using pytest-xdist for parallel test execution.
450
451
Parameters:
452
- node: xdist node object
453
- ids: list of test IDs collected by the node
454
"""
455
```
456
457
### Utility Functions
458
459
Helper functions for text processing and display calculations.
460
461
```python { .api }
462
def flatten(seq) -> Generator[Any, None, None]:
463
"""
464
Flatten nested sequences into a single generator.
465
466
Recursively flattens lists and tuples within the input sequence.
467
468
Parameters:
469
- seq: sequence to flatten (can contain nested lists/tuples)
470
471
Yields:
472
Any - flattened items from the input sequence
473
"""
474
475
def strip_colors(text: str) -> str:
476
"""
477
Remove ANSI color codes from text strings.
478
479
Uses regex to remove ANSI escape sequences for color formatting,
480
useful for text processing and length calculations.
481
482
Parameters:
483
- text: str - text containing ANSI color codes
484
485
Returns:
486
str - text with color codes removed
487
"""
488
489
def real_string_length(string: str) -> int:
490
"""
491
Calculate the real display length of a string after removing color codes.
492
493
Determines actual character width for proper terminal alignment
494
by first stripping ANSI color codes.
495
496
Parameters:
497
- string: str - string potentially containing color codes
498
499
Returns:
500
int - actual display length without color codes
501
"""
502
```
503
504
## Command Line Options
505
506
pytest-sugar adds several command-line options to customize its behavior:
507
508
```bash
509
# Show detailed test failures instead of one-line tracebacks
510
pytest --old-summary
511
512
# Force pytest-sugar output even if not in a real terminal
513
pytest --force-sugar
514
515
# Specify directory for Playwright trace files (default: test-results)
516
pytest --sugar-trace-dir custom-traces
517
518
# Disable Playwright trace file detection and display
519
pytest --sugar-no-trace
520
```
521
522
## Configuration
523
524
### Config File Format
525
526
Create `pytest-sugar.conf` in your project root or `~/.pytest-sugar.conf` in your home directory:
527
528
```ini
529
[theme]
530
# Color customization (use color names or None to disable)
531
success = green
532
fail = red
533
skipped = blue
534
warning = yellow
535
error = red
536
xfailed = green
537
xpassed = red
538
progressbar = green
539
progressbar_fail = red
540
progressbar_background = grey
541
path = cyan
542
name =
543
unknown = blue
544
rerun = blue
545
546
# Symbol customization
547
symbol_passed = ✓
548
symbol_skipped = s
549
symbol_failed = ⨯
550
symbol_failed_not_call = ₓ
551
symbol_xfailed_skipped = x
552
symbol_xfailed_failed = X
553
symbol_unknown = ?
554
symbol_rerun = R
555
556
[sugar]
557
# Progress bar length (integer or percentage)
558
progressbar_length = 20%
559
```
560
561
### Playwright Integration
562
563
pytest-sugar automatically detects and displays Playwright trace files for failed tests:
564
565
- Searches for trace files in the configured directory (default: `test-results`)
566
- Converts pytest node IDs to trace directory names
567
- Displays trace file paths and view commands in failure output
568
- Can be disabled with `--sugar-no-trace` option
569
570
## Types
571
572
```python { .api }
573
# Type definitions from typing module used in API signatures
574
from typing import Any, Dict, Generator, List, Optional, Sequence, TextIO, Tuple, Union
575
576
# pytest imports for type annotations
577
from _pytest.config import Config
578
from _pytest.config.argparsing import Parser
579
from _pytest.main import Session
580
from _pytest.nodes import Item
581
from _pytest.reports import BaseReport, CollectReport, TestReport
582
from _pytest.terminal import TerminalReporter
583
584
# External dependencies
585
from termcolor import colored
586
```
587
588
## Constants
589
590
```python { .api }
591
__version__: str = "1.1.1"
592
LEN_RIGHT_MARGIN: int = 0
593
LEN_PROGRESS_PERCENTAGE: int = 5
594
LEN_PROGRESS_BAR_SETTING: str = "10"
595
LEN_PROGRESS_BAR: Optional[int] = None
596
THEME: Theme # Global theme instance
597
PROGRESS_BAR_BLOCKS: List[str] # Unicode progress bar characters
598
IS_SUGAR_ENABLED: bool = False # Global enable flag
599
```