Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.

Commit 42d8f3e

Browse files
authored
Merge pull request #28 from UiPath/feature/basic-serialization-method
feat: simple serialization defaults
2 parents c88d5da + b6f71ac commit 42d8f3e

6 files changed

Lines changed: 776 additions & 55 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-core"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
description = "UiPath Core abstractions"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Serialization utilities for converting Python objects to various formats."""
2+
3+
from .json import serialize_defaults, serialize_json
4+
5+
__all__ = ["serialize_defaults", "serialize_json"]
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""JSON serialization utilities for converting Python objects to JSON formats."""
2+
3+
import json
4+
from dataclasses import asdict, is_dataclass
5+
from datetime import datetime, timezone
6+
from enum import Enum
7+
from typing import Any, cast
8+
from zoneinfo import ZoneInfo
9+
10+
from pydantic import BaseModel
11+
12+
13+
def serialize_defaults(
14+
obj: Any,
15+
) -> dict[str, Any] | list[Any] | str | int | float | bool | None:
16+
"""Convert Python objects to JSON-serializable formats.
17+
18+
Handles common Python types that are not natively JSON-serializable:
19+
- Pydantic models (v1 and v2)
20+
- Dataclasses
21+
- Enums
22+
- Datetime objects
23+
- Timezone objects
24+
- Named tuples
25+
- Sets and tuples
26+
27+
This function is designed to be used as the `default` parameter in json.dumps():
28+
```python
29+
import json
30+
result = json.dumps(obj, default=serialize_defaults)
31+
```
32+
33+
Or use the convenience function `serialize_json()` which wraps this:
34+
```python
35+
result = serialize_json(obj)
36+
```
37+
38+
Args:
39+
obj: The object to serialize
40+
41+
Returns:
42+
A JSON-serializable representation of the object:
43+
- Pydantic models: dict from model_dump()
44+
- Dataclasses: dict from asdict()
45+
- Enums: the enum value (recursively serialized)
46+
- datetime: ISO format string
47+
- timezone/ZoneInfo: timezone name
48+
- sets/tuples: converted to lists
49+
- named tuples: converted to dict
50+
- Primitives (None, bool, int, float, str, list, dict): returned unchanged
51+
- Other types: converted to string with str()
52+
53+
Examples:
54+
>>> from datetime import datetime
55+
>>> from pydantic import BaseModel
56+
>>>
57+
>>> class User(BaseModel):
58+
... name: str
59+
... created_at: datetime
60+
>>>
61+
>>> user = User(name="Alice", created_at=datetime.now())
62+
>>> import json
63+
>>> json.dumps(user, default=serialize_defaults)
64+
'{"name": "Alice", "created_at": "2024-01-01T12:00:00"}'
65+
>>> # Or use the convenience function
66+
>>> serialize_json(user)
67+
'{"name": "Alice", "created_at": "2024-01-01T12:00:00"}'
68+
"""
69+
# Handle Pydantic BaseModel instances
70+
if hasattr(obj, "model_dump") and not isinstance(obj, type):
71+
return obj.model_dump(exclude_none=True, mode="json")
72+
73+
# Handle Pydantic model classes - convert to schema representation
74+
if isinstance(obj, type) and issubclass(obj, BaseModel):
75+
return {
76+
"__class__": obj.__name__,
77+
"__module__": obj.__module__,
78+
"schema": obj.model_json_schema(),
79+
}
80+
81+
# Handle Pydantic v1 models
82+
if hasattr(obj, "dict") and not isinstance(obj, type):
83+
return obj.dict()
84+
85+
# Handle objects with to_dict method
86+
if hasattr(obj, "to_dict") and not isinstance(obj, type):
87+
return obj.to_dict()
88+
89+
# Handle dataclasses
90+
if is_dataclass(obj) and not isinstance(obj, type):
91+
return asdict(obj)
92+
93+
# Handle enums - recursively serialize the value
94+
if isinstance(obj, Enum):
95+
return serialize_defaults(obj.value)
96+
97+
# Handle sets and tuples
98+
if isinstance(obj, (set, tuple)):
99+
# Check if it's a named tuple (has _asdict method)
100+
if hasattr(obj, "_asdict") and callable(
101+
obj._asdict # pyright: ignore[reportAttributeAccessIssue]
102+
):
103+
return cast(
104+
dict[str, Any],
105+
obj._asdict(), # pyright: ignore[reportAttributeAccessIssue]
106+
)
107+
# Convert to list
108+
return list(obj)
109+
110+
# Handle datetime objects
111+
if isinstance(obj, datetime):
112+
return obj.isoformat()
113+
114+
# Handle timezone objects
115+
if isinstance(obj, (timezone, ZoneInfo)):
116+
return obj.tzname(None)
117+
118+
# Allow JSON-serializable primitives to pass through unchanged
119+
if obj is None or isinstance(obj, (bool, int, float, str, list, dict)):
120+
return obj
121+
122+
# Fallback: convert to string
123+
return str(obj)
124+
125+
126+
def serialize_json(obj: Any) -> str:
127+
"""Serialize Python object to JSON string.
128+
129+
This is a convenience function that wraps json.dumps() with serialize_defaults()
130+
as the default handler for non-JSON-serializable types.
131+
132+
Args:
133+
obj: The object to serialize to JSON
134+
135+
Returns:
136+
JSON string representation of the object
137+
138+
Examples:
139+
>>> from datetime import datetime
140+
>>> from pydantic import BaseModel
141+
>>>
142+
>>> class Task(BaseModel):
143+
... name: str
144+
... created: datetime
145+
>>>
146+
>>> task = Task(name="Review PR", created=datetime(2024, 1, 15, 10, 30))
147+
>>> serialize_json(task)
148+
'{"name": "Review PR", "created": "2024-01-15T10:30:00"}'
149+
"""
150+
return json.dumps(obj, default=serialize_defaults)

src/uipath/core/tracing/_utils.py

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
import inspect
44
import json
55
from collections.abc import Callable
6-
from dataclasses import asdict, is_dataclass
7-
from datetime import datetime, timezone
8-
from enum import Enum
9-
from typing import Any, Mapping, Optional, cast
10-
from zoneinfo import ZoneInfo
6+
from typing import Any, Mapping, Optional
117

128
from opentelemetry.trace import Span
13-
from pydantic import BaseModel
9+
10+
from uipath.core.serialization import serialize_json
1411

1512

1613
def get_supported_params(
@@ -31,64 +28,19 @@ def get_supported_params(
3128
return supported
3229

3330

34-
def _simple_serialize_defaults(
35-
obj: Any,
36-
) -> dict[str, Any] | list[Any] | str | int | float | bool | None:
37-
# Handle Pydantic BaseModel instances
38-
if hasattr(obj, "model_dump") and not isinstance(obj, type):
39-
return obj.model_dump(exclude_none=True, mode="json")
40-
41-
# Handle classes - convert to schema representation
42-
if isinstance(obj, type) and issubclass(obj, BaseModel):
43-
return {
44-
"__class__": obj.__name__,
45-
"__module__": obj.__module__,
46-
"schema": obj.model_json_schema(),
47-
}
48-
if hasattr(obj, "dict") and not isinstance(obj, type):
49-
return obj.dict()
50-
if hasattr(obj, "to_dict") and not isinstance(obj, type):
51-
return obj.to_dict()
52-
53-
# Handle dataclasses
54-
if is_dataclass(obj) and not isinstance(obj, type):
55-
return asdict(obj)
56-
57-
# Handle enums
58-
if isinstance(obj, Enum):
59-
return _simple_serialize_defaults(obj.value)
60-
61-
if isinstance(obj, (set, tuple)):
62-
if hasattr(obj, "_asdict") and callable(obj._asdict): # pyright: ignore[reportAttributeAccessIssue]
63-
return cast(dict[str, Any], obj._asdict()) # pyright: ignore[reportAttributeAccessIssue]
64-
return list(obj)
65-
66-
if isinstance(obj, datetime):
67-
return obj.isoformat()
68-
69-
if isinstance(obj, (timezone, ZoneInfo)):
70-
return obj.tzname(None)
71-
72-
# Allow JSON-serializable primitives to pass through unchanged
73-
if obj is None or isinstance(obj, (bool, int, float, str)):
74-
return obj
75-
76-
return str(obj)
77-
78-
7931
def format_args_for_trace_json(
8032
signature: inspect.Signature, *args: Any, **kwargs: Any
8133
) -> str:
8234
"""Return a JSON string of inputs from the function signature."""
8335
result = format_args_for_trace(signature, *args, **kwargs)
84-
return json.dumps(result, default=_simple_serialize_defaults)
36+
return serialize_json(result)
8537

8638

8739
def format_object_for_trace_json(
8840
input_object: Any,
8941
) -> str:
9042
"""Return a JSON string of inputs from the function signature."""
91-
return json.dumps(input_object, default=_simple_serialize_defaults)
43+
return serialize_json(input_object)
9244

9345

9446
def format_args_for_trace(

0 commit comments

Comments
 (0)