# Example: Docstring for Complex Function

This example shows how to write comprehensive docstrings for complex functions with multiple parameters, error handling, and edge cases.

## Example Function: Retry Decorator with Exponential Backoff

### ✅ Good Example (Complete Docstring)

```python
from typing import TypeVar, Callable, Any, Type, Optional
from functools import wraps
import time
import logging

T = TypeVar('T')
logger = logging.getLogger(__name__)


def retry_with_backoff(
    max_retries: int = 3,
    base_delay: float = 1.0,
    max_delay: float = 60.0,
    exponential_base: float = 2.0,
    exceptions: tuple[Type[Exception], ...] = (Exception,),
    logger_name: Optional[str] = None,
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """Retry a function with exponential backoff on specified exceptions.

    This decorator retries a function call if it raises one of the specified
    exceptions, using exponential backoff between attempts. The delay starts
    at `base_delay` and multiplies by `exponential_base` after each failure,
    up to `max_delay`.

    The decorator is useful for handling transient failures in network requests,
    database operations, or external API calls where temporary errors are
    expected and retries may succeed.

    Args:
        max_retries: Maximum number of retry attempts before giving up.
            Must be non-negative. Default is 3 retries (4 total attempts).
        base_delay: Initial delay in seconds before the first retry.
            Must be positive. Default is 1.0 second.
        max_delay: Maximum delay in seconds between retries, regardless of
            exponential calculation. Must be >= base_delay. Default is 60.0 seconds.
        exponential_base: Base for exponential backoff calculation. The delay
            for attempt N is min(base_delay * (exponential_base ** N), max_delay).
            Must be > 1.0. Default is 2.0 (doubles each time).
        exceptions: Tuple of exception types to catch and retry. Other exceptions
            will propagate immediately without retry. Default is (Exception,),
            which catches all exceptions.
        logger_name: Optional logger name for retry logging. If None, uses the
            logger for this module. Useful for tracking retries in application logs.

    Returns:
        A decorator function that wraps the target function with retry logic.
        The wrapped function has the same signature and return type as the
        original function.

    Raises:
        ValueError: If max_retries < 0, base_delay <= 0, max_delay < base_delay,
            or exponential_base <= 1.0.
        TypeError: If exceptions is not a tuple of exception types.
        <Last exception from wrapped function>: If all retries are exhausted,
            the last exception raised by the wrapped function is re-raised.

    Example:
        Basic usage with default settings:

        >>> @retry_with_backoff(max_retries=3)
        ... def fetch_data(url: str) -> dict:
        ...     response = requests.get(url)
        ...     response.raise_for_status()
        ...     return response.json()
        ...
        >>> data = fetch_data("https://api.example.com/data")
        # Retries up to 3 times with delays: 1s, 2s, 4s

        Custom retry configuration for database operations:

        >>> @retry_with_backoff(
        ...     max_retries=5,
        ...     base_delay=0.5,
        ...     max_delay=30.0,
        ...     exceptions=(DatabaseError, ConnectionError),
        ...     logger_name="db.retries"
        ... )
        ... def save_user(user: User) -> int:
        ...     return db.session.add(user)
        ...
        >>> user_id = save_user(new_user)
        # Retries up to 5 times on database/connection errors only
        # Delays: 0.5s, 1.0s, 2.0s, 4.0s, 8.0s

        Using with async functions:

        >>> @retry_with_backoff(max_retries=3, exceptions=(asyncio.TimeoutError,))
        ... async def fetch_async(url: str) -> bytes:
        ...     async with aiohttp.ClientSession() as session:
        ...         async with session.get(url) as response:
        ...             return await response.read()

    Notes:
        - The decorator counts retries, not total attempts. With max_retries=3,
          the function is called up to 4 times (1 initial + 3 retries).
        - Delays are applied BEFORE retry attempts, not after the initial call.
        - The decorator is thread-safe and can be used with concurrent calls.
        - For async functions, use time.sleep() replacement like asyncio.sleep()
          by modifying the decorator implementation.
        - All retry attempts are logged at INFO level with timing information.

    See Also:
        - tenacity library: More feature-rich retry implementation
        - backoff library: Alternative with additional backoff strategies
        - Circuit breaker pattern: For handling cascading failures
    """
    # Validate input parameters
    if max_retries < 0:
        raise ValueError(f"max_retries must be non-negative, got {max_retries}")
    if base_delay <= 0:
        raise ValueError(f"base_delay must be positive, got {base_delay}")
    if max_delay < base_delay:
        raise ValueError(
            f"max_delay ({max_delay}) must be >= base_delay ({base_delay})"
        )
    if exponential_base <= 1.0:
        raise ValueError(
            f"exponential_base must be > 1.0, got {exponential_base}"
        )
    if not isinstance(exceptions, tuple) or not all(
        isinstance(e, type) and issubclass(e, BaseException) for e in exceptions
    ):
        raise TypeError("exceptions must be a tuple of exception types")

    # Get logger
    retry_logger = logging.getLogger(logger_name) if logger_name else logger

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> T:
            last_exception: Optional[Exception] = None

            for attempt in range(max_retries + 1):
                try:
                    result = func(*args, **kwargs)
                    if attempt > 0:
                        retry_logger.info(
                            f"{func.__name__} succeeded on attempt {attempt + 1}"
                        )
                    return result
                except exceptions as e:
                    last_exception = e
                    if attempt < max_retries:
                        delay = min(
                            base_delay * (exponential_base ** attempt),
                            max_delay
                        )
                        retry_logger.info(
                            f"{func.__name__} failed on attempt {attempt + 1}, "
                            f"retrying in {delay:.2f}s: {e}"
                        )
                        time.sleep(delay)
                    else:
                        retry_logger.error(
                            f"{func.__name__} failed after {max_retries + 1} attempts"
                        )

            # All retries exhausted, re-raise the last exception
            assert last_exception is not None
            raise last_exception

        return wrapper
    return decorator
```

### ❌ Bad Example (Missing/Incomplete Docstring)

```python
def retry_with_backoff(max_retries=3, base_delay=1.0, max_delay=60.0,
                       exponential_base=2.0, exceptions=(Exception,),
                       logger_name=None):
    """Retry function with exponential backoff."""
    # Implementation...
```

**Problems:**
- ❌ No description of algorithm or use cases
- ❌ No parameter descriptions
- ❌ No return value description
- ❌ No exception documentation
- ❌ No usage examples
- ❌ No notes about threading, async, or edge cases

---

## Key Elements of Complex Function Docstrings

### 1. **Detailed Description**
- Explain WHAT the function does
- Explain WHY you'd use it (use cases)
- Explain HOW it works (algorithm overview)
- Include any important context or background

### 2. **Complete Args Section**
- Document EVERY parameter
- Include type information
- Explain valid ranges/values
- Mention defaults and their significance
- Explain parameter interactions

### 3. **Clear Returns Section**
- Describe the return value
- Include type information
- Explain different return scenarios
- Document return value structure for complex types

### 4. **Comprehensive Raises Section**
- List ALL exceptions that can be raised
- Include exceptions from validation
- Include exceptions from wrapped code
- Explain WHEN each exception occurs

### 5. **Multiple Examples**
- Basic usage example
- Advanced usage example
- Edge case examples
- Real-world scenarios
- Show expected output

### 6. **Important Notes**
- Thread safety
- Performance considerations
- Async/await compatibility
- Edge cases and limitations
- Common pitfalls

### 7. **See Also Section**
- Related functions
- Alternative implementations
- External libraries
- Design patterns
- Documentation links

---

## Docstring Template for Complex Functions

```python
def complex_function(
    required_param: RequiredType,
    optional_param: OptionalType = default_value,
    *args: VarArgsType,
    **kwargs: KwargsType,
) -> ReturnType:
    """One-line summary (< 80 chars).

    Detailed description explaining:
    - What this function does
    - Why you'd use it (use cases)
    - How it works (algorithm overview)
    - Important context or background

    Additional paragraphs as needed for complex logic,
    algorithms, or important implementation details.

    Args:
        required_param: Description of required parameter.
            Include valid ranges, formats, or constraints.
        optional_param: Description of optional parameter.
            Explain the default value and its significance.
            Default: default_value.
        *args: Description of variable positional arguments.
            When and how they're used.
        **kwargs: Description of variable keyword arguments.
            Supported keys and their meanings:
            - key1 (type): Description
            - key2 (type): Description

    Returns:
        Description of return value. For complex types:
        - Structure explanation
        - Field meanings
        - Different return scenarios

    Raises:
        ValueError: When and why this is raised.
            Include specific conditions.
        TypeError: When and why this is raised.
        CustomException: When and why this is raised.

    Example:
        Basic usage:

        >>> result = complex_function("required", optional=42)
        >>> print(result)
        expected_output

        Advanced usage:

        >>> result = complex_function(
        ...     "required",
        ...     optional=100,
        ...     extra_key="value"
        ... )
        >>> # Additional example code

        Edge case:

        >>> # Show handling of special scenarios

    Notes:
        - Thread safety information
        - Performance considerations
        - Async/await compatibility
        - Edge cases and limitations
        - Common pitfalls to avoid

    See Also:
        - related_function: Brief explanation
        - ExternalLibrary: Why you might use it instead
        - Design pattern name: Relevant pattern link
    """
    # Implementation...
```

---

## When to Write This Level of Detail

**Always write comprehensive docstrings for:**

- ✅ Public API functions used by external code
- ✅ Complex algorithms or business logic
- ✅ Functions with >3 parameters
- ✅ Functions that raise multiple exceptions
- ✅ Functions with subtle behavior or edge cases
- ✅ Decorators, context managers, and metaclasses
- ✅ Functions that will be used by junior developers

**Simpler docstrings are OK for:**

- ℹ️  Private helper functions (still document purpose!)
- ℹ️  Obvious operations with 1-2 parameters
- ℹ️  Functions with no exceptions or return values
- ℹ️  Simple getters/setters

---

## Related Documentation

- **Docstring prompt template**: `prompts/documentation/add-docstrings.md`
- **Google Python Style Guide**: Standard docstring format
- **Engineering Standards**: `.claude/standards/ENGINEERING_STANDARDS.md#docstrings`
- **Good PR example**: `examples/documentation/good-pr-example.md`
