CQ011: FunctionLength

Overview

Property Value
ID CQ011
Name FunctionLength
Group code-quality
Severity NOTE

Description

Warns when functions exceed a configurable line threshold (default: 50 lines).

Long functions indicate:

  • Multiple responsibilities: Functions doing too many things
  • Poor abstraction: Missing helper functions or classes
  • Difficult testing: Hard to test individual behaviors
  • Code smells: Often accompanied by deep nesting and complexity

This check uses AST parsing to measure function length from definition to end.

What it checks

The check scans all Python files in the package (excluding hidden directories and __pycache__), measures the line count of each function and method, and reports those exceeding the threshold:

  • PASSED: All functions are within the line limit
  • FAILED: One or more functions exceed the limit
  • NOT_APPLICABLE: Package directory not found

How length is calculated

Function length is measured from the def line to the last line of the function body, inclusive of:

  • Docstrings
  • Comments
  • Blank lines within the function
  • All nested blocks and statements
def example():      # Line 1
    """Docstring.   # Line 2

    More docs.      # Line 4
    """             # Line 5
    x = 1           # Line 6
    return x        # Line 7
# Total: 7 lines

How to fix

Extract helper functions

# Before - 60 lines
def process_order(order):
    # Validate order (20 lines)
    if not order.customer:
        raise ValueError("No customer")
    if not order.items:
        raise ValueError("No items")
    # ... more validation

    # Calculate totals (20 lines)
    subtotal = sum(item.price for item in order.items)
    tax = subtotal * 0.08
    # ... more calculations

    # Send notifications (20 lines)
    send_email(order.customer.email, ...)
    # ... more notifications

# After - each function focused
def process_order(order):
    validate_order(order)
    totals = calculate_totals(order)
    send_order_notifications(order, totals)
    return totals

def validate_order(order):
    """Validate order has required fields."""
    if not order.customer:
        raise ValueError("No customer")
    if not order.items:
        raise ValueError("No items")

def calculate_totals(order):
    """Calculate order subtotal, tax, and total."""
    subtotal = sum(item.price for item in order.items)
    tax = subtotal * TAX_RATE
    return OrderTotals(subtotal=subtotal, tax=tax)

def send_order_notifications(order, totals):
    """Send order confirmation emails."""
    send_email(order.customer.email, ...)

Use early returns to reduce nesting

# Before
def process(data):
    if data:
        if data.valid:
            if data.ready:
                # 40 lines of processing
                ...

# After
def process(data):
    if not data:
        return None
    if not data.valid:
        raise ValueError("Invalid data")
    if not data.ready:
        return pending_result()

    return _process_ready_data(data)

Extract complex conditionals

# Before
def should_process(item):
    if (item.status == "active" and
        item.created_at > threshold and
        item.owner in allowed_users and
        not item.is_deleted and
        item.type in ALLOWED_TYPES):
        # processing logic...

# After
def should_process(item):
    if not is_eligible(item):
        return False
    return _do_processing(item)

def is_eligible(item):
    return (
        item.status == "active" and
        item.created_at > threshold and
        item.owner in allowed_users and
        not item.is_deleted and
        item.type in ALLOWED_TYPES
    )

Configuration

Adjust line threshold

[tool.pycmdcheck.checks.CQ011]
max_function_lines = 30  # Stricter limit
[tool.pycmdcheck.checks.CQ011]
max_function_lines = 100  # More lenient

Skip this check

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

CLI

pycmdcheck --skip CQ011

References