aeat.core.output_rendering module

Central command-output rendering contract.

render_command_output() is the shared text/JSON transport boundary for command handlers that do not need a full SchemaEnvelope. It returns RenderedCommandOutput, chooses an OutputFormat, and applies aeat.core.redaction.redact_for_cli_output() or aeat.core.redaction.redact_structured_for_cli_output() before text reaches stdout.

Envelope-aware commands bypass this bare renderer in JSON mode through aeat.core.json_contract.emit_json_success(), but both paths consult reveal_cli_identifiers_opt_in() so profile and bucket identifier redaction cannot drift between direct payload rendering and the SchemaEnvelope path.

This module returns rendered text only; CLI transports own stdout/stderr writes and exit-code handling. It also owns success-output redaction only. Error envelopes, log records, and observability sinks use their own redaction boundaries.

exception OutputRenderingError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: AeatError

Raised when command output cannot be rendered safely.

Used after payload normalization when a value still cannot be serialized as CLI success output. It is not the invalid-format error; unsupported format strings raise OutputFormatRefusedError before rendering starts.

Parameters:
  • message (str | None)

  • context (dict[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
exception OutputFormatRefusedError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: AeatError

Raised when a command requests an unsupported OutputFormat.

Parameters:
  • message (str | None)

  • context (dict[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
class OutputFormat(*values)[source]

Bases: StrEnum

Accepted command output formats.

TEXT
JSON
class RenderedCommandOutput(**data)[source]

Bases: BaseModel

Rendered, already-redacted output document returned to CLI transports.

text is the complete body to emit. The original payload is deliberately not retained so downstream transports cannot accidentally bypass the central success-output redaction pass.

Parameters:
format: OutputFormat
text: str
render_command_output(*, format_name, payload, lines)[source]

Render a payload or line iterator according to the root output format.

JSON output normalises payload through jsonable_output_payload() and applies structured CLI redaction before serialization. Text output ignores payload and joins the supplied lines after applying line-oriented CLI redaction.

The helper does not write to stdout, translate messages, choose exit codes, or register JSON schemas. Callers supply localized text lines or typed payloads, then emit the returned RenderedCommandOutput through the CLI transport layer.

Return type:

RenderedCommandOutput

Returns:

A RenderedCommandOutput containing the format and the rendered, redacted text body.

Raises:

OutputFormatRefusedError – If format_name is not one of the accepted OutputFormat values.

Parameters:
reveal_cli_identifiers_opt_in()[source]

Resolve the profile/bucket identifier reveal opt-out at the output boundary.

Reading aeat.core.config.load_settings() here keeps the policy decision at the central success-output privacy boundary (per the centralized-output-redaction ADR) and keeps the pure redaction module free of a Settings dependency. Default off preserves the paste-safe placeholder behaviour; an operator sets AEAT_CLI_REVEAL_IDENTIFIERS=1 to opt out. Both success-output emitters — render_command_output() and the JSON envelope aeat.core.json_contract.emit_json_success() — consult this one resolver so the two transports cannot diverge.

The opt-in reveals only opaque profile and bucket identifiers. Tax identities, URLs, tokens, and secure-object keys remain redacted by the CLI redaction profile.

Return type:

bool

jsonable_output_payload(payload)[source]

Convert command payload values into JSON-serialisable primitives.

Tuples / sets / frozensets are flattened to JSON arrays (list) because JSON has no native tuple or set type. Downstream consumers that need to round-trip a typed payload back into its declared schema MUST use ModelClass.model_validate_json(raw_bytes) rather than ModelClass.model_validate(json.loads(raw)): pydantic coerces list -> tuple when it owns the JSON parse, but not when handed a pre-parsed dict. The roundtrip tests in aeat.core.tests.test_json_envelope_roundtrip pin the correct usage.

This is a transport normalization helper, not a domain contract authority. Command-specific payload classes own field semantics and ordering; this helper only prepares already-selected output values for redaction and JSON serialization.

Return type:

object

Parameters:

payload (object)