Skip to content

Commit

Permalink
feat: Added expand flag to stringify_unsupported() to expand seri…
Browse files Browse the repository at this point in the history
…es (#1862)

Co-authored-by: Sabine Ståhlberg <[email protected]>
  • Loading branch information
SiddhantSadangi and normandy7 authored Oct 31, 2024
1 parent 7da7535 commit 0dc2ea0
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## neptune 1.13.0

### Features
- Added optional `expand` flag to `stringify_unsupported()` to expand series ([#1862](https://github.com/neptune-ai/neptune-client/pull/1862))
- Added optional `include_plotlyjs` keyword-only parameter to `upload()` and `File.as_html()` methods to fetch Plotly.js library from CDN ([#1881](https://github.com/neptune-ai/neptune-client/pull/1876))

## neptune 1.12.0
Expand Down
20 changes: 16 additions & 4 deletions src/neptune/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,38 @@
logger = get_logger()


def stringify_unsupported(value: Any) -> Union[StringifyValue, Mapping]:
def stringify_unsupported(
value: Any,
expand: bool = False,
) -> Union[StringifyValue, Mapping]:
"""Helper function that converts unsupported values in a collection or dictionary to strings.
Args:
value (Any): A dictionary with values or a collection
expand (bool, optional): If True, the function expands series to store each item as an
enumerated key-value pair. Otherwise, the entire series is logged as a string. Defaults to False.
Example:
>>> import neptune
>>> run = neptune.init_run()
>>> complex_dict = {"tuple": ("hi", 1), "metric": 0.87}
>>> run["complex_dict"] = complex_dict
>>> # (as of 1.0.0) error - tuple is not a supported type
... from neptune.utils import stringify_unsupported
>>> from neptune.utils import stringify_unsupported
>>> run["complex_dict"] = stringify_unsupported(complex_dict)
>>> run["complex_dict"].fetch()
>>> # {'metric': 0.87, 'tuple': "('hi', 1)"} - tuple logged as string
>>> run["complex_dict_expanded"] = stringify_unsupported(complex_dict, expand=True)
>>> run["complex_dict_expanded"].fetch()
>>> # {'metric': 0.87, 'tuple': {'0': 'hi', '1': 1} - tuple logged as an enumerated dictionary
For more information, see:
https://docs.neptune.ai/setup/neptune-client_1-0_release_changes/#no-more-implicit-casting-to-string
https://docs.neptune.ai/api/utils/#stringify_unsupported
"""
if isinstance(value, MutableMapping):
return {str(k): stringify_unsupported(v) for k, v in value.items()}
return {str(k): stringify_unsupported(v, expand=expand) for k, v in value.items()}
if expand and isinstance(value, (list, tuple, set)):
return {str(i): stringify_unsupported(v, expand=True) for i, v in enumerate(value)}

return StringifyValue(value=value)

Expand Down
79 changes: 69 additions & 10 deletions tests/unit/neptune/new/test_stringify_unsupported.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,29 +322,50 @@ def test_assign__tuple_inside_dict(self, run):

def test_assign__dict(self, run):
with assert_unsupported_warning():
run["unsupported"] = {"a": Obj(), "b": "Test", "c": 25, "d": 1997, "e": {"f": Boolean(True)}}
run["unsupported"] = {
"a": Obj(),
"b": "Test",
"c": 25,
"d": 1997,
"e": {"f": Boolean(True)},
}

with assert_no_warnings():
run["stringified"] = stringify_unsupported(
{"a": Obj(), "b": "Test", "c": 25, "d": 1997, "e": {"f": Boolean(True)}}
)

with assert_no_warnings():
run["regular"] = {"a": str(Obj()), "b": "Test", "c": 25, "d": 1997, "e": {"f": Boolean(True)}}
run["regular"] = {
"a": str(Obj()),
"b": "Test",
"c": 25,
"d": 1997,
"e": {"f": Boolean(True)},
}

assert run["regular"].fetch() == run["stringified"].fetch()

def test_assign__mutable_mapping(self, run):
with assert_no_warnings():

run["stringified_mutable_mapping"] = stringify_unsupported(
CustomMutableMapping({5: None, "b": None, "c": (None, Obj())})
)

def test_assign__dict__reassign(self, run):
with assert_unsupported_warning():
run["unsupported"] = {"a": Obj(), "b": "Test", "c": 25, "d": 1997, "e": {"f": Boolean(True)}}
run["unsupported"] = {"a": Obj(name="B"), "d": 12, "e": {"f": Boolean(False)}}
run["unsupported"] = {
"a": Obj(),
"b": "Test",
"c": 25,
"d": 1997,
"e": {"f": Boolean(True)},
}
run["unsupported"] = {
"a": Obj(name="B"),
"d": 12,
"e": {"f": Boolean(False)},
}

with assert_no_warnings():
run["stringified"] = stringify_unsupported(
Expand All @@ -353,8 +374,18 @@ def test_assign__dict__reassign(self, run):
run["stringified"] = stringify_unsupported({"a": Obj(name="B"), "d": 12, "e": {"f": Boolean(False)}})

with assert_no_warnings():
run["regular"] = {"a": str(Obj()), "b": "Test", "c": 25, "d": 1997, "e": {"f": Boolean(True)}}
run["regular"] = {"a": str(Obj(name="B")), "d": 12, "e": {"f": Boolean(False)}}
run["regular"] = {
"a": str(Obj()),
"b": "Test",
"c": 25,
"d": 1997,
"e": {"f": Boolean(True)},
}
run["regular"] = {
"a": str(Obj(name="B")),
"d": 12,
"e": {"f": Boolean(False)},
}

assert run["regular"].fetch() == run["stringified"].fetch()

Expand Down Expand Up @@ -461,16 +492,29 @@ def test_extend__float(self, run):

def test_extend__dict(self, run):
with assert_unsupported_warning():
run["unsupported"].extend({"zz": [1.0, 2.0, 3.0, 4.0, 5.0], "bb": [Obj(), Obj(), Obj(), Obj(), Obj()]})
run["unsupported"].extend(
{
"zz": [1.0, 2.0, 3.0, 4.0, 5.0],
"bb": [Obj(), Obj(), Obj(), Obj(), Obj()],
}
)

with assert_no_warnings():
run["stringified"].extend(
stringify_unsupported({"zz": [1.0, 2.0, 3.0, 4.0, 5.0], "bb": [Obj(), Obj(), Obj(), Obj(), Obj()]})
stringify_unsupported(
{
"zz": [1.0, 2.0, 3.0, 4.0, 5.0],
"bb": [Obj(), Obj(), Obj(), Obj(), Obj()],
}
)
)

with assert_no_warnings():
run["regular"].extend(
{"zz": [1.0, 2.0, 3.0, 4.0, 5.0], "bb": [str(Obj()), str(Obj()), str(Obj()), str(Obj()), str(Obj())]}
{
"zz": [1.0, 2.0, 3.0, 4.0, 5.0],
"bb": [str(Obj()), str(Obj()), str(Obj()), str(Obj()), str(Obj())],
}
)

assert run["regular/zz"].fetch_values().equals(run["stringified/zz"].fetch_values())
Expand Down Expand Up @@ -544,3 +588,18 @@ def test_stringifying_unsupported_floats(self, run):
assert run["neg_infinity"].fetch() == "-inf"

assert math.isnan(float(run["nan"].fetch()))

def test_stringify_unsupported_expand(self, run):
data = {
"a": [1, 2, 3],
"b": {"c": 4, "d": 5},
"e": [{"f": (6, 7)}, {"g": 8}],
}

with assert_no_warnings():
run["data_expanded"] = stringify_unsupported(data, expand=True)

assert run["data_expanded/a/0"].fetch() == 1
assert run["data_expanded/b/c"].fetch() == 4
assert run["data_expanded/e/0/f/0"].fetch() == 6
assert run["data_expanded/e/1/g"].fetch() == 8

0 comments on commit 0dc2ea0

Please sign in to comment.