From 04dafc7ec97841e87dcf5ad9113843ab265bd4cc Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 19 Feb 2026 14:16:02 +0200 Subject: [PATCH] fix: handle errors in serialization --- pyproject.toml | 2 +- src/uipath/core/serialization/json.py | 8 ++++++++ tests/serialization/test_json.py | 24 ++++++++++++++++++++++++ uv.lock | 2 +- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5a8d56f..aea629d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-core" -version = "0.5.0" +version = "0.5.1" description = "UiPath Core abstractions" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/src/uipath/core/serialization/json.py b/src/uipath/core/serialization/json.py index 0208454..a29c3b1 100644 --- a/src/uipath/core/serialization/json.py +++ b/src/uipath/core/serialization/json.py @@ -86,6 +86,10 @@ def serialize_defaults( if hasattr(obj, "to_dict") and not isinstance(obj, type): return obj.to_dict() + # Handle objects with as_dict property (UiPathBaseRuntimeError) + if hasattr(obj, "as_dict") and not isinstance(obj, type): + return obj.as_dict + # Handle dataclasses if is_dataclass(obj) and not isinstance(obj, type): return asdict(obj) @@ -107,6 +111,10 @@ def serialize_defaults( # Convert to list return list(obj) + # Handle exceptions + if isinstance(obj, Exception): + return str(obj) + # Handle datetime objects if isinstance(obj, datetime): return obj.isoformat() diff --git a/tests/serialization/test_json.py b/tests/serialization/test_json.py index 663b292..7f1b134 100644 --- a/tests/serialization/test_json.py +++ b/tests/serialization/test_json.py @@ -268,6 +268,20 @@ def test_serializes_named_tuple(self) -> None: assert isinstance(parsed["point"], list) assert parsed["point"] == [10, 20] + def test_serializes_object_with_as_dict(self) -> None: + """Test object with as_dict property via json.dumps.""" + + class RuntimeLike: + @property + def as_dict(self) -> dict[str, Any]: + return {"host": "localhost", "port": 8080} + + obj = RuntimeLike() + data = {"runtime": obj} + result = serialize_json(data) + parsed = json.loads(result) + assert parsed["runtime"] == {"host": "localhost", "port": 8080} + def test_serializes_object_with_to_dict(self) -> None: """Test object with to_dict method via json.dumps.""" @@ -294,6 +308,14 @@ def __str__(self) -> str: parsed = json.loads(result) assert parsed["obj"] == "custom_string" + def test_serializes_exception(self) -> None: + """Test Exception serialization via json.dumps.""" + err = ValueError("something went wrong") + data = {"error": err} + result = serialize_json(data) + parsed = json.loads(result) + assert parsed["error"] == "something went wrong" + def test_with_json_dumps(self) -> None: """Test integration with json.dumps().""" @@ -325,6 +347,7 @@ def test_with_json_dumps_complex_nested(self) -> None: "datetime": datetime(2024, 1, 1), "set": {1, 2, 3}, "tuple": (4, 5, 6), + "error": ValueError("something failed"), } result = serialize_json(data) @@ -336,6 +359,7 @@ def test_with_json_dumps_complex_nested(self) -> None: assert "2024-01-01" in parsed["datetime"] assert set(parsed["set"]) == {1, 2, 3} assert parsed["tuple"] == [4, 5, 6] + assert parsed["error"] == "something failed" def test_with_list_of_pydantic_models(self) -> None: """Test with list of Pydantic models (common MCP scenario).""" diff --git a/uv.lock b/uv.lock index 30df5af..c7b74e9 100644 --- a/uv.lock +++ b/uv.lock @@ -1007,7 +1007,7 @@ wheels = [ [[package]] name = "uipath-core" -version = "0.5.0" +version = "0.5.1" source = { editable = "." } dependencies = [ { name = "opentelemetry-instrumentation" },