diff --git a/RELEASE.md b/RELEASE.md index 72cfee8cdb..b93010273a 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -12,6 +12,9 @@ Please follow the established format: - Update Kedro-Viz telemetry for opt-out model (#2022) +## Bug fixes and other changes + +- Fix unserializable parameters value (#2122) # Release 10.0.0 diff --git a/package/kedro_viz/data_access/repositories/catalog.py b/package/kedro_viz/data_access/repositories/catalog.py index a7d568ca8a..38d9a6772d 100644 --- a/package/kedro_viz/data_access/repositories/catalog.py +++ b/package/kedro_viz/data_access/repositories/catalog.py @@ -131,7 +131,7 @@ def get_dataset(self, dataset_name: str) -> Optional["AbstractDataset"]: else: # pragma: no cover dataset_obj = self._catalog._get_dataset(dataset_name) except DatasetNotFoundError: - dataset_obj = MemoryDataset() # pylint: disable=abstract-class-instantiated + dataset_obj = MemoryDataset() return dataset_obj diff --git a/package/kedro_viz/models/flowchart.py b/package/kedro_viz/models/flowchart.py index 9e6cb087d4..8828650a7e 100644 --- a/package/kedro_viz/models/flowchart.py +++ b/package/kedro_viz/models/flowchart.py @@ -9,6 +9,7 @@ from types import FunctionType from typing import Any, ClassVar, Dict, List, Optional, Set, Union, cast +from fastapi.encoders import jsonable_encoder from kedro.pipeline.node import Node as KedroNode from pydantic import ( BaseModel, @@ -861,7 +862,13 @@ def parameter_value(self) -> Any: return None try: - return self.kedro_obj.load() + actual_parameter_value = self.kedro_obj.load() + # Return only json serializable value + return jsonable_encoder(actual_parameter_value) + except (TypeError, ValueError, RecursionError): + # In case the parameter is not JSON serializable, + # return the string representation + return str(actual_parameter_value) except (AttributeError, DatasetError): # This except clause triggers if the user passes a parameter that is not # defined in the catalog (DatasetError) it also catches any case where @@ -870,6 +877,14 @@ def parameter_value(self) -> Any: "Cannot find parameter `%s` in the catalog.", self.parameter_name ) return None + # pylint: disable=broad-exception-caught + except Exception as exc: # pragma: no cover + logger.error( + "An error occurred when loading parameter `%s` in the catalog :: %s", + self.parameter_name, + exc, + ) + return None class ParametersNodeMetadata(GraphNodeMetadata): diff --git a/package/kedro_viz/models/utils.py b/package/kedro_viz/models/utils.py index 1749946a3f..0024f13e22 100644 --- a/package/kedro_viz/models/utils.py +++ b/package/kedro_viz/models/utils.py @@ -1,4 +1,5 @@ """`kedro_viz.models.utils` contains utility functions used in the `kedro_viz.models` package""" + import logging from typing import TYPE_CHECKING diff --git a/package/tests/test_models/test_flowchart.py b/package/tests/test_models/test_flowchart.py index 521cc419f8..eab5283817 100644 --- a/package/tests/test_models/test_flowchart.py +++ b/package/tests/test_models/test_flowchart.py @@ -1,7 +1,7 @@ from functools import partial from pathlib import Path from textwrap import dedent -from unittest.mock import call, patch +from unittest.mock import Mock, call, patch import pytest from kedro.io import MemoryDataset @@ -215,6 +215,46 @@ def test_create_parameters_node_single_parameter( assert parameters_node.parameter_value == 0.3 assert parameters_node.modular_pipelines == expected_modular_pipelines + def test_create_single_parameter_with_complex_type(self): + parameters_dataset = MemoryDataset(data=object()) + parameters_node = GraphNode.create_parameters_node( + dataset_id="params:test_split_ratio", + dataset_name="params:test_split_ratio", + layer=None, + tags=set(), + parameters=parameters_dataset, + modular_pipelines=set(), + ) + assert isinstance(parameters_node, ParametersNode) + assert parameters_node.kedro_obj is parameters_dataset + assert not parameters_node.is_all_parameters() + assert parameters_node.is_single_parameter() + assert isinstance(parameters_node.parameter_value, str) + + def test_create_all_parameters_with_complex_type(self): + mock_object = Mock() + parameters_dataset = MemoryDataset( + data={ + "test_split_ratio": 0.3, + "num_epochs": 1000, + "complex_param": mock_object, + } + ) + parameters_node = GraphNode.create_parameters_node( + dataset_id="parameters", + dataset_name="parameters", + layer=None, + tags=set(), + parameters=parameters_dataset, + modular_pipelines=set(), + ) + assert isinstance(parameters_node, ParametersNode) + assert parameters_node.kedro_obj is parameters_dataset + assert parameters_node.id == "parameters" + assert parameters_node.is_all_parameters() + assert not parameters_node.is_single_parameter() + assert isinstance(parameters_node.parameter_value, str) + def test_create_non_existing_parameter_node(self): """Test the case where ``parameters`` is equal to None""" parameters_node = GraphNode.create_parameters_node(