From df1c9ba42b40b8980a042cb46a508b45af4e328c Mon Sep 17 00:00:00 2001 From: Benjamin Auquite Date: Sat, 23 Mar 2024 20:14:35 -0500 Subject: [PATCH] fix critical recursion problem with `safe_repr` --- .../src/pykotor/resource/formats/gff/gff_data.py | 3 --- Libraries/Utility/src/utility/error_handling.py | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Libraries/PyKotor/src/pykotor/resource/formats/gff/gff_data.py b/Libraries/PyKotor/src/pykotor/resource/formats/gff/gff_data.py index efe4e6cfd..a431354a9 100644 --- a/Libraries/PyKotor/src/pykotor/resource/formats/gff/gff_data.py +++ b/Libraries/PyKotor/src/pykotor/resource/formats/gff/gff_data.py @@ -329,9 +329,6 @@ def __init__( self._field_type: GFFFieldType = field_type self._value: Any = value - def __repr__(self): - return safe_repr(self) - def field_type( self, ) -> GFFFieldType: diff --git a/Libraries/Utility/src/utility/error_handling.py b/Libraries/Utility/src/utility/error_handling.py index 6c7edfa59..54522ac2a 100644 --- a/Libraries/Utility/src/utility/error_handling.py +++ b/Libraries/Utility/src/utility/error_handling.py @@ -107,7 +107,7 @@ def is_builtin_class_instance(obj: Any) -> bool: _currently_processing: ContextVar[list] = ContextVar("_currently_processing", default=[]) -def safe_repr(obj: Any, max_length: int = 200, indent_level: int = 0) -> str: +def safe_repr(obj: Any, max_length: int = 200, indent_level: int = 0, max_depth: int = 3, _depth: int = 0) -> str: """Safely generate a repr string for objects without a custom __repr__, with line wrapping and indentation.""" if is_builtin_class_instance(obj): try: @@ -132,6 +132,17 @@ def safe_repr(obj: Any, max_length: int = 200, indent_level: int = 0) -> str: base_indent = indent * frame["indent_level"] return f"{frame['representation']}...\n{base_indent})" + if _depth > max_depth: + try: + obj_repr = repr(obj) + # Truncate if necessary + if len(obj_repr) > max_length: + return f"{obj_repr[:max_length]}..." + except Exception: # noqa: BLE001 + return object.__repr__(obj) + else: + return obj_repr + try: # Initialize the representation for this object and add it to the stack base_indent = indent * indent_level @@ -154,7 +165,7 @@ def safe_repr(obj: Any, max_length: int = 200, indent_level: int = 0) -> str: attr_value = getattr(obj, attr_name) if not attr_name.startswith("__") and not callable(attr_value): try: - this_repr = safe_repr(attr_value, max_length, indent_level + 1) + this_repr = safe_repr(attr_value, max_length, indent_level + 1, _depth=_depth+1) # Concatenate attribute name and its representation with appropriate indentation attr_repr = f"{attr_name}={this_repr}" # Check if current attribute representation exceeds the max length