Skip to main content
In multi-step workflows — read a file, then parse it, then validate its contents — if any step fails you want to stop immediately and propagate the failure upward, not continue into the next step with bad data. BasicReturns makes this trivial. Because every function returns the same structure, you can check result.ok after each call and return the failed result directly, keeping the error context intact without any nested try/except blocks or re-raising exceptions.

Early Return Pattern

The simplest chaining technique is the early return: call a function, and if it did not succeed, return its result immediately. The caller receives the same DataAndMsgReturn that the inner function produced — including its error, msg, and ok = False — without any wrapping or translation.
read_json never needs to know why read_file failed — it just passes the result along. The caller of read_json gets back either a valid parsed payload or a fully described failure, regardless of which layer it originated in.

Constructing a New Return from a Failed One

Sometimes you need to add context at the current layer rather than passing the original result unchanged. For example, a higher-level function may want to describe what it was trying to do rather than surface the low-level technical message. In that case, build a new DataAndMsgReturn that preserves the original exception while replacing the message:
The key rule: always forward result.error — never replace the exception object with a plain string. Keeping the original exception preserves the full traceback for logging and debugging.

Full Chain Example

Here is a complete three-step chain that reads a JSON configuration file and then validates its contents, taken directly from the BasicReturns README:
Each step is isolated: read_json knows nothing about validation, and validate_config knows nothing about files. load_and_validate_config composes them, checks the result of each, and either short-circuits with the error or assembles the final success return.
This pattern is structurally identical to Rust’s ? operator and to monadic bind (the >>= operator in Haskell). Each step only executes if the previous one succeeded, and errors propagate automatically through the chain without boilerplate. BasicReturns brings that discipline to Python in an explicit, readable form.

Propagating to API Responses

Chained results naturally serialize at the boundary. Call to_dict() once, at the outermost layer, and the full context — including the original error and every message accumulated along the chain — is ready for a JSON response or log entry:
Because the structure is always the same, the API layer does not need to branch on the result type — it calls to_dict() unconditionally and lets the ok field carry the signal to the client.