Type Checking Guide

Stogger uses ty (Astral’s type checker, configured in [tool.ty.src]) for static type analysis. This guide covers the type patterns specific to this codebase.

Project Type Configuration

# pyproject.toml
[tool.ty.src]
exclude = ["tests", "docs"]

requires-python = ">=3.13"

Type checking runs as part of tox:

# Via tox (recommended — runs with correct dependencies)
CI=1 uv run tox -p

# Direct (if needed)
uv run ty check src/

Modern Type Syntax

Stogger targets Python 3.13+. Use modern syntax everywhere — no typing module imports for standard containers:

# ✅ Correct — modern syntax
def process(data: dict[str, Any]) -> list[str] | None:
    ...

items: list[str] = []
config: dict[str, Any] = {}
result: str | None = None

# ❌ Wrong — legacy typing
from typing import List, Dict, Optional
def process(data: Dict[str, Any]) -> Optional[List[str]]:
    ...

Common Patterns in Stogger

attrs classes with type annotations

StoggerConfig and FormatConfig use attrs.define. Type annotations go directly on attributes:

import attrs

@attrs.define(slots=False)
class FormatConfig:
    timestamp_precision: str = "iso_seconds"
    min_level: str = "info"
    show_code_info: bool = False
    pad_event_width: int = 30

@attrs.define
class StoggerConfig:
    verbose: bool = False
    logdir: Path | None = None
    enable_pii_scrubbing: bool = True
    systemd_facility: str | None = None
    ast_enabled_patterns: list | None = None

Processor type signature

All structlog processors follow the StructlogProcessor protocol defined in _types.py:

type EventDict = MutableMapping[str, Any]

class StructlogProcessor(Protocol):
    def __call__(
        self,
        logger: object,
        method_name: str,
        event_dict: EventDict,
    ) -> EventDict | None: ...

Use EventDict from stogger._types for processor signatures:

from stogger._types import EventDict

def my_processor(_logger: object, _method_name: str, event_dict: EventDict) -> EventDict:
    event_dict["custom_field"] = "value"
    return event_dict

ty: ignore directives

Stogger uses ty: ignore (not type: ignore) for the Astral type checker. Always include the specific error code:

# ty: ignore[unresolved-import] — dynamic import of optional package
from stogger_systemd import get_journal_logger_factory  # ty: ignore[unresolved-import]

# ty: ignore[unresolved-attribute] — accessing private structlog API
structlog._frames._find_first_app_frame_and_name(...)  # ty: ignore[unresolved-attribute]

# ty: ignore[invalid-argument-type] — structlog processor chain typing gaps
structlog.configure(processors=processors)  # ty: ignore[invalid-argument-type]

Dynamic imports for optional packages

Systemd and PostgreSQL support are optional. Use try/except with ty: ignore:

if cfg.enable_postgres:
    try:
        from stogger_postgres import get_postgres_logger_factory  # ty: ignore[unresolved-import]
        factory = get_postgres_logger_factory(dsn=cfg.postgres_dsn, table=cfg.postgres_table)
        loggers["postgres"] = factory
    except ImportError:
        pass  # Optional package not installed

noqa comments

Stogger uses Ruff with specific rule codes. Common suppressions:

# noqa: SLF001 — accessing private attributes (e.g., _file, __attrs_init__)
# noqa: PLC0415 — late import inside function body (intentional for optional deps)
# noqa: T201 — print() statements (used for early stderr warnings)
# noqa: S603, S607 — subprocess without shell=False

Troubleshooting

unresolved-import for optional packages

The stogger-systemd and stogger-postgres packages are workspace members. They’re imported dynamically and need ty: ignore[unresolved-import].

invalid-argument-type in processor chains

Structlog’s processor chain typing doesn’t perfectly match all stogger processor signatures. Use ty: ignore[invalid-argument-type] where the runtime behavior is correct but the type system can’t verify it.

attrs __attrs_init__ access

StoggerConfig.__init__ merges TOML config with kwargs and calls self.__attrs_init__(...). This requires ty: ignore[unresolved-attribute] because attrs generates this method at class creation time.