Skip to content

Code Style Guide

This guide covers code style requirements for MAID contributions.

Python Style

General Rules

  • Follow PEP 8
  • Maximum line length: 88 characters (Black default)
  • Use type hints everywhere
  • Use ruff for linting
  • Use mypy in strict mode for type checking

Type Hints

All functions must have complete type annotations:

# Good
def process_entity(entity_id: UUID, components: list[Component]) -> bool:
    ...

# Bad - missing types
def process_entity(entity_id, components):
    ...

For complex types, use type aliases:

from typing import TypeAlias

EntityComponents: TypeAlias = dict[UUID, list[Component]]

Imports

Organize imports in this order:

  1. Standard library
  2. Third-party packages
  3. Local imports
import asyncio
from collections.abc import Callable
from uuid import UUID

import structlog
from pydantic import BaseModel

from maid_engine.core.ecs import Component, Entity
from maid_engine.core.world import World

Docstrings

Use Google-style docstrings for all public APIs:

def create_entity(
    self,
    components: list[Component] | None = None,
    tags: set[str] | None = None,
) -> Entity:
    """Create a new entity in the world.

    Creates an entity with a unique UUID and optionally attaches
    components and tags.

    Args:
        components: Optional list of components to attach.
        tags: Optional set of string tags for filtering.

    Returns:
        The newly created Entity.

    Raises:
        WorldError: If the world is not running.

    Example:
        >>> entity = world.create_entity(
        ...     components=[HealthComponent(100)],
        ...     tags={"player"},
        ... )
    """

Async/Await

MAID uses async/await throughout. Follow these patterns:

# Good - use async context managers
async with session.begin():
    await session.execute(query)

# Good - gather concurrent operations
results = await asyncio.gather(
    fetch_player(player_id),
    fetch_inventory(player_id),
)

# Avoid - blocking calls in async code
# Bad: time.sleep(1)
# Good: await asyncio.sleep(1)

Commit Messages

We follow Conventional Commits:

type(scope): description

[optional body]

[optional footer]

Types

Type Description
feat New feature
fix Bug fix
docs Documentation changes
style Code style changes (formatting, etc.)
refactor Code refactoring
test Adding or updating tests
chore Maintenance tasks
perf Performance improvements

Examples

feat(engine): add hot reload for content packs

Implements hot reload functionality that allows reloading
content pack code without server restart.

Closes #123
fix(telnet): handle connection timeout gracefully

Previously, connection timeouts would crash the session.
Now they trigger proper cleanup and notify the user.
docs(readme): add installation instructions

Pull Request Guidelines

PR Size

  • Keep PRs focused and reasonably sized
  • One logical change per PR
  • Split large changes into a series of smaller PRs

PR Title

Use the same format as commit messages:

feat(stdlib): add DialogueComponent for NPC conversations

PR Description

Include:

  1. Summary: What does this PR do?
  2. Motivation: Why is this change needed?
  3. Testing: How was this tested?
  4. Related Issues: Reference any related issues with Fixes #123

Checklist

Before submitting:

  • [ ] Tests pass: uv run pytest packages/
  • [ ] Linting passes: uv run ruff check packages/
  • [ ] Types check: uv run mypy packages/
  • [ ] Documentation updated (if needed)
  • [ ] CHANGELOG.md updated (for user-facing changes)

File Organization

Package Structure

packages/maid-engine/
├── src/maid_engine/
│   ├── __init__.py      # Public API exports
│   ├── core/
│   │   ├── __init__.py  # Subpackage exports
│   │   ├── engine.py
│   │   └── world.py
│   └── ...
├── tests/
│   ├── conftest.py      # Shared fixtures
│   ├── test_engine.py
│   └── ...
└── pyproject.toml

Naming Conventions

Item Convention Example
Modules snake_case document_store.py
Classes PascalCase DocumentStore
Functions snake_case get_document
Constants SCREAMING_SNAKE MAX_CONNECTIONS
Type aliases PascalCase EntityComponents

Running Style Checks

# Lint check
uv run ruff check packages/

# Auto-fix lint issues
uv run ruff check packages/ --fix

# Type check
uv run mypy packages/

# Format check (ruff includes formatting)
uv run ruff format packages/ --check

# Auto-format
uv run ruff format packages/