CQ007: DoctestCheck

Overview

Property Value
ID CQ007
Name DoctestCheck
Group code-quality
Severity WARNING

Description

Verifies that doctest examples in docstrings execute correctly.

Doctests serve dual purposes:

  • Documentation: Show users exactly how to use functions with expected output
  • Verification: Ensure examples remain accurate as code evolves

This check ensures your documentation examples actually work.

What it checks

The check scans all Python files in the package (excluding tests and hidden directories), extracts doctest examples from docstrings, and runs them:

  • PASSED: All doctest examples produce expected output
  • FAILED: One or more examples fail (wrong output or exceptions)
  • NOT_APPLICABLE: No doctest examples found in the package

How to fix

Run doctests manually

# Run doctests on a specific file
python -m doctest -v src/mypackage/module.py

# Run doctests on all files
python -m pytest --doctest-modules src/

Fix incorrect expected output

# Before (wrong expected output)
def add(a: int, b: int) -> int:
    """Add two numbers.

    Examples:
        >>> add(1, 2)
        4
    """
    return a + b

# After (correct expected output)
def add(a: int, b: int) -> int:
    """Add two numbers.

    Examples:
        >>> add(1, 2)
        3
    """
    return a + b

Use ellipsis for variable output

For output that varies (memory addresses, timestamps, etc.), use # doctest: +ELLIPSIS:

def get_object():
    """Get an object.

    Examples:
        >>> get_object()  # doctest: +ELLIPSIS
        <__main__.MyClass object at 0x...>
    """
    return MyClass()

Handle dictionary output

Dictionary output order may vary. Use ellipsis or normalize:

def get_config():
    """Get configuration.

    Examples:
        >>> sorted(get_config().items())
        [('a', 1), ('b', 2)]
    """
    return {'a': 1, 'b': 2}

Skip doctests conditionally

For platform-specific or optional-dependency doctests:

def windows_only():
    """Windows-specific function.

    Examples:
        >>> windows_only()  # doctest: +SKIP
        'windows result'
    """
    ...

Common doctest directives

Directive Purpose
+ELLIPSIS Match ... in output
+NORMALIZE_WHITESPACE Ignore whitespace differences
+SKIP Skip this example
+IGNORE_EXCEPTION_DETAIL Match exception type only

Writing good doctests

Do

def greet(name: str) -> str:
    """Return a greeting.

    Examples:
        >>> greet("World")
        'Hello, World!'
        >>> greet("Python")
        'Hello, Python!'
    """
    return f"Hello, {name}!"

Avoid

def greet(name: str) -> str:
    """Return a greeting.

    Examples:
        >>> # This is fragile - relies on external state
        >>> import os; os.environ['NAME'] = 'Test'
        >>> greet(os.environ['NAME'])
        'Hello, Test!'
    """
    return f"Hello, {name}!"

Configuration

Skip patterns

Skip specific files from doctest checking:

[tool.pycmdcheck.checks.CQ007]
skip_patterns = [
    "**/conftest.py",
    "**/migrations/*.py",
]

Skip this check

[tool.pycmdcheck]
skip = ["CQ007"]

CLI

pycmdcheck --skip CQ007

References