Skip to content

Output

OperationResult

The atomic result type returned by interface methods. Carries success state, an item identifier, a message, optional data payload, and an error code.

pyclif.OperationResult dataclass

Outcome of a single interface action.

Interface methods return this instead of rising for expected business failures. Exceptions are reserved for programming errors (broken invariant, missing template, corrupt state).

Attributes:

Name Type Description
success bool

Whether the action succeeded.

item str

Human-readable identifier (file path, resource name, …).

data Any

Optional payload attached to the result.

message str

Human-readable description of the outcome.

error_code int

Non-zero on failure.

Source code in src/pyclif/core/output/responses.py
@dataclasses.dataclass
class OperationResult:
    """Outcome of a single interface action.

    Interface methods return this instead of rising for expected business
    failures. Exceptions are reserved for programming errors (broken invariant,
    missing template, corrupt state).

    Attributes:
        success: Whether the action succeeded.
        item: Human-readable identifier (file path, resource name, …).
        data: Optional payload attached to the result.
        message: Human-readable description of the outcome.
        error_code: Non-zero on failure.
    """

    success: bool
    item: str
    data: Any = None
    message: str = ""
    error_code: int = 0

    @classmethod
    def ok(cls, item: str, message: str = "", data: Any = None) -> OperationResult:
        """Create a successful result.

        Args:
            item: Human-readable identifier for the operated resource.
            message: Human-readable description of what happened.
            data: Optional domain payload.

        Returns:
            A successful OperationResult with error_code 0.
        """
        return cls(success=True, item=item, message=message, data=data)

    @classmethod
    def error(cls, item: str, message: str, error_code: int = 1) -> OperationResult:
        """Create a failed result.

        Args:
            item: Human-readable identifier for the operated resource.
            message: Description of the failure.
            error_code: Exit code for this failure (default 1).

        Returns:
            A failed OperationResult with the given error_code.
        """
        return cls(success=False, item=item, message=message, error_code=error_code)

ok(item, message='', data=None) classmethod

Create a successful result.

Parameters:

Name Type Description Default
item str

Human-readable identifier for the operated resource.

required
message str

Human-readable description of what happened.

''
data Any

Optional domain payload.

None

Returns:

Type Description
OperationResult

A successful OperationResult with error_code 0.

Source code in src/pyclif/core/output/responses.py
@classmethod
def ok(cls, item: str, message: str = "", data: Any = None) -> OperationResult:
    """Create a successful result.

    Args:
        item: Human-readable identifier for the operated resource.
        message: Human-readable description of what happened.
        data: Optional domain payload.

    Returns:
        A successful OperationResult with error_code 0.
    """
    return cls(success=True, item=item, message=message, data=data)

error(item, message, error_code=1) classmethod

Create a failed result.

Parameters:

Name Type Description Default
item str

Human-readable identifier for the operated resource.

required
message str

Description of the failure.

required
error_code int

Exit code for this failure (default 1).

1

Returns:

Type Description
OperationResult

A failed OperationResult with the given error_code.

Source code in src/pyclif/core/output/responses.py
@classmethod
def error(cls, item: str, message: str, error_code: int = 1) -> OperationResult:
    """Create a failed result.

    Args:
        item: Human-readable identifier for the operated resource.
        message: Description of the failure.
        error_code: Exit code for this failure (default 1).

    Returns:
        A failed OperationResult with the given error_code.
    """
    return cls(success=False, item=item, message=message, error_code=error_code)

Response

The standard return type for all pyclif commands. Carries success state, a human-readable message, optional structured data, and an error code.

pyclif.Response dataclass

Represents a CLI command response with structured output support.

Attributes:

Name Type Description
success bool

Indicates whether the response is successful.

message str

The message associated with the response.

data Any

Additional data associated with the response.

error_code int | None

The error code associated with the response.

renderer BaseRenderer | None

Renderer controlling all output formats for this response.

Source code in src/pyclif/core/output/responses.py
@dataclasses.dataclass
class Response:
    """Represents a CLI command response with structured output support.

    Attributes:
        success: Indicates whether the response is successful.
        message: The message associated with the response.
        data: Additional data associated with the response.
        error_code: The error code associated with the response.
        renderer: Renderer controlling all output formats for this response.
    """

    success: bool
    message: str
    data: Any = dataclasses.field(default_factory=dict)
    error_code: int | None = None
    renderer: BaseRenderer | None = None

    def to_dict(self) -> dict[str, Any]:
        """Return a dictionary representation of the object.

        Only includes fields whose values differ from their defaults.

        Returns:
            Dictionary mapping attributes names to their values.
        """
        return dict(
            (f.name, attrgetter(f.name)(self))
            for f in dataclasses.fields(self)
            if attrgetter(f.name)(self) != f.default
        )

    def to_json(self) -> dict[str, Any]:
        """Return a JSON-serializable dictionary of the response.

        Non-serializable fields (callbacks) are excluded from the output.

        Returns:
            Dictionary containing the serializable attributes.
        """
        self._serialize_data()
        data = self.to_dict()
        for field in NON_SERIALIZABLE_FIELDS:
            data.pop(field, None)
        return data

    @classmethod
    def from_results(
        cls,
        results: list[OperationResult],
        message: str = "",
        success_message: str = "",
        failure_message: str = "",
        renderer: BaseRenderer | None = None,
    ) -> Response:
        """Build a Response from a list of OperationResult.

        Aggregates a batch of interface results into a single Response.
        success is True only if every result succeeded. error_code is taken
        from the first failed result, or 0 if all passed.

        Args:
            results: Outcomes returned by the interface layer.
            message: Fixed message used regardless of the outcome. When omitted,
                success_message / failure_message are used, or a default
                summary is generated from the result counts.
            success_message: Message used when all results succeeded.
            failure_message: Message used when at least one result failed.
            renderer: Renderer instance controlling all output formats.

        Returns:
            An aggregated Response reflecting the overall outcome.
        """
        failed = [r for r in results if not r.success]
        success = not failed
        error_code = failed[0].error_code if failed else 0

        if not message:
            if success:
                message = success_message or f"{len(results)} operation(s) completed successfully."
            else:
                message = failure_message or f"{len(failed)}/{len(results)} operation(s) failed."

        return cls(
            success=success,
            message=message,
            data={"results": results},
            error_code=error_code if not success else None,
            renderer=renderer,
        )

    @classmethod
    def from_stream(
        cls,
        stream: Iterator[OperationResult],
        renderer: BaseRenderer,
    ) -> Response:
        """Build a Response from a generator of OperationResult.

        The generator is stored without being consumed. The framework
        materializes it at dispatch time: for rich output the Live context
        drives iteration via renderer hooks; for all other formats
        OutputFormatMixin calls _materialise_stream() before dispatch.

        success, message, and error_code are left blank — they are
        re-evaluated by the framework after the stream is consumed, using
        renderer.get_success_message() / get_failure_message().

        Args:
            stream: Generator yielding OperationResult items one by one.
            renderer: Renderer instance — required, a stream with no renderer
                has no output contract.

        Returns:
            An incomplete Response carrying the stream and renderer.
        """
        return cls(
            success=True,
            message="",
            data={"stream": stream},
            renderer=renderer,
        )

    def _serialize_data(self):
        """Serialize dict values that expose a to_dict method in place."""
        if isinstance(self.data, dict):
            serialized_dict = {}
            for key, value in self.data.items():
                if (hasattr(value, "to_dict")) and callable(value.to_dict):
                    serialized_dict[key] = value.to_dict()
                else:
                    serialized_dict[key] = value
            self.data = serialized_dict

to_dict()

Return a dictionary representation of the object.

Only includes fields whose values differ from their defaults.

Returns:

Type Description
dict[str, Any]

Dictionary mapping attributes names to their values.

Source code in src/pyclif/core/output/responses.py
def to_dict(self) -> dict[str, Any]:
    """Return a dictionary representation of the object.

    Only includes fields whose values differ from their defaults.

    Returns:
        Dictionary mapping attributes names to their values.
    """
    return dict(
        (f.name, attrgetter(f.name)(self))
        for f in dataclasses.fields(self)
        if attrgetter(f.name)(self) != f.default
    )

to_json()

Return a JSON-serializable dictionary of the response.

Non-serializable fields (callbacks) are excluded from the output.

Returns:

Type Description
dict[str, Any]

Dictionary containing the serializable attributes.

Source code in src/pyclif/core/output/responses.py
def to_json(self) -> dict[str, Any]:
    """Return a JSON-serializable dictionary of the response.

    Non-serializable fields (callbacks) are excluded from the output.

    Returns:
        Dictionary containing the serializable attributes.
    """
    self._serialize_data()
    data = self.to_dict()
    for field in NON_SERIALIZABLE_FIELDS:
        data.pop(field, None)
    return data

from_results(results, message='', success_message='', failure_message='', renderer=None) classmethod

Build a Response from a list of OperationResult.

Aggregates a batch of interface results into a single Response. success is True only if every result succeeded. error_code is taken from the first failed result, or 0 if all passed.

Parameters:

Name Type Description Default
results list[OperationResult]

Outcomes returned by the interface layer.

required
message str

Fixed message used regardless of the outcome. When omitted, success_message / failure_message are used, or a default summary is generated from the result counts.

''
success_message str

Message used when all results succeeded.

''
failure_message str

Message used when at least one result failed.

''
renderer BaseRenderer | None

Renderer instance controlling all output formats.

None

Returns:

Type Description
Response

An aggregated Response reflecting the overall outcome.

Source code in src/pyclif/core/output/responses.py
@classmethod
def from_results(
    cls,
    results: list[OperationResult],
    message: str = "",
    success_message: str = "",
    failure_message: str = "",
    renderer: BaseRenderer | None = None,
) -> Response:
    """Build a Response from a list of OperationResult.

    Aggregates a batch of interface results into a single Response.
    success is True only if every result succeeded. error_code is taken
    from the first failed result, or 0 if all passed.

    Args:
        results: Outcomes returned by the interface layer.
        message: Fixed message used regardless of the outcome. When omitted,
            success_message / failure_message are used, or a default
            summary is generated from the result counts.
        success_message: Message used when all results succeeded.
        failure_message: Message used when at least one result failed.
        renderer: Renderer instance controlling all output formats.

    Returns:
        An aggregated Response reflecting the overall outcome.
    """
    failed = [r for r in results if not r.success]
    success = not failed
    error_code = failed[0].error_code if failed else 0

    if not message:
        if success:
            message = success_message or f"{len(results)} operation(s) completed successfully."
        else:
            message = failure_message or f"{len(failed)}/{len(results)} operation(s) failed."

    return cls(
        success=success,
        message=message,
        data={"results": results},
        error_code=error_code if not success else None,
        renderer=renderer,
    )

from_stream(stream, renderer) classmethod

Build a Response from a generator of OperationResult.

The generator is stored without being consumed. The framework materializes it at dispatch time: for rich output the Live context drives iteration via renderer hooks; for all other formats OutputFormatMixin calls _materialise_stream() before dispatch.

success, message, and error_code are left blank — they are re-evaluated by the framework after the stream is consumed, using renderer.get_success_message() / get_failure_message().

Parameters:

Name Type Description Default
stream Iterator[OperationResult]

Generator yielding OperationResult items one by one.

required
renderer BaseRenderer

Renderer instance — required, a stream with no renderer has no output contract.

required

Returns:

Type Description
Response

An incomplete Response carrying the stream and renderer.

Source code in src/pyclif/core/output/responses.py
@classmethod
def from_stream(
    cls,
    stream: Iterator[OperationResult],
    renderer: BaseRenderer,
) -> Response:
    """Build a Response from a generator of OperationResult.

    The generator is stored without being consumed. The framework
    materializes it at dispatch time: for rich output the Live context
    drives iteration via renderer hooks; for all other formats
    OutputFormatMixin calls _materialise_stream() before dispatch.

    success, message, and error_code are left blank — they are
    re-evaluated by the framework after the stream is consumed, using
    renderer.get_success_message() / get_failure_message().

    Args:
        stream: Generator yielding OperationResult items one by one.
        renderer: Renderer instance — required, a stream with no renderer
            has no output contract.

    Returns:
        An incomplete Response carrying the stream and renderer.
    """
    return cls(
        success=True,
        message="",
        data={"stream": stream},
        renderer=renderer,
    )

CliTable

Wrapper around Rich Table for consistent tabular output.

pyclif.CliTable

Create and manage tables for CLI output with custom styling.

This class wraps the Rich library's Table to provide convenient methods for adding columns and rows with automatic formatting.

Source code in src/pyclif/core/output/tables.py
class CliTable:
    """Create and manage tables for CLI output with custom styling.

    This class wraps the Rich library's Table to provide convenient methods
    for adding columns and rows with automatic formatting.
    """

    table_style = {
        "show_lines": True,
        "row_styles": ["none", "dim"],
        "border_style": "bright_blue",
        "header_style": "bold blue",
        "box": box.ROUNDED,
    }

    def __init__(self, fields: dict, rows: list | dict, table_style: dict | None = None):
        """Initialize the CLI table with columns and rows.

        Args:
            fields: Dictionary of field names to CliTableColumn objects.
            rows: Single dictionary or list of dictionaries representing rows.
            table_style: Optional dictionary to override default table styling.
        """
        if isinstance(table_style, dict):
            self.table_style |= table_style
        # noinspection PyArgumentList
        self.table = Table(**self.table_style)
        self.update_columns(fields)
        self.update_rows(fields, rows)

    def __rich__(self) -> Table | str:
        """Return the table for rich rendering.

        Returns:
            The rich Table object or a message if no data exists.
        """
        return self.table if self.table.row_count != 0 else "[i]No dataset available.[/i]"

    def table(self) -> Table:  # pragma: no cover
        """Return the internal table object.

        Returns:
            Table: The rich Table instance.
        """
        return self.table

    def update_columns(self, fields: dict) -> None:
        """Add columns to the table from field definitions.

        Args:
            fields: Dictionary of field names to CliTableColumn objects.
        """
        for field in fields.values():
            self.table.add_column(**field.to_dict())

    def update_rows(self, fields: dict, rows: list | dict) -> None:
        """Add rows to the table.

        Accepts either a single dictionary or list of dictionaries representing
        rows. Each row is processed to extract values matching field definitions.

        Args:
            fields: Dictionary of field names to column definitions.
            rows: Single dictionary or list of dictionaries for rows.
        """
        if rows is not None:
            if isinstance(rows, dict):
                rows = [rows]
            for row in rows:
                columns = self._generate_columns(fields, row)
                self.table.add_row(*columns)

    def _generate_columns(self, fields: dict, row: dict) -> list:
        """Generate column values from a row and field definition.

        Handles nested relationships using dot notation (e.g., "relation.target").
        For dotted fields, extracts target values from each item in the relation
        list and joins them with commas.

        Args:
            fields: Dictionary of field names to column definitions.
            row: Dictionary representing a single row of data.

        Returns:
            list: List of formatted column values for the row.
        """
        columns = []
        for field in fields:
            if "." in field:
                relation, target = field.split(".")
                if row.get(relation):
                    # noinspection PyTypeChecker
                    row_field = [
                        self.__rich_field__(item.get(target, None)) for item in row.get(relation)
                    ]
                    columns.append(",".join(row_field))
                else:
                    columns.append("")
            else:
                columns.append(self.__rich_field__(row.get(field)))
        return columns

    @staticmethod
    def __rich_field__(field: Any) -> str | Any:
        """Format a field value for rich table output.

        Converts booleans to emoji representations, integers to strings,
        and None values to "N/A".

        Args:
            field: The field value to format.

        Returns:
            Formatted value suitable for table display.
        """
        if isinstance(field, bool):
            return convert_bool_to_emoji(field)
        if isinstance(field, int):
            return str(field)
        if isinstance(field, type(None)):
            return "N/A"
        return field

__init__(fields, rows, table_style=None)

Initialize the CLI table with columns and rows.

Parameters:

Name Type Description Default
fields dict

Dictionary of field names to CliTableColumn objects.

required
rows list | dict

Single dictionary or list of dictionaries representing rows.

required
table_style dict | None

Optional dictionary to override default table styling.

None
Source code in src/pyclif/core/output/tables.py
def __init__(self, fields: dict, rows: list | dict, table_style: dict | None = None):
    """Initialize the CLI table with columns and rows.

    Args:
        fields: Dictionary of field names to CliTableColumn objects.
        rows: Single dictionary or list of dictionaries representing rows.
        table_style: Optional dictionary to override default table styling.
    """
    if isinstance(table_style, dict):
        self.table_style |= table_style
    # noinspection PyArgumentList
    self.table = Table(**self.table_style)
    self.update_columns(fields)
    self.update_rows(fields, rows)

table()

Return the internal table object.

Returns:

Name Type Description
Table Table

The rich Table instance.

Source code in src/pyclif/core/output/tables.py
def table(self) -> Table:  # pragma: no cover
    """Return the internal table object.

    Returns:
        Table: The rich Table instance.
    """
    return self.table

__rich__()

Return the table for rich rendering.

Returns:

Type Description
Table | str

The rich Table object or a message if no data exists.

Source code in src/pyclif/core/output/tables.py
def __rich__(self) -> Table | str:
    """Return the table for rich rendering.

    Returns:
        The rich Table object or a message if no data exists.
    """
    return self.table if self.table.row_count != 0 else "[i]No dataset available.[/i]"

update_columns(fields)

Add columns to the table from field definitions.

Parameters:

Name Type Description Default
fields dict

Dictionary of field names to CliTableColumn objects.

required
Source code in src/pyclif/core/output/tables.py
def update_columns(self, fields: dict) -> None:
    """Add columns to the table from field definitions.

    Args:
        fields: Dictionary of field names to CliTableColumn objects.
    """
    for field in fields.values():
        self.table.add_column(**field.to_dict())

update_rows(fields, rows)

Add rows to the table.

Accepts either a single dictionary or list of dictionaries representing rows. Each row is processed to extract values matching field definitions.

Parameters:

Name Type Description Default
fields dict

Dictionary of field names to column definitions.

required
rows list | dict

Single dictionary or list of dictionaries for rows.

required
Source code in src/pyclif/core/output/tables.py
def update_rows(self, fields: dict, rows: list | dict) -> None:
    """Add rows to the table.

    Accepts either a single dictionary or list of dictionaries representing
    rows. Each row is processed to extract values matching field definitions.

    Args:
        fields: Dictionary of field names to column definitions.
        rows: Single dictionary or list of dictionaries for rows.
    """
    if rows is not None:
        if isinstance(rows, dict):
            rows = [rows]
        for row in rows:
            columns = self._generate_columns(fields, row)
            self.table.add_row(*columns)

__rich_field__(field) staticmethod

Format a field value for rich table output.

Converts booleans to emoji representations, integers to strings, and None values to "N/A".

Parameters:

Name Type Description Default
field Any

The field value to format.

required

Returns:

Type Description
str | Any

Formatted value suitable for table display.

Source code in src/pyclif/core/output/tables.py
@staticmethod
def __rich_field__(field: Any) -> str | Any:
    """Format a field value for rich table output.

    Converts booleans to emoji representations, integers to strings,
    and None values to "N/A".

    Args:
        field: The field value to format.

    Returns:
        Formatted value suitable for table display.
    """
    if isinstance(field, bool):
        return convert_bool_to_emoji(field)
    if isinstance(field, int):
        return str(field)
    if isinstance(field, type(None)):
        return "N/A"
    return field

CliTableColumn

Column definition for CliTable.

pyclif.CliTableColumn

Bases: Column

Column definition for CLI tables with serialization support.

Source code in src/pyclif/core/output/tables.py
class CliTableColumn(Column):
    """Column definition for CLI tables with serialization support."""

    def to_dict(self) -> dict:
        """Convert the column object to a dictionary.

        Returns a dictionary of all non-private fields and their values,
        excluding fields that have default values.

        Returns:
            Dictionary representation of the column.
        """
        return dict(
            (f.name, attrgetter(f.name)(self))
            for f in dataclasses.fields(self)  # type: ignore
            if attrgetter(f.name)(self) != f.default and not f.name.startswith("_")
        )

to_dict()

Convert the column object to a dictionary.

Returns a dictionary of all non-private fields and their values, excluding fields that have default values.

Returns:

Type Description
dict

Dictionary representation of the column.

Source code in src/pyclif/core/output/tables.py
def to_dict(self) -> dict:
    """Convert the column object to a dictionary.

    Returns a dictionary of all non-private fields and their values,
    excluding fields that have default values.

    Returns:
        Dictionary representation of the column.
    """
    return dict(
        (f.name, attrgetter(f.name)(self))
        for f in dataclasses.fields(self)  # type: ignore
        if attrgetter(f.name)(self) != f.default and not f.name.startswith("_")
    )

ExceptionTable

Renders an exception as a Rich table. Used internally by the response formatter but available for direct use in error handlers.

pyclif.ExceptionTable

Bases: CliTable

Specialized table for displaying exception information.

Displays error code, message, and traceback with red styling for exception visualization.

Source code in src/pyclif/core/output/tables.py
class ExceptionTable(CliTable):
    """Specialized table for displaying exception information.

    Displays error code, message, and traceback with red styling for
    exception visualization.
    """

    table_style = {
        "row_styles": ["none", "dim"],
        "border_style": "bright_red",
        "header_style": "bold red",
        "box": box.SQUARE_DOUBLE_HEAD,
    }

    fields = {
        "error_code": CliTableColumn(header="Error"),
        "message": CliTableColumn(header="Message"),
        "data": CliTableColumn(header="Traceback", justify="full"),
    }

    def __init__(self, columns: dict):
        """Initialize the exception table with error data.

        Args:
            columns: Dictionary containing error_code, message, and data keys.
        """
        super().__init__(fields=self.fields, rows=columns, table_style=self.table_style)

__init__(columns)

Initialize the exception table with error data.

Parameters:

Name Type Description Default
columns dict

Dictionary containing error_code, message, and data keys.

required
Source code in src/pyclif/core/output/tables.py
def __init__(self, columns: dict):
    """Initialize the exception table with error data.

    Args:
        columns: Dictionary containing error_code, message, and data keys.
    """
    super().__init__(fields=self.fields, rows=columns, table_style=self.table_style)

Output formats

Format Output Filterable
table Rich table — default format no
rich Live / panels / markdown no
text Plain text: response.message only no
json Syntax-highlighted JSON — always valid JSON yes
yaml Syntax-highlighted YAML — always valid YAML yes
raw Compact JSON, no highlighting — machine-readable yes

table is the default format.

--output-filter accepts a dotted key path (results.0.id, article.title, message). Numeric segments are treated as list indices. Resolution: data["data"] first, then top-level.

  • raw — prints the extracted value as-is. running, not "running". Best for scripting.
  • json — re-serializes the extracted value as valid JSON. Always outputs valid JSON.
  • yaml — re-serializes the extracted value as valid YAML. Always outputs valid YAML.

BaseRenderer

Declarative base class for all pyclif output renderers. Subclass and set class attributes (fields, columns, rich_title, success_message, failure_message) to control every output format without overriding methods.

Key hooks:

  • text(response) — returns response.message as plain text (used by --output-format text)
  • raw(response) — returns a serialized dict for machine-readable output (used by --output-format raw)
  • serialize(response) — returns a JSON-serializable dict (used by json, yaml, and raw)

pyclif.BaseRenderer

Declarative base class for pyclif output renderers.

Subclass and declare class attributes to control every output format. Override individual hooks for custom behavior. Override the full method only as a last resort.

Class attributes

fields: Field names included in JSON/YAML serialization. Empty means all fields via the standard Response.to_json() fallback. columns: Column names for table output. Falls back to fields when empty. rich_title: Panel title used by the default rich() and table() display. success_message: Static success message returned by get_success_message(). failure_message: Static failure message returned by get_failure_message().

Implementation note — class-level lists: fields and columns are ClassVar. Subclasses override them as plain class attributes (never mutated at runtime). get_fields() and get_columns() always return a copy, so callers cannot accidentally mutate the class-level list.

Source code in src/pyclif/core/output/renderer.py
class BaseRenderer:
    """Declarative base class for pyclif output renderers.

    Subclass and declare class attributes to control every output format.
    Override individual hooks for custom behavior. Override the full method
    only as a last resort.

    Class attributes:
        fields: Field names included in JSON/YAML serialization. Empty means
            all fields via the standard Response.to_json() fallback.
        columns: Column names for table output. Falls back to fields when empty.
        rich_title: Panel title used by the default rich() and table() display.
        success_message: Static success message returned by get_success_message().
        failure_message: Static failure message returned by get_failure_message().

    Implementation note — class-level lists: fields and columns are ClassVar.
    Subclasses override them as plain class attributes (never mutated at runtime).
    get_fields() and get_columns() always return a copy, so callers cannot
    accidentally mutate the class-level list.
    """

    fields: ClassVar[list[str]] = []
    columns: ClassVar[list[str]] = []
    rich_title: ClassVar[str] = ""
    success_message: ClassVar[str] = ""
    failure_message: ClassVar[str] = ""

    def get_fields(self) -> list[str]:
        """Return a copy of the declared fields list."""
        return list(self.fields)

    def get_columns(self) -> list[str]:
        """Return a copy of the declared columns list, falling back to fields."""
        return list(self.columns) or list(self.fields)

    def _result_to_row(self, result: OperationResult, columns: list[str]) -> dict:
        """Extract a row dict from an OperationResult for the given column names.

        Checks result.data first (domain payload), then falls back to top-level
        OperationResult attributes (item, success, message, error_code).

        Args:
            result: The operation result to extract data from.
            columns: Column names to extract.

        Returns:
            Dict mapping each column name to its value.
        """
        row = {}
        for col in columns:
            if isinstance(result.data, dict) and col in result.data:
                row[col] = result.data[col]
            else:
                row[col] = getattr(result, col, None)
        return row

    def serialize(self, response: Response) -> dict:
        """Return a JSON-serializable dict filtered to self.fields.

        When fields are empty, delegates to response.to_json() for full
        serialization with standard exclusions.

        Args:
            response: The command response to serialize.

        Returns:
            Dict suitable for JSON/YAML output.
        """
        fields = self.get_fields()
        if not fields:
            return response.to_json()

        results = response.data.get("results", [])
        serialized = [
            {
                f: (r.data.get(f) if isinstance(r.data, dict) else None)
                if isinstance(r.data, dict) and f in r.data
                else getattr(r, f, None)
                for f in fields
            }
            for r in results
        ]
        return {
            "success": response.success,
            "message": response.message,
            "error_code": response.error_code,
            "data": {"results": serialized},
        }

    def table(self, response: Response) -> CliTable:
        """Build a CliTable from response.data["results"] using self.columns.

        Args:
            response: The command response carrying the result list.

        Returns:
            A CliTable instance is ready for console.print().
        """
        # Lazy import — renderer.py and tables.py are in the same package;
        # importing at module level would create a circular dependency via
        # responses.py (which imports BaseRenderer for its renderer field).
        from .tables import CliTable, CliTableColumn  # noqa: PLC0415

        cols = self.get_columns()
        fields_dict: dict[str, CliTableColumn] = {
            col: CliTableColumn(header=col.replace("_", " ").title()) for col in cols
        }
        results = response.data.get("results", [])
        rows = [self._result_to_row(r, cols) for r in results]
        title = self.rich_title or response.message or None
        return CliTable(
            fields=fields_dict,
            rows=rows,
            table_style={"title": title} if title else None,
        )

    # noinspection PyMethodMayBeStatic
    def text(self, response: Response) -> str:
        """Return the response message as plain text.

        Args:
            response: The command response.

        Returns:
            The response message string.
        """
        return response.message

    def raw(self, response: Response) -> dict:
        """Return a serialized dict for machine-readable output.

        Defaults to serialize() output. Override for a custom raw representation.

        Args:
            response: The command response.

        Returns:
            Serialized dict suitable for compact JSON output.
        """
        return self.serialize(response)

    def rich(self, response: Response, console: Console) -> None:
        """Display a panel with the response message.

        Override for panels, rules, markdown, or any static Rich display.

        Args:
            response: The command response to display.
            console: The Rich console to print to.
        """
        title = self.rich_title or None
        console.print(Panel(response.message, title=title))

    def rich_setup(self) -> Any:
        """Return the initial renderable for the Live context.

        Called once before iteration starts. Override to create and store
        stateful Rich objects (Progress, Layout, etc.) as instance attributes
        so rich_on_item() can mutate them.

        Returns:
            A Rich renderable to wrap in Live().
        """
        return Panel("Working…")

    def rich_on_item(self, result: OperationResult, all_so_far: list) -> None:
        """Called after each streamed item inside the Live context.

        Override to mutate the Rich objects created in rich_setup().

        Args:
            result: The latest OperationResult.
            all_so_far: All results received so far, including a result.
        """

    def rich_summary(self, response: Response, console: Console) -> None:
        """Called after all items are processed and the Live context is closed.

        Defaults to the static rich() display. Override for a custom summary.

        Args:
            response: The fully materialized response with all results.
            console: The Rich console to print to.
        """
        self.rich(response, console)

    def get_success_message(self, results: list) -> str:
        """Return the success message for a completed batch.

        Args:
            results: All OperationResult items from the batch.

        Returns:
            Human-readable success message.
        """
        return self.success_message or f"{len(results)} operation(s) completed successfully."

    def get_failure_message(self, results: list) -> str:
        """Return the failure message for a partially or fully failed batch.

        Args:
            results: All OperationResult items from the batch.

        Returns:
            Human-readable failure message with failure count.
        """
        if self.failure_message:
            return self.failure_message
        failed = sum(1 for r in results if not r.success)
        return f"{failed}/{len(results)} operation(s) failed."

get_fields()

Return a copy of the declared fields list.

Source code in src/pyclif/core/output/renderer.py
def get_fields(self) -> list[str]:
    """Return a copy of the declared fields list."""
    return list(self.fields)

get_columns()

Return a copy of the declared columns list, falling back to fields.

Source code in src/pyclif/core/output/renderer.py
def get_columns(self) -> list[str]:
    """Return a copy of the declared columns list, falling back to fields."""
    return list(self.columns) or list(self.fields)

serialize(response)

Return a JSON-serializable dict filtered to self.fields.

When fields are empty, delegates to response.to_json() for full serialization with standard exclusions.

Parameters:

Name Type Description Default
response Response

The command response to serialize.

required

Returns:

Type Description
dict

Dict suitable for JSON/YAML output.

Source code in src/pyclif/core/output/renderer.py
def serialize(self, response: Response) -> dict:
    """Return a JSON-serializable dict filtered to self.fields.

    When fields are empty, delegates to response.to_json() for full
    serialization with standard exclusions.

    Args:
        response: The command response to serialize.

    Returns:
        Dict suitable for JSON/YAML output.
    """
    fields = self.get_fields()
    if not fields:
        return response.to_json()

    results = response.data.get("results", [])
    serialized = [
        {
            f: (r.data.get(f) if isinstance(r.data, dict) else None)
            if isinstance(r.data, dict) and f in r.data
            else getattr(r, f, None)
            for f in fields
        }
        for r in results
    ]
    return {
        "success": response.success,
        "message": response.message,
        "error_code": response.error_code,
        "data": {"results": serialized},
    }

table(response)

Build a CliTable from response.data["results"] using self.columns.

Parameters:

Name Type Description Default
response Response

The command response carrying the result list.

required

Returns:

Type Description
CliTable

A CliTable instance is ready for console.print().

Source code in src/pyclif/core/output/renderer.py
def table(self, response: Response) -> CliTable:
    """Build a CliTable from response.data["results"] using self.columns.

    Args:
        response: The command response carrying the result list.

    Returns:
        A CliTable instance is ready for console.print().
    """
    # Lazy import — renderer.py and tables.py are in the same package;
    # importing at module level would create a circular dependency via
    # responses.py (which imports BaseRenderer for its renderer field).
    from .tables import CliTable, CliTableColumn  # noqa: PLC0415

    cols = self.get_columns()
    fields_dict: dict[str, CliTableColumn] = {
        col: CliTableColumn(header=col.replace("_", " ").title()) for col in cols
    }
    results = response.data.get("results", [])
    rows = [self._result_to_row(r, cols) for r in results]
    title = self.rich_title or response.message or None
    return CliTable(
        fields=fields_dict,
        rows=rows,
        table_style={"title": title} if title else None,
    )

text(response)

Return the response message as plain text.

Parameters:

Name Type Description Default
response Response

The command response.

required

Returns:

Type Description
str

The response message string.

Source code in src/pyclif/core/output/renderer.py
def text(self, response: Response) -> str:
    """Return the response message as plain text.

    Args:
        response: The command response.

    Returns:
        The response message string.
    """
    return response.message

raw(response)

Return a serialized dict for machine-readable output.

Defaults to serialize() output. Override for a custom raw representation.

Parameters:

Name Type Description Default
response Response

The command response.

required

Returns:

Type Description
dict

Serialized dict suitable for compact JSON output.

Source code in src/pyclif/core/output/renderer.py
def raw(self, response: Response) -> dict:
    """Return a serialized dict for machine-readable output.

    Defaults to serialize() output. Override for a custom raw representation.

    Args:
        response: The command response.

    Returns:
        Serialized dict suitable for compact JSON output.
    """
    return self.serialize(response)

rich(response, console)

Display a panel with the response message.

Override for panels, rules, markdown, or any static Rich display.

Parameters:

Name Type Description Default
response Response

The command response to display.

required
console Console

The Rich console to print to.

required
Source code in src/pyclif/core/output/renderer.py
def rich(self, response: Response, console: Console) -> None:
    """Display a panel with the response message.

    Override for panels, rules, markdown, or any static Rich display.

    Args:
        response: The command response to display.
        console: The Rich console to print to.
    """
    title = self.rich_title or None
    console.print(Panel(response.message, title=title))

rich_setup()

Return the initial renderable for the Live context.

Called once before iteration starts. Override to create and store stateful Rich objects (Progress, Layout, etc.) as instance attributes so rich_on_item() can mutate them.

Returns:

Type Description
Any

A Rich renderable to wrap in Live().

Source code in src/pyclif/core/output/renderer.py
def rich_setup(self) -> Any:
    """Return the initial renderable for the Live context.

    Called once before iteration starts. Override to create and store
    stateful Rich objects (Progress, Layout, etc.) as instance attributes
    so rich_on_item() can mutate them.

    Returns:
        A Rich renderable to wrap in Live().
    """
    return Panel("Working…")

rich_on_item(result, all_so_far)

Called after each streamed item inside the Live context.

Override to mutate the Rich objects created in rich_setup().

Parameters:

Name Type Description Default
result OperationResult

The latest OperationResult.

required
all_so_far list

All results received so far, including a result.

required
Source code in src/pyclif/core/output/renderer.py
def rich_on_item(self, result: OperationResult, all_so_far: list) -> None:
    """Called after each streamed item inside the Live context.

    Override to mutate the Rich objects created in rich_setup().

    Args:
        result: The latest OperationResult.
        all_so_far: All results received so far, including a result.
    """

rich_summary(response, console)

Called after all items are processed and the Live context is closed.

Defaults to the static rich() display. Override for a custom summary.

Parameters:

Name Type Description Default
response Response

The fully materialized response with all results.

required
console Console

The Rich console to print to.

required
Source code in src/pyclif/core/output/renderer.py
def rich_summary(self, response: Response, console: Console) -> None:
    """Called after all items are processed and the Live context is closed.

    Defaults to the static rich() display. Override for a custom summary.

    Args:
        response: The fully materialized response with all results.
        console: The Rich console to print to.
    """
    self.rich(response, console)

get_success_message(results)

Return the success message for a completed batch.

Parameters:

Name Type Description Default
results list

All OperationResult items from the batch.

required

Returns:

Type Description
str

Human-readable success message.

Source code in src/pyclif/core/output/renderer.py
def get_success_message(self, results: list) -> str:
    """Return the success message for a completed batch.

    Args:
        results: All OperationResult items from the batch.

    Returns:
        Human-readable success message.
    """
    return self.success_message or f"{len(results)} operation(s) completed successfully."

get_failure_message(results)

Return the failure message for a partially or fully failed batch.

Parameters:

Name Type Description Default
results list

All OperationResult items from the batch.

required

Returns:

Type Description
str

Human-readable failure message with failure count.

Source code in src/pyclif/core/output/renderer.py
def get_failure_message(self, results: list) -> str:
    """Return the failure message for a partially or fully failed batch.

    Args:
        results: All OperationResult items from the batch.

    Returns:
        Human-readable failure message with failure count.
    """
    if self.failure_message:
        return self.failure_message
    failed = sum(1 for r in results if not r.success)
    return f"{failed}/{len(results)} operation(s) failed."

ResponseRenderer

Protocol that all renderer implementations must satisfy. Implement this Protocol directly only when inheriting BaseRenderer is not appropriate.

pyclif.ResponseRenderer

Bases: Protocol

Protocol for renderer implementations.

Renderers are the single source of truth for all output formats of a command. Implement this Protocol directly only when inheriting BaseRenderer is not appropriate. All methods called by the framework must be present.

Source code in src/pyclif/core/output/renderer.py
class ResponseRenderer(Protocol):  # pragma: no cover
    """Protocol for renderer implementations.

    Renderers are the single source of truth for all output formats of a command.
    Implement this Protocol directly only when inheriting BaseRenderer is not
    appropriate. All methods called by the framework must be present.
    """

    def serialize(self, response: Response) -> dict:
        """Return a JSON-serializable dict for the response."""
        ...

    def table(self, response: Response) -> CliTable:
        """Build a CliTable from the response results."""
        ...

    def text(self, response: Response) -> str:
        """Return the response message as plain text."""
        ...

    def raw(self, response: Response) -> dict:
        """Return a serialized dict for machine-readable output."""
        ...

    def rich(self, response: Response, console: Console) -> None:
        """Print a static Rich display (panels, markdown, tables) to the console."""
        ...

    def rich_setup(self) -> Any:
        """Return the initial Rich renderable for the Live context."""
        ...

    def rich_on_item(self, result: OperationResult, all_so_far: list) -> None:
        """Update the Live renderable after each streamed item."""
        ...

    def rich_summary(self, response: Response, console: Console) -> None:
        """Print a summary after the Live context closes."""
        ...

    def get_success_message(self, results: list) -> str:
        """Return the success message for a completed batch."""
        ...

    def get_failure_message(self, results: list) -> str:
        """Return the failure message for a partially or fully failed batch."""
        ...

serialize(response)

Return a JSON-serializable dict for the response.

Source code in src/pyclif/core/output/renderer.py
def serialize(self, response: Response) -> dict:
    """Return a JSON-serializable dict for the response."""
    ...

table(response)

Build a CliTable from the response results.

Source code in src/pyclif/core/output/renderer.py
def table(self, response: Response) -> CliTable:
    """Build a CliTable from the response results."""
    ...

text(response)

Return the response message as plain text.

Source code in src/pyclif/core/output/renderer.py
def text(self, response: Response) -> str:
    """Return the response message as plain text."""
    ...

raw(response)

Return a serialized dict for machine-readable output.

Source code in src/pyclif/core/output/renderer.py
def raw(self, response: Response) -> dict:
    """Return a serialized dict for machine-readable output."""
    ...

rich(response, console)

Print a static Rich display (panels, markdown, tables) to the console.

Source code in src/pyclif/core/output/renderer.py
def rich(self, response: Response, console: Console) -> None:
    """Print a static Rich display (panels, markdown, tables) to the console."""
    ...

rich_setup()

Return the initial Rich renderable for the Live context.

Source code in src/pyclif/core/output/renderer.py
def rich_setup(self) -> Any:
    """Return the initial Rich renderable for the Live context."""
    ...

rich_on_item(result, all_so_far)

Update the Live renderable after each streamed item.

Source code in src/pyclif/core/output/renderer.py
def rich_on_item(self, result: OperationResult, all_so_far: list) -> None:
    """Update the Live renderable after each streamed item."""
    ...

rich_summary(response, console)

Print a summary after the Live context closes.

Source code in src/pyclif/core/output/renderer.py
def rich_summary(self, response: Response, console: Console) -> None:
    """Print a summary after the Live context closes."""
    ...

get_success_message(results)

Return the success message for a completed batch.

Source code in src/pyclif/core/output/renderer.py
def get_success_message(self, results: list) -> str:
    """Return the success message for a completed batch."""
    ...

get_failure_message(results)

Return the failure message for a partially or fully failed batch.

Source code in src/pyclif/core/output/renderer.py
def get_failure_message(self, results: list) -> str:
    """Return the failure message for a partially or fully failed batch."""
    ...