Files
2025-11-30 08:51:46 +08:00

11 KiB

name, description
name description
docstring-format Automatically applies when writing function docstrings. Uses Google-style format with Args, Returns, Raises, Examples, and Security Note sections for proper documentation.

Docstring Format Enforcer

Use Google-style docstrings with proper sections for all functions and classes.

Standard Function Docstring

def calculate_total(items: List[Item], tax_rate: float, discount: Optional[float] = None) -> Decimal:
    """
    Calculate total price including tax and optional discount.

    Computes the subtotal from items, applies discount if provided,
    then adds tax to get final total.

    Args:
        items: List of items with price and quantity
        tax_rate: Tax rate as decimal (e.g., 0.08 for 8%)
        discount: Optional discount as decimal (e.g., 0.10 for 10% off)

    Returns:
        Total price as Decimal with 2 decimal places

    Raises:
        ValueError: If tax_rate is negative or > 1
        ValueError: If discount is negative or > 1

    Example:
        >>> items = [Item(price=10.00, quantity=2)]
        >>> calculate_total(items, tax_rate=0.08)
        Decimal('21.60')
    """
    if tax_rate < 0 or tax_rate > 1:
        raise ValueError("tax_rate must be between 0 and 1")

    subtotal = sum(item.price * item.quantity for item in items)

    if discount:
        if discount < 0 or discount > 1:
            raise ValueError("discount must be between 0 and 1")
        subtotal = subtotal * (1 - discount)

    total = subtotal * (1 + tax_rate)
    return round(total, 2)

Async Function with Security Note

async def fetch_user_payment_methods(user_id: str, include_expired: bool = False) -> List[PaymentMethod]:
    """
    Fetch payment methods for a user.

    Retrieves all payment methods from database, optionally filtering
    out expired cards. Payment tokens are included for transaction use.

    Args:
        user_id: User's unique identifier (MongoDB ObjectId)
        include_expired: Whether to include expired payment methods

    Returns:
        List of PaymentMethod objects containing:
        - token: Payment token for transactions (handle securely)
        - last_four: Last 4 digits of card
        - expiry: Expiration date (MM/YY format)
        - brand: Card brand (visa, mastercard, etc.)

    Raises:
        UserNotFoundError: If user_id doesn't exist
        DatabaseError: If database connection fails

    Security Note:
        Returns payment tokens that can be used for transactions.
        - Never log tokens in full
        - Always use HTTPS for transmission
        - Tokens expire after 1 hour of inactivity

    Example:
        >>> methods = await fetch_user_payment_methods("user_123")
        >>> for method in methods:
        ...     print(f"Card ending in {method.last_four}")
    """
    user = await db.users.find_one({"_id": user_id})
    if not user:
        raise UserNotFoundError(f"User {user_id} not found")

    methods = await db.payment_methods.find({"user_id": user_id}).to_list()

    if not include_expired:
        methods = [m for m in methods if not m.is_expired()]

    return methods

Class Docstring

class UserRepository:
    """
    Repository for user data access.

    Provides CRUD operations for user entities with caching
    and automatic cache invalidation on updates.

    Attributes:
        db: Database connection
        cache: Redis cache instance
        cache_ttl: Cache time-to-live in seconds (default: 3600)

    Example:
        >>> repo = UserRepository(db_conn, redis_client)
        >>> user = await repo.get_by_id("user_123")
        >>> await repo.update(user_id, {"name": "New Name"})
    """

    def __init__(self, db: Database, cache: Redis, cache_ttl: int = 3600):
        """
        Initialize repository with database and cache.

        Args:
            db: Database connection instance
            cache: Redis cache instance
            cache_ttl: Cache time-to-live in seconds
        """
        self.db = db
        self.cache = cache
        self.cache_ttl = cache_ttl

Property Docstring

class User:
    """User model."""

    @property
    def full_name(self) -> str:
        """
        Get user's full name.

        Combines first and last name with a space. Returns empty
        string if both names are missing.

        Returns:
            Full name as string, or empty string if no names set
        """
        if not self.first_name and not self.last_name:
            return ""
        return f"{self.first_name} {self.last_name}".strip()

    @full_name.setter
    def full_name(self, value: str) -> None:
        """
        Set user's full name.

        Splits on first space to set first_name and last_name.
        If no space, sets only first_name.

        Args:
            value: Full name to parse and set

        Raises:
            ValueError: If value is empty or only whitespace
        """
        if not value or not value.strip():
            raise ValueError("Name cannot be empty")

        parts = value.strip().split(" ", 1)
        self.first_name = parts[0]
        self.last_name = parts[1] if len(parts) > 1 else ""

Tool/API Function Docstring

@tool
async def search_products(
    query: str,
    category: Optional[str] = None,
    max_results: int = 10
) -> str:
    """
    Search for products in catalog.

    Performs full-text search across product names and descriptions.
    Results are ranked by relevance and limited to max_results.

    Use this when customers ask to:
    - Find products by name or description
    - Search within a specific category
    - Browse available products

    Args:
        query: Search query string (e.g., "wireless headphones")
        category: Optional category filter (e.g., "electronics")
        max_results: Maximum results to return (1-100, default: 10)

    Returns:
        JSON string containing:
        - products: List of matching products
        - total: Total number of matches
        - query: Original search query
        - request_id: Request identifier for debugging

    Example Response:
        {
            "products": [
                {
                    "id": "prod_123",
                    "name": "Wireless Headphones",
                    "price": 99.99,
                    "in_stock": true
                }
            ],
            "total": 1,
            "query": "wireless headphones",
            "request_id": "req_abc123"
        }

    Security Note:
        Logs are PII-redacted. User ID is logged but not included
        in response to maintain privacy.
    """
    # Implementation

Required Sections

Always include:

  • Brief description (one-line summary)
  • Extended description (what it does, how it works)
  • Args: section (if has parameters)
  • Returns: section (if returns value)

Include when applicable:

  • Raises: section (if raises exceptions)
  • Example: or Example Response: section
  • Security Note: (if handles PII, payment data, auth)
  • Note: or Warning: for important caveats
  • Attributes: (for classes)
  • Use cases (for tools: "Use this when...")

Args Section Format

def function(
    required_param: str,
    optional_param: Optional[int] = None,
    flag: bool = False
) -> dict:
    """
    Function description.

    Args:
        required_param: Description of required parameter.
            Can span multiple lines with 4-space indent.
        optional_param: Description of optional parameter.
            Default: None
        flag: Whether to enable feature. Default: False

    Returns:
        Dictionary containing results
    """

Returns Section Format

def get_user_stats(user_id: str) -> dict:
    """
    Get user statistics.

    Returns:
        Dictionary containing:
        - total_orders: Total number of orders (int)
        - total_spent: Total amount spent (Decimal)
        - last_order_date: Date of last order (datetime)
        - loyalty_tier: Current loyalty tier (str)
    """

def process_payment(amount: Decimal) -> Tuple[bool, Optional[str]]:
    """
    Process payment transaction.

    Returns:
        Tuple of (success, error_message) where:
        - success: True if payment succeeded, False otherwise
        - error_message: Error description if failed, None if succeeded
    """

Raises Section Format

def divide(a: float, b: float) -> float:
    """
    Divide two numbers.

    Args:
        a: Numerator
        b: Denominator

    Returns:
        Result of division

    Raises:
        ValueError: If b is zero
        TypeError: If a or b are not numeric
    """
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Security Note Guidelines

Add Security Note when function:

  • Handles payment tokens/cards
  • Logs or processes PII data
  • Accesses customer data
  • Performs financial transactions
  • Requires authentication/authorization
  • Handles secrets or API keys

Security Note should mention:

"""
Security Note:
    Handles customer payment data (PCI-DSS Level 1).
    - All PII is redacted in logs
    - Payment tokens expire after 1 hour
    - Requires user authentication
    - Never log full card numbers
    - Always use HTTPS for transmission
"""

Anti-Patterns

# ❌ No docstring
def calculate_total(items, tax):
    return sum(items) * (1 + tax)

# ❌ Minimal/unhelpful docstring
def calculate_total(items, tax):
    """Calculate total."""
    return sum(items) * (1 + tax)

# ❌ Wrong format (not Google-style)
def calculate_total(items, tax):
    """
    Calculate total.
    :param items: The items
    :param tax: The tax
    :return: The total
    """

# ❌ No type information
def calculate_total(items, tax):
    """
    Calculate total.

    Args:
        items: List of items
        tax: Tax rate

    Returns:
        Total
    """
    # Types should be in signature AND described in docstring!

# ❌ Vague descriptions
def process(data):
    """
    Process data.

    Args:
        data: The data

    Returns:
        The result
    """
    # Not helpful! What kind of data? What processing? What result?

Best Practices Checklist

  • Start with brief one-line summary
  • Add detailed description for complex functions
  • Document all parameters with clear descriptions
  • Specify parameter types (should match type hints)
  • Document return value structure and type
  • List all exceptions that can be raised
  • Add examples for non-obvious usage
  • Include Security Note for sensitive operations
  • Use complete sentences with proper punctuation
  • Be specific about formats (ISO dates, decimals, etc.)
  • Mention side effects (logs, DB writes, API calls)
  • Document default values for optional parameters

Auto-Apply

When writing functions:

  1. Start with brief one-line description
  2. Add extended description if needed
  3. Add Args: section with all parameters
  4. Add Returns: section describing output
  5. Add Raises: if throws exceptions
  6. Add Security Note: if handling sensitive data
  7. Add Example: for complex usage
  8. Use complete sentences
  9. Be specific about data types and formats
  • pydantic-models - Document model fields
  • structured-errors - Document error responses
  • tool-design-pattern - Document tool usage
  • pytest-patterns - Write test docstrings