Skip to content

Utility Functions

The Utils module provides essential helper functions and utilities used throughout the project. These functions are designed to work seamlessly with the BaseExample class and are extensively used by the Calculator module.

Overview

This module contains standalone utility functions that provide common functionality like input validation, number formatting, and data processing. All functions are designed to be stateless and can be used independently or in combination with other modules.

Function Categories

Input Validation

Function Purpose Used By
validate_input(value) Validates and converts input data Calculator, BaseExample
check_type(value, expected_type) Type validation All modules
sanitize_string(text) String cleaning and validation Input processing

Data Formatting

Function Purpose Used By
format_number(number, precision=2) Number formatting with precision Calculator output
format_currency(amount) Currency formatting Financial calculations
format_percentage(ratio) Percentage display Statistical operations

Data Processing

Function Purpose Used By
process_list(items) List processing utilities Batch operations
merge_dicts(dict1, dict2) Dictionary merging Configuration management
deep_copy(obj) Safe object copying Data manipulation

Usage Examples

Basic Validation

from src.example.utils import validate_input, format_number
from src.example.calculator import Calculator

# Validate and format user input
user_input = "42.5"
validated_number = validate_input(user_input)
formatted_output = format_number(validated_number, precision=3)

# Use with Calculator
calc = Calculator()
result = calc.add(validated_number, 10)
print(format_number(result))

Advanced Processing

from src.example.utils import process_list, merge_dicts
from src.example.base import BaseExample

# Process multiple values
numbers = ["1", "2", "3.14", "42"]
validated_numbers = process_list(numbers, validator=validate_input)

# Merge configurations
base_config = BaseExample.default_config()
custom_config = {"precision": 4, "strict_mode": True}
final_config = merge_dicts(base_config, custom_config)

Configuration

The utils module supports configuration for default behaviors:

# Global configuration
UTILS_CONFIG = {
    'default_precision': 2,
    'strict_validation': True,
    'error_on_invalid': False,
    'locale_formatting': 'en_US'
}

Configuration Options

Option Type Default Description
default_precision int 2 Default decimal places for number formatting
strict_validation bool True Enable strict input validation
error_on_invalid bool False Raise errors on invalid input vs. returning None
locale_formatting str 'en_US' Locale for number/currency formatting

Error Handling

The utils module implements consistent error handling patterns:

Exception Types

  • ValidationError - Input validation failures
  • FormattingError - Output formatting issues
  • ProcessingError - Data processing problems

Error Handling Patterns

from src.example.utils import validate_input, ValidationError

try:
    result = validate_input(user_data)
except ValidationError as e:
    logger.warning(f"Invalid input: {e}")
    # Handle gracefully or use default
    result = default_value

Integration Patterns

With BaseExample

class CustomExample(BaseExample):
    def process(self, data):
        # Use utils for validation
        validated_data = validate_input(data)

        # Process using base methods
        result = super().process(validated_data)

        # Format output using utils
        return format_number(result)

With Calculator

calc = Calculator()

# All calculator operations use utils internally
def safe_calculate(operation, a, b):
    """Safe calculation with validation and formatting."""
    # Validation happens inside calculator methods
    result = getattr(calc, operation)(a, b)

    # Format using utils
    return format_number(result, precision=4)

Performance Considerations

  • Caching: Validation functions use internal caching for repeated inputs
  • Lazy Loading: Configuration loaded only when needed
  • Memory Efficient: Functions designed to minimize memory usage
  • Thread Safe: All utility functions are thread-safe

Testing Utilities

The utils module includes testing helpers:

from src.example.utils import UtilsTestCase

class TestMyUtilsUsage(UtilsTestCase):
    def test_validation_pipeline(self):
        """Test complete validation and formatting pipeline."""
        # Use inherited utilities
        self.assert_valid_input("42")
        self.assert_formatted_output(42.0, "42.00")

Migration and Compatibility

When upgrading utils functions:

  1. Backward Compatibility: All functions maintain backward compatibility
  2. Deprecation Warnings: Old patterns show deprecation warnings
  3. Migration Guides: Available in upgrade documentation

Best Practices

Function Usage

  1. Always Validate: Use validate_input() for all user input
  2. Consistent Formatting: Use format_number() for all numeric output
  3. Error Handling: Always handle ValidationError and FormattingError
  4. Type Checking: Use check_type() for runtime type validation

Performance Tips

  1. Batch Processing: Use process_list() for multiple items
  2. Configuration Caching: Cache configuration objects when possible
  3. Avoid Repeated Validation: Cache validated inputs when reused

Examples and Tutorials

For detailed examples:

API Reference

Utility functions for the example package.

This module contains helper functions and decorators that can be used throughout the application.

Classes

DateFormatter

DateFormatter(default_format: str = '%Y-%m-%d %H:%M:%S')

Format dates in various formats.

This class provides methods to format datetime objects into different string representations.

Attributes:

Name Type Description
default_format

Default format string for dates

Example
formatter = DateFormatter()
now = datetime.now()

# Use default format
print(formatter.format(now))

# Use custom format
print(formatter.format(now, "%B %d, %Y"))

# Get relative time
print(formatter.relative_time(now))

Initialize formatter with default format.

Parameters:

Name Type Description Default
default_format str

Default strftime format string

'%Y-%m-%d %H:%M:%S'
Source code in src/example/utils.py
def __init__(self, default_format: str = "%Y-%m-%d %H:%M:%S") -> None:
    """Initialize formatter with default format.

    Args:
        default_format: Default strftime format string
    """
    self.default_format = default_format

Functions

format
format(date: datetime, format_string: Optional[str] = None) -> str

Format a datetime object.

Parameters:

Name Type Description Default
date datetime

The datetime to format

required
format_string Optional[str]

Optional format string (uses default if None)

None

Returns:

Type Description
str

Formatted date string

Example

formatter = DateFormatter() date = datetime(2024, 1, 15, 14, 30, 45) formatter.format(date) '2024-01-15 14:30:45'

Source code in src/example/utils.py
def format(self, date: datetime, format_string: Optional[str] = None) -> str:
    """Format a datetime object.

    Args:
        date: The datetime to format
        format_string: Optional format string (uses default if None)

    Returns:
        Formatted date string

    Example:
        >>> formatter = DateFormatter()
        >>> date = datetime(2024, 1, 15, 14, 30, 45)
        >>> formatter.format(date)
        '2024-01-15 14:30:45'
    """
    fmt = format_string or self.default_format
    return date.strftime(fmt)
parse_iso staticmethod
parse_iso(date_string: str) -> datetime

Parse ISO format date string.

Parameters:

Name Type Description Default
date_string str

ISO format date string

required

Returns:

Type Description
datetime

Parsed datetime object

Raises:

Type Description
ValueError

If date string is invalid

Example

DateFormatter.parse_iso("2024-01-15T14:30:45") datetime.datetime(2024, 1, 15, 14, 30, 45)

Source code in src/example/utils.py
@staticmethod
def parse_iso(date_string: str) -> datetime:
    """Parse ISO format date string.

    Args:
        date_string: ISO format date string

    Returns:
        Parsed datetime object

    Raises:
        ValueError: If date string is invalid

    Example:
        >>> DateFormatter.parse_iso("2024-01-15T14:30:45")
        datetime.datetime(2024, 1, 15, 14, 30, 45)
    """
    return datetime.fromisoformat(date_string)
relative_time
relative_time(date: datetime) -> str

Get relative time string (e.g., "2 hours ago").

Parameters:

Name Type Description Default
date datetime

The datetime to compare against now

required

Returns:

Type Description
str

Human-readable relative time string

Example

formatter = DateFormatter() past = datetime.now() - timedelta(hours=2) formatter.relative_time(past) '2 hours ago'

Source code in src/example/utils.py
def relative_time(self, date: datetime) -> str:
    """Get relative time string (e.g., "2 hours ago").

    Args:
        date: The datetime to compare against now

    Returns:
        Human-readable relative time string

    Example:
        >>> formatter = DateFormatter()
        >>> past = datetime.now() - timedelta(hours=2)
        >>> formatter.relative_time(past)
        '2 hours ago'
    """
    now = datetime.now()
    delta = now - date

    seconds = delta.total_seconds()

    if seconds < 60:
        return "just now"
    elif seconds < 3600:
        minutes = int(seconds / 60)
        return f"{minutes} minute{'s' if minutes != 1 else ''} ago"
    elif seconds < 86400:
        hours = int(seconds / 3600)
        return f"{hours} hour{'s' if hours != 1 else ''} ago"
    else:
        days = int(seconds / 86400)
        return f"{days} day{'s' if days != 1 else ''} ago"

Functions

format_number

format_number(number: Union[int, float], decimals: int = 2, thousands_separator: bool = True) -> str

Format a number for display.

Parameters:

Name Type Description Default
number Union[int, float]

The number to format

required
decimals int

Number of decimal places (default: 2)

2
thousands_separator bool

Whether to use thousands separator (default: True)

True

Returns:

Type Description
str

Formatted number as string

Example

format_number(1234567.89) '1,234,567.89' format_number(1234567.89, decimals=0) '1,234,568' format_number(1234.5, thousands_separator=False) '1234.50'

See Also
  • :func:validate_input: Validate numeric input
  • locale.format_string: For locale-specific formatting
Source code in src/example/utils.py
def format_number(
    number: Union[int, float], decimals: int = 2, thousands_separator: bool = True
) -> str:
    """Format a number for display.

    Args:
        number: The number to format
        decimals: Number of decimal places (default: 2)
        thousands_separator: Whether to use thousands separator (default: True)

    Returns:
        Formatted number as string

    Example:
        >>> format_number(1234567.89)
        '1,234,567.89'
        >>> format_number(1234567.89, decimals=0)
        '1,234,568'
        >>> format_number(1234.5, thousands_separator=False)
        '1234.50'

    See Also:
        - :func:`validate_input`: Validate numeric input
        - `locale.format_string`: For locale-specific formatting
    """
    if thousands_separator:
        return f"{number:,.{decimals}f}"
    else:
        return f"{number:.{decimals}f}"

timer

timer(func: F) -> F

Decorator to measure function execution time.

This decorator wraps a function and prints the time taken to execute it.

Parameters:

Name Type Description Default
func F

The function to time

required

Returns:

Type Description
F

Wrapped function that prints execution time

Example
@timer
def slow_function():
    time.sleep(1)
    return "Done"

result = slow_function()
# Output: slow_function took 1.0012 seconds
Note

The timing includes all operations within the function, including any I/O operations.

Source code in src/example/utils.py
def timer(func: F) -> F:
    """Decorator to measure function execution time.

    This decorator wraps a function and prints the time taken
    to execute it.

    Args:
        func: The function to time

    Returns:
        Wrapped function that prints execution time

    Example:
        ```python
        @timer
        def slow_function():
            time.sleep(1)
            return "Done"

        result = slow_function()
        # Output: slow_function took 1.0012 seconds
        ```

    Note:
        The timing includes all operations within the function,
        including any I/O operations.
    """

    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result

    return wrapper  # type: ignore

validate_input

validate_input(value: Any, expected_type: type, min_value: Optional[Union[int, float]] = None, max_value: Optional[Union[int, float]] = None, allow_none: bool = False) -> bool

Validate input value against constraints.

This function checks if a value meets specified type and range constraints.

Parameters:

Name Type Description Default
value Any

The value to validate

required
expected_type type

Expected type of the value

required
min_value Optional[Union[int, float]]

Minimum allowed value (for numeric types)

None
max_value Optional[Union[int, float]]

Maximum allowed value (for numeric types)

None
allow_none bool

Whether None is a valid value

False

Returns:

Type Description
bool

True if value is valid, False otherwise

Example

validate_input(42, int, min_value=0, max_value=100) True validate_input(-5, int, min_value=0) False validate_input(None, str, allow_none=True) True

Warning

This function only performs basic validation. For complex validation logic, consider using a dedicated validation library like pydantic or cerberus.

Source code in src/example/utils.py
def validate_input(
    value: Any,
    expected_type: type,
    min_value: Optional[Union[int, float]] = None,
    max_value: Optional[Union[int, float]] = None,
    allow_none: bool = False,
) -> bool:
    """Validate input value against constraints.

    This function checks if a value meets specified type and range
    constraints.

    Args:
        value: The value to validate
        expected_type: Expected type of the value
        min_value: Minimum allowed value (for numeric types)
        max_value: Maximum allowed value (for numeric types)
        allow_none: Whether None is a valid value

    Returns:
        True if value is valid, False otherwise

    Example:
        >>> validate_input(42, int, min_value=0, max_value=100)
        True
        >>> validate_input(-5, int, min_value=0)
        False
        >>> validate_input(None, str, allow_none=True)
        True

    Warning:
        This function only performs basic validation. For complex
        validation logic, consider using a dedicated validation library
        like `pydantic` or `cerberus`.
    """
    if value is None:
        return allow_none

    if not isinstance(value, expected_type):
        return False

    if expected_type in (int, float):
        if min_value is not None and value < min_value:
            return False
        if max_value is not None and value > max_value:
            return False

    return True