claude-memory/graph/fixes/fix-pydantic-bare-default-evaluated-at-class-definition-time-530bfb.md

1.8 KiB

id type title tags importance confidence created updated relations
530bfb6f-841c-4381-b8a4-d0b6fbc02878 fix Fix: Pydantic bare default evaluated at class definition time (paper-dynasty-database)
paper-dynasty
python
pydantic
fix
bug
fastapi
0.65 0.8 2026-03-03T20:06:06.122074+00:00 2026-03-03T20:06:06.763300+00:00
target type direction strength edge_id
04e57a23-0a20-49d6-8c5a-2fa5fc4e55b5 RELATED_TO outgoing 0.7 b210b16d-b168-4798-b73f-98ef27f46dd8
target type direction strength edge_id
d36a86f0-8183-4c94-8d63-0be65d3fd63a RELATED_TO outgoing 0.68 62213d38-b5b9-4166-8d1b-4b48f216772a
target type direction strength edge_id
5ae1bf6e-1e1c-4486-8a4d-10afd9e42189 RELATED_TO outgoing 0.68 b3e61cf3-d61d-4421-8e81-8818a7601791

Problem

PlayerModel.offense_col in app/routers_v2/mlbplayers.py used a bare random.randint(1, 3) as the Pydantic field default. Python evaluates this expression once at class definition time (module import), so every instance shared the same random integer for the entire process lifetime.

Root Cause

Pydantic field defaults that are not wrapped in Field(default_factory=...) are evaluated eagerly at class body execution time — identical to how Python function default argument values work. This is a common footgun with mutable or randomised defaults.

Solution

# Before (broken)
offense_col: int = random.randint(1, 3)

# After (correct)
offense_col: int = pydantic.Field(default_factory=lambda: random.randint(1, 3))

default_factory is called on each model instantiation, producing a fresh value per request.

Files Changed

  • app/routers_v2/mlbplayers.py line 40

References

  • Issue #24, PR #38 in cal/paper-dynasty-database