Returns a stable digest for a pydantic model. The digest is stable across: - Python hash seeds (does not use `hash()`). - Dict insertion ordering differences (canonicalizes via `sort_keys=True`). - Pydantic `model_extra` values (ignored).
(model: BaseModel)
| 27 | |
| 28 | |
| 29 | def _stable_model_digest(model: BaseModel) -> str: |
| 30 | """Returns a stable digest for a pydantic model. |
| 31 | |
| 32 | The digest is stable across: |
| 33 | - Python hash seeds (does not use `hash()`). |
| 34 | - Dict insertion ordering differences (canonicalizes via `sort_keys=True`). |
| 35 | - Pydantic `model_extra` values (ignored). |
| 36 | """ |
| 37 | if getattr(model, "model_extra", None): |
| 38 | model = model.model_copy(deep=True) |
| 39 | model.model_extra.clear() |
| 40 | |
| 41 | dumped = model.model_dump(by_alias=True, exclude_none=True, mode="json") |
| 42 | canonical_json = json.dumps( |
| 43 | dumped, |
| 44 | sort_keys=True, |
| 45 | ensure_ascii=False, |
| 46 | separators=(",", ":"), |
| 47 | ) |
| 48 | return hashlib.sha256(canonical_json.encode("utf-8")).hexdigest()[:16] |
| 49 | |
| 50 | |
| 51 | class AuthConfig(BaseModelWithConfig): |
no test coverage detected