Skip to content

Print Class Objects Sub-Module

t3co.utils.print_class_objects

obj_to_string(obj, indent=' ')

Converts an object or list of objects to a formatted string representation using a flattened dictionary view for objects with nested dictionaries.

Parameters:

Name Type Description Default
obj Union[object, List[object]]

The object or list of objects to convert.

required
indent str

Indentation string for nested objects. Defaults to " ".

' '

Returns:

Name Type Description
str str

Formatted string representation of the object.

Source code in src/t3co/utils/print_class_objects.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def obj_to_string(obj: Union[object, List[object]], indent: str = "    ") -> str:
    """
    Converts an object or list of objects to a formatted string representation
    using a flattened dictionary view for objects with nested dictionaries.

    Args:
        obj (Union[object, List[object]]): The object or list of objects to convert.
        indent (str, optional): Indentation string for nested objects. Defaults to "    ".

    Returns:
        str: Formatted string representation of the object.
    """
    if isinstance(obj, list):
        return (
            "[\n"
            + ",\n".join(indent + obj_to_string(item, indent + "    ") for item in obj)
            + "\n"
            + indent[:-4]
            + "]"
        )
    elif isinstance(obj, dict):
        # If already a dict, pretty-print it.
        return json.dumps(obj, indent=4)
    elif hasattr(obj, "__dict__"):
        return f"{obj.__class__.__name__}\n" + "\n".join(
            f"{indent}{attr} = {obj_to_string(value, indent + '    ')}"
            for attr, value in sorted(vars(obj).items())
        )
    return str(obj)

handle_nan(obj)

Replaces NaN values in an object with None.

Parameters:

Name Type Description Default
obj Union[float, dict, list, Any]

The object to process.

required

Returns:

Type Description
Union[None, dict, list, float, Any]

Union[None, dict, list, float, Any]: The processed object with NaN values replaced by None.

Source code in src/t3co/utils/print_class_objects.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def handle_nan(
    obj: Union[float, dict, list, Any],
) -> Union[None, dict, list, float, Any]:
    """
    Replaces NaN values in an object with None.

    Args:
        obj (Union[float, dict, list, Any]): The object to process.

    Returns:
        Union[None, dict, list, float, Any]: The processed object with NaN values replaced by None.
    """
    if isinstance(obj, float) and pd.isna(obj):
        return None
    elif isinstance(obj, dict):
        return {k: handle_nan(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [handle_nan(v) for v in obj]
    return obj

custom_default(obj)

Custom default function for JSON serialization.

Parameters:

Name Type Description Default
obj Any

The object to serialize.

required

Returns:

Type Description
Union[None, dict, str]

Union[None, dict, str]: The serialized object.

Source code in src/t3co/utils/print_class_objects.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def custom_default(obj: Any) -> Union[None, dict, str]:
    """
    Custom default function for JSON serialization.

    Args:
        obj (Any): The object to serialize.

    Returns:
        Union[None, dict, str]: The serialized object.
    """
    if isinstance(obj, float) and pd.isna(obj):
        return None
    elif isinstance(obj, Path):
        return str(obj)
    elif isinstance(obj, pd.DataFrame):
        return None
    elif hasattr(obj, "__dict__"):
        return vars(obj)
    return str(obj)

to_flat_dict(obj, include_prefix=True, prefix='', delimiter='_', nested_attrs=None, include_calcs=True, exclude_list_fields=False, json_lists=True)

Flattens a nested object into a dictionary while preserving the order of declared attributes. Nested dictionaries and list items that are dict-like (or objects with dict) are recursively flattened.

Parameters:

Name Type Description Default
obj object

The object to flatten.

required
include_prefix bool

Whether to include the prefix in the keys. Defaults to True.

True
prefix str

The prefix for the keys. Defaults to "".

''
delimiter str

The delimiter for the keys. Defaults to "_".

'_'
nested_attrs List[str]

List of attribute names to include as nested dictionaries. Defaults to None.

None
include_calcs bool

Whether to include calculations (e.g. tco_per_year). Defaults to True.

True
exclude_list_fields bool

Whether to exclude list/array fields to reduce CSV size. Defaults to False.

False
json_lists bool

Whether to convert lists to JSON strings for CSV compatibility. Defaults to True.

True

Returns:

Name Type Description
dict dict

The flattened dictionary.

Source code in src/t3co/utils/print_class_objects.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def to_flat_dict(
    obj: object,
    include_prefix: bool = True,
    prefix: str = "",
    delimiter: str = "_",
    nested_attrs: List[str] = None,
    include_calcs: bool = True,
    exclude_list_fields: bool = False,
    json_lists: bool = True,
) -> dict:
    """
    Flattens a nested object into a dictionary while preserving the order of declared attributes.
    Nested dictionaries and list items that are dict-like (or objects with __dict__)
    are recursively flattened.

    Args:
        obj (object): The object to flatten.
        include_prefix (bool, optional): Whether to include the prefix in the keys. Defaults to True.
        prefix (str, optional): The prefix for the keys. Defaults to "".
        delimiter (str, optional): The delimiter for the keys. Defaults to "_".
        nested_attrs (List[str], optional): List of attribute names to include as nested dictionaries. Defaults to None.
        include_calcs (bool, optional): Whether to include calculations (e.g. tco_per_year). Defaults to True.
        exclude_list_fields (bool, optional): Whether to exclude list/array fields to reduce CSV size. Defaults to False.
        json_lists (bool, optional): Whether to convert lists to JSON strings for CSV compatibility. Defaults to True.

    Returns:
        dict: The flattened dictionary.
    """

    flat_dict = {}
    nested_attrs = nested_attrs or []

    def flatten(item, current_prefix):
        """Recursively flattens attributes."""
        if isinstance(item, dict):
            for key, value in item.items():
                new_key = f"{current_prefix}{delimiter}{key}" if current_prefix else key
                flatten(value, new_key if include_prefix else key)
        elif isinstance(item, list):
            # Skip lists if exclude_list_fields is True (reduces CSV file size significantly)
            # However, do not exclude if the field is explicitly in nested_attrs (e.g. tco_per_year)
            if exclude_list_fields and current_prefix not in nested_attrs:
                return
            # For lists, flatten each item if it is dict-like.
            flat_list = []
            for sub_item in item:
                if isinstance(sub_item, dict) or hasattr(sub_item, "__dict__"):
                    flat_list.append(
                        to_flat_dict(
                            sub_item,
                            include_prefix,
                            "",
                            delimiter,
                            nested_attrs,
                            include_calcs,
                            exclude_list_fields,
                            json_lists,
                        )
                    )
                else:
                    if isinstance(sub_item, (float, np.floating)) and (
                        "cost" in current_prefix.lower()
                        or "dol" in current_prefix.lower()
                    ):
                        flat_list.append(round(float(sub_item), 2))
                    else:
                        flat_list.append(sub_item)
            # Convert to JSON string for CSV compatibility if json_lists is True
            # Ensure no newlines or extra spaces in JSON string
            # Also handle NaN values to ensure valid JSON
            if json_lists:
                flat_list = handle_nan(flat_list)
                flat_dict[current_prefix] = json.dumps(flat_list, separators=(",", ":"))
            else:
                flat_dict[current_prefix] = flat_list
        elif hasattr(item, "__dict__"):
            # If the item is an object, flatten its __dict__.
            flatten(vars(item), current_prefix)
        else:
            if isinstance(item, (float, np.floating)) and (
                "cost" in current_prefix.lower() or "dol" in current_prefix.lower()
            ):
                flat_dict[current_prefix] = round(float(item), 2)
            else:
                flat_dict[current_prefix] = item

    if hasattr(obj, "__dict__"):
        # Extract attributes in the order they were declared.
        cls = obj.__class__
        declared_attributes = list(getattr(cls, "__annotations__", {}).keys())
        instance_attributes = list(vars(obj).keys())

        # Maintain order: declared first, then dynamically assigned attributes.
        ordered_fields = OrderedDict.fromkeys(declared_attributes + instance_attributes)

        # Create ordered dictionary of attributes.
        ordered_obj = OrderedDict(
            (field, getattr(obj, field, None)) for field in ordered_fields
        )

        if not include_calcs and "tco_per_year" in ordered_obj:
            del ordered_obj["tco_per_year"]

        # Flatten the ordered object.
        flatten(ordered_obj, prefix if include_prefix else "")
    else:
        flatten(obj, prefix if include_prefix else "")

    return flat_dict

remove_df_attrs(obj)

Removes attributes from an object if they are DataFrame instances.

Parameters:

Name Type Description Default
obj object

The object to process.

required
Source code in src/t3co/utils/print_class_objects.py
196
197
198
199
200
201
202
203
204
205
def remove_df_attrs(obj: object) -> None:
    """
    Removes attributes from an object if they are DataFrame instances.

    Args:
        obj (object): The object to process.
    """
    for attr in list(vars(obj).keys()):  # Use `list()` to avoid modification issues.
        if isinstance(getattr(obj, attr), pd.DataFrame):
            delattr(obj, attr)