Skip to main content

Pytest plugin providing debug fixtures, ANSI-stripped capsys, whitespace-visible assertions, and terminal column management.

Project description

pytest-devtools

Small pytest plugin that provides some niceties for writing and debugging tests.

Features

  • Debug fixture -- Pretty-print variables, paths, and data structures with Rich. Output appears only when tests fail (or always with --print-debug).
  • ANSI-stripped capsys -- Automatically strip ANSI escape sequences from captured output so assertions don't break on color codes.
  • Visible whitespace -- Replace invisible whitespace characters (tabs, trailing spaces, carriage returns, newlines) with Unicode symbols in assertion failure diffs.
  • Terminal column width -- Optionally set the COLUMNS environment variable so Rich and other terminal-aware libraries don't introduce unwanted line wraps.

Installation

# Using uv
uv add pytest-devtools

# Using pip
pip install pytest-devtools

Requirements: Python 3.11+ and pytest 7.0+

The plugin activates automatically once installed. No conftest.py changes are needed.

Debug Fixture

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.

Basic Usage

def test_user_creation(debug, tmp_path):
    user = {"name": "Alice", "roles": ["admin", "editor"]}
    debug(user)

    config_path = tmp_path / "config.toml"
    config_path.write_text("[settings]\nverbose = true")
    debug(config_path)

    assert user["name"] == "Alice"

On failure, stderr shows the Rich-formatted output between rule separators:

──────────────────────────── Debug ─────────────────────────────
{'name': 'Alice', 'roles': ['admin', 'editor']}
──────────────────────────── Debug ─────────────────────────────

Multiple Values and Titles

Pass multiple arguments in a single call, and use title to label sections:

def test_transform(debug):
    before = [1, 2, 3]
    after = [x * 2 for x in before]
    debug(before, after, title="Transform")

Per-Call Options

Every option can be overridden on a per-call basis:

def test_deep_structure(debug, tmp_path):
    nested = {"a": {"b": {"c": {"d": "deep"}}}}

    # Limit nesting depth
    debug(nested, max_depth=2)

    # Limit collection length
    debug(list(range(100)), max_length=5)

    # Show type annotations
    debug(nested, show_type=True)

    # Show directory tree for Path objects
    debug(tmp_path, list_dir_contents=True)

    # Disable tmp_path prefix stripping
    debug(tmp_path / "output.txt", strip_tmp_path=False)

Path Handling

When you pass a pathlib.Path to debug:

  • 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.
  • Directory listing (default: off) -- When enabled and the path is a directory, a Rich tree shows the full directory contents recursively.

CLI Options

Flag Description
--print-debug Always show debug output, even on passing tests
--debug-strip-tmp-path Strip tmp_path prefix from Path objects (default)
--no-debug-strip-tmp-path Show full absolute paths
--debug-list-dir-contents Show directory tree for Path directories
--no-debug-list-dir-contents Don't list directory contents (default)
--debug-max-depth=N Limit nesting depth in pretty-printed output
--debug-max-length=N Limit collection length in pretty-printed output
--debug-show-type Show type annotations above each value
--no-debug-show-type Don't show type annotations (default)

INI Options

Add these to pyproject.toml under [tool.pytest.ini_options]:

[tool.pytest.ini_options]
print_debug = true
debug_strip_tmp_path = true
debug_list_dir_contents = false
debug_max_depth = 4
debug_max_length = 20
debug_show_type = false

Option Precedence

Per-call arguments take highest priority, then CLI flags, then INI settings:

per-call override  >  CLI flag  >  INI option  >  built-in default

ANSI-Stripped capsys

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.).

Basic Usage

def test_greeting(capsys):
    # Imagine this function uses Rich to print colored output
    print("\x1b[32mHello, world!\x1b[0m")

    captured = capsys.readouterr()
    assert captured.out == "Hello, world!\n"  # No ANSI codes to worry about

Keeping ANSI Codes

For tests that need to verify color output, disable stripping per-test with a marker:

import pytest

@pytest.mark.keep_ansi
def test_color_codes(capsys):
    print("\x1b[32mgreen\x1b[0m")
    captured = capsys.readouterr()
    assert "\x1b[32m" in captured.out

Or disable stripping globally with a CLI flag:

pytest --no-strip-ansi

INI Option

[tool.pytest.ini_options]
strip_ansi = false

Visible Whitespace in Assertions

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.

Symbol Reference

Character Symbol Name
Trailing space · Middle dot
Tab (\t) Rightwards arrow
Carriage return (\r) Leftwards arrow
Newline (\n) Return symbol

Example Output

For a test like:

def test_output():
    assert "hello " == "hello"

The failure message shows:

AssertionError: 'hello·' == 'hello'

Whitespace-visible comparison:
  Left:  'hello·'
  Right: 'hello'

Disabling

Disable with the --no-show-whitespace CLI flag or in INI:

[tool.pytest.ini_options]
show_whitespace = false

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.

Terminal Column Width

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.

This feature is disabled by default. Enable it with the --columns CLI flag or via INI options.

CLI Option

Set COLUMNS for a single run:

pytest --columns=180

INI Options

Enable permanently in pyproject.toml:

[tool.pytest.ini_options]
set_columns = true   # Enable the feature
columns = 180        # Value to set (default when enabled)

The --columns CLI flag overrides the INI columns value when both are present.

Configuration Summary

All features can be configured via CLI flags, pyproject.toml INI options, or (for the debug fixture) per-call arguments.

Feature Enabled by default Enable/Disable with
Debug fixture Output on failure only N/A (always available)
ANSI stripping Yes --no-strip-ansi or strip_ansi = false
Visible whitespace Yes --no-show-whitespace or show_whitespace = false
Column width No --columns=N or set_columns = true

AI Policy

All AI generated content is and always will be meticulously reviewed and approved by the author.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_devtools-1.0.0.tar.gz (11.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_devtools-1.0.0-py3-none-any.whl (12.8 kB view details)

Uploaded Python 3

File details

Details for the file pytest_devtools-1.0.0.tar.gz.

File metadata

  • Download URL: pytest_devtools-1.0.0.tar.gz
  • Upload date:
  • Size: 11.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for pytest_devtools-1.0.0.tar.gz
Algorithm Hash digest
SHA256 57f4ba55f159bd7cf0872c2500ca19b99790cc8a417172069f55696451abe43d
MD5 0eddc0ffa80e2b77fcba907dbedf9a08
BLAKE2b-256 fe29315fa195601aab6ede0cc67ac9edb939f523774edb812be5d8a6ed91f89f

See more details on using hashes here.

File details

Details for the file pytest_devtools-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_devtools-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2dded6be7bcd6e954b4e3a539099a4317b2ed426b3a8e19173f9bc1ca8b02879
MD5 7aca28f6e40f7f97f004ee45029576e7
BLAKE2b-256 201a56abf4e04c1f13ff588a013762279d7c21ace4569c6feac6c97cc786ff78

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page