diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..2d1bd15 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-05-25 - Cached JSON Parsing in Pydantic Schema Validators +**Learning:** In Pydantic v2 schemas configured with `from_attributes=True` that map ORM strings to lists (e.g. JSON strings), using a pre-validator that directly calls `json.loads()` becomes a bottleneck when loading thousands of records from the database. +**Action:** Extract the JSON parsing into a private helper function, cache it with `@functools.lru_cache(maxsize=1024)` to return an immutable `tuple`, and have the public validator return `list(cached_tuple)` to maintain type safety and avoid cache pollution. diff --git a/src/app/schemas/pokemon.py b/src/app/schemas/pokemon.py index 7111dad..d67d808 100644 --- a/src/app/schemas/pokemon.py +++ b/src/app/schemas/pokemon.py @@ -1,10 +1,28 @@ """Pokemon module.""" +import functools import json +import logging from typing import Any from pydantic import BaseModel, ConfigDict, field_validator +logger = logging.getLogger(__name__) + + +@functools.lru_cache(maxsize=1024) +def _parse_types_json(value: str) -> tuple[str, ...]: + """Private helper to parse and cache JSON strings into immutable tuples.""" + try: + parsed = json.loads(value) + if not isinstance(parsed, list): + logger.error("invalid types format") + raise ValueError("invalid types format") + return tuple(parsed) + except json.JSONDecodeError as e: + logger.error("malformed types JSON") + raise ValueError("malformed types JSON") from e + def parse_pokemon_types(value: Any) -> Any: """Helper function to parse the types JSON string. @@ -19,13 +37,7 @@ def parse_pokemon_types(value: Any) -> Any: ValueError: If the string is not valid JSON or not a list. """ if isinstance(value, str): - try: - parsed = json.loads(value) - if not isinstance(parsed, list): - raise ValueError("invalid types format") - return parsed - except json.JSONDecodeError as e: - raise ValueError("malformed types JSON") from e + return list(_parse_types_json(value)) return value