Source code for hbutils.model.repr

"""
Representation formatting utilities for model-like objects.

This module provides a small but flexible utility for generating consistent
``__repr__`` output across custom classes. It is designed to simplify the
construction of informative object representations while allowing conditional
display of attributes.

The module contains the following public function:

* :func:`get_repr_info` - Build a formatted representation string for an object

Example::

    >>> from hbutils.model.repr import get_repr_info
    >>> class Point:
    ...     def __init__(self, x, y=None):
    ...         self._x = x
    ...         self._y = y
    ...     def __repr__(self):
    ...         return get_repr_info(
    ...             cls=self.__class__,
    ...             args=[
    ...                 ('y', (lambda: self._y, lambda: self._y is not None)),
    ...                 ('x', lambda: self._x),
    ...             ]
    ...         )
    ...
    >>> Point(3, 4)
    <Point y: 4, x: 3>
    >>> Point(3)
    <Point x: 3>

.. note::
   This utility does not access object state directly; all data access is done
   through the provided callables. This makes it safe to use for lazily computed
   properties or sensitive attributes.
"""
from typing import List, Tuple, Callable, Union, Any, Sequence

__all__ = [
    'get_repr_info',
]


[docs] def get_repr_info( cls: type, args: Sequence[ Union[ Tuple[str, Callable[[], Any]], Tuple[str, Tuple[Callable[[], Any], Callable[[], bool]]], Tuple[str, Callable[[], Any], Callable[[], bool]], ] ], ) -> str: """ Get representation information for object. This function generates a formatted string representation of an object based on the provided class type and argument information. It can be used in ``__repr__`` methods to create consistent and informative object representations. :param cls: The class type of the object to represent. :type cls: type :param args: A sequence of tuples describing how each field should be shown. Each item must be one of the following forms: - ``(name, data_func)``: Always display ``name`` with value from ``data_func``. - ``(name, (data_func, present_func))``: Display only if ``present_func`` is ``True``. - ``(name, data_func, present_func)``: Equivalent to the previous form. Where: - ``name`` (str): The name of the field to display. - ``data_func`` (Callable): Callable returning the value to display. - ``present_func`` (Callable): Callable returning ``True`` if the field should be displayed. :type args: Sequence[Union[Tuple[str, Callable[[], Any]], Tuple[str, Tuple[Callable[[], Any], Callable[[], bool]]], Tuple[str, Callable[[], Any], Callable[[], bool]]]] :return: A formatted representation string in the format ``<ClassName arg1: value1, arg2: value2>``. If no fields are displayed, the format is ``<ClassName>``. :rtype: str :raises ValueError: If a tuple's length is not 2 or 3. :raises TypeError: If an argument item is not a tuple. Example:: >>> from hbutils.model.repr import get_repr_info >>> class Sum: ... def __init__(self, a, b): ... self.__a = a ... self.__b = b ... def __repr__(self): ... return get_repr_info( ... cls=self.__class__, ... args=[ ... ('b', lambda: self.__b, lambda: self.__b is not None), ... ('a', lambda: self.__a), ... ] ... ) ... >>> Sum(1, 2) <Sum b: 2, a: 1> >>> Sum(1, None) <Sum a: 1> >>> Sum(None, None) <Sum a: None> """ _data_items = [] for item in args: if isinstance(item, tuple): if len(item) == 2: name, fd = item if isinstance(fd, tuple): _data_func, _present_func = fd else: _data_func, _present_func = fd, lambda: True elif len(item) == 3: name, _data_func, _present_func = item else: raise ValueError('Tuple\'s length should be 2 or 3 but {actual} found.'.format(actual=repr(len(item)))) if _present_func(): _data_items.append('{name}: {data}'.format(name=name, data=_data_func())) else: raise TypeError( 'Argument item should be tuple but {actual} found.'.format(actual=repr(type(item).__name__))) if _data_items: return '<{cls} {data}>'.format(cls=cls.__name__, data=', '.join(_data_items)) else: return '<{cls}>'.format(cls=cls.__name__)