Reworked the MVP code along the lines Ryth flagged. Same behavior
(11/11 tests still pass), tighter idiom.
executor.py:
- Replaced FunctionResult / FunctionError dataclasses with a MizanError
exception hierarchy (NotFound, BadRequest, ValidationFailed,
Unauthorized, Forbidden, NotImplementedYet, InternalError). Each
carries its own ErrorCode + HTTP status; the dispatcher path raises
rather than returning sentinel objects.
- Auth check uses match/case for the requirement (True / 'staff' /
'superuser' / callable / other) — single declarative dispatch instead
of an if/elif chain.
- Broke up the single 80-line execute_function into focused helpers:
_resolve_function, _enforce_auth, _validate_input, _serialize,
_invalidation_target. The execute_function body now reads as five
declarative steps.
- Input validation uses Pydantic's model_fields[name].is_required()
directly and a list comprehension for required-field reporting,
instead of round-tripping through model_json_schema().
router.py:
- POST /call/ now declares its body as a Pydantic CallBody model;
FastAPI handles parsing + envelope validation. No more manual
await request.json() + dict[get] dancing.
- Endpoint bodies shrink to 3-5 lines each. Context fetch uses a
dict comprehension over the function group.
- mizan_exception_handler renders MizanError to the protocol's
{error: {code, message, details}} envelope.
- mizan_validation_handler maps FastAPI's RequestValidationError to
the same envelope under BAD_REQUEST so the wire format is uniform
whether the failure is body-shape or business validation.
__init__.py: exposes the full exception hierarchy + both handlers
so consumers can wire them onto their FastAPI app declaratively:
app.add_exception_handler(MizanError, mizan_exception_handler)
app.add_exception_handler(RequestValidationError, mizan_validation_handler)
Verified: mizan-core 15/15, mizan-django 348 pass, mizan-fastapi 11/11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>