diff --git a/metricflow-semantics/metricflow_semantics/mf_logging/lazy_formattable.py b/metricflow-semantics/metricflow_semantics/mf_logging/lazy_formattable.py index e4f994ae3..320570c0e 100644 --- a/metricflow-semantics/metricflow_semantics/mf_logging/lazy_formattable.py +++ b/metricflow-semantics/metricflow_semantics/mf_logging/lazy_formattable.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import cached_property -from typing import Callable, Union +from typing import Callable, Optional, Union from typing_extensions import override @@ -35,7 +35,7 @@ class LazyFormat: logger.debug(LazyFormat(lambda: f"Result is: {expensive_function()}") """ - def __init__(self, message: Union[str, Callable[[], str]], **kwargs) -> None: # type: ignore[no-untyped-def] + def __init__(self, message: Optional[Union[str, Callable[[], str]]] = None, **kwargs) -> None: # type: ignore[no-untyped-def] """Initializer. Args: @@ -47,12 +47,13 @@ def __init__(self, message: Union[str, Callable[[], str]], **kwargs) -> None: # @cached_property def _str_value(self) -> str: + message: Optional[str] """Cache the result as `__str__` can be called multiple times for multiple log handlers.""" - if callable(self._message): + if self._message is not None and callable(self._message): message = self._message() else: message = self._message - return mf_pformat_dict(message, self._kwargs, preserve_raw_strings=True) + return mf_pformat_dict(description=message, obj_dict=self._kwargs, preserve_raw_strings=True) @override def __str__(self) -> str: diff --git a/metricflow-semantics/metricflow_semantics/mf_logging/pretty_print.py b/metricflow-semantics/metricflow_semantics/mf_logging/pretty_print.py index 5b1ffada2..f9e5c5837 100644 --- a/metricflow-semantics/metricflow_semantics/mf_logging/pretty_print.py +++ b/metricflow-semantics/metricflow_semantics/mf_logging/pretty_print.py @@ -5,7 +5,7 @@ from collections.abc import Mapping from dataclasses import fields, is_dataclass from enum import Enum -from typing import Any, Dict, List, Optional, Sized, Tuple, Union +from typing import Any, List, Optional, Sized, Tuple, Union from dsi_pydantic_shim import BaseModel @@ -429,8 +429,8 @@ def mf_pformat( # type: ignore def mf_pformat_dict( # type: ignore - description: str, - obj_dict: Dict[str, Any], + description: Optional[str] = None, + obj_dict: Optional[Mapping[str, Any]] = None, max_line_length: int = 120, indent_prefix: str = " ", include_object_field_names: bool = True, @@ -444,7 +444,8 @@ def mf_pformat_dict( # type: ignore representation of the string. e.g. if value="foo", then "foo" instead of "'foo'". Useful for values that contain newlines. """ - lines: List[str] = [description] + lines: List[str] = [description] if description is not None else [] + obj_dict = obj_dict or {} for key, value in obj_dict.items(): if preserve_raw_strings and isinstance(value, str): value_str = value @@ -471,5 +472,9 @@ def mf_pformat_dict( # type: ignore else: item_block_lines = (f"{key}: {value_str}",) item_block = "\n".join(item_block_lines) - lines.append(indent(item_block)) + + if description is None: + lines.append(item_block) + else: + lines.append(indent(item_block)) return "\n".join(lines) diff --git a/metricflow-semantics/tests_metricflow_semantics/collection_helpers/test_pretty_print.py b/metricflow-semantics/tests_metricflow_semantics/collection_helpers/test_pretty_print.py index 1c9bdde49..4c42b1ac1 100644 --- a/metricflow-semantics/tests_metricflow_semantics/collection_helpers/test_pretty_print.py +++ b/metricflow-semantics/tests_metricflow_semantics/collection_helpers/test_pretty_print.py @@ -7,6 +7,7 @@ from dbt_semantic_interfaces.implementations.elements.dimension import PydanticDimension from dbt_semantic_interfaces.type_enums import DimensionType +from metricflow_semantics.formatting.formatting_helpers import mf_dedent from metricflow_semantics.mf_logging.formatting import indent from metricflow_semantics.mf_logging.pretty_formattable import MetricFlowPrettyFormattable from metricflow_semantics.mf_logging.pretty_print import mf_pformat, mf_pformat_dict @@ -202,3 +203,18 @@ def pretty_format(self) -> Optional[str]: return f"{self.__class__.__name__}({self.field_0:.2f})" assert mf_pformat(_ExampleDataclass(1.2345)) == f"{_ExampleDataclass.__name__}(1.23)" + + +def test_pformat_dict_with_empty_message() -> None: + """Test `mf_pformat_dict` without a description.""" + result = mf_pformat_dict(obj_dict={"object_0": (1, 2, 3), "object_1": {4: 5}}) + + assert ( + mf_dedent( + """ + object_0: (1, 2, 3) + object_1: {4: 5} + """ + ) + == result + )