"""Module for representing objects."""


def default_repr(
    elem: object,
    *,
    class_name: str | None = None,
    module_name: str | None = None,
) -> str:
    """
    Generate a string representation of an object.

    This function generates a string representation of a given Python object.
    It is particularly useful for debugging or logging, where a clear and informative
    representation of an object is needed.

    The function takes an object, an optional class name and a optional module name as
    input. If the class name is provided, it is used as the class name in the
    representation. If not provided, the function uses the actual class name of the
    object. If the module name is provided, it is used as the module path in the
    representation. If not provided, the function constructs the module path by
    extracting the module name from the object's type and filtering out any private
    elements (those starting with an underscore).

    Parameters
    ----------
    elem
        The Python object to represent
    class_name
        An optional class name to use in the representation
    module_name
        An optional module name to use in the representation. If not provided,
        the module name is derived from the object's type. If provided and does not end
        with a dot, a dot is appended to it.

    Returns
    -------
    str
        A string representation of the object

    """
    class_name = class_name or type(elem).__name__
    if module_name is None:
        module_name = (
            ".".join(
                element
                for element in type(elem).__module__.split(".")
                if element[0] != "_"
            )
            + "."
        )
    elif module_name and module_name[-1] != ".":
        module_name += "."
    return f"<{module_name}{class_name} object at {id(elem):#x}>"
