Skip to content

Commit 4f3d906

Browse files
author
Vasileios Karakasis
authored
Merge pull request #2059 from jjotero/bugfix/json-tuples-as-keys
[bugfix] Fix `TypeError` in logger when using tuples as dict keys
2 parents 6c5f3fc + a80f4fc commit 4f3d906

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

reframe/utility/jsonext.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inspect
77
import json
88
import traceback
9+
from collections.abc import MutableMapping
910

1011
import reframe.utility as util
1112

@@ -17,10 +18,30 @@ def __rfm_json_encode__(self):
1718
'__rfm_file__': inspect.getfile(type(self))
1819
}
1920
ret.update(self.__dict__)
20-
return ret
21+
encoded_ret = encode_dict(ret, recursive=True)
22+
return encoded_ret if encoded_ret else ret
2123

2224

23-
def encode(obj):
25+
def encode_dict(obj, *, recursive=False):
26+
'''Transform tuple dict keys into strings.
27+
28+
Use the recursive option to also check the keys in nested dicts.
29+
'''
30+
# FIXME: Need to add support for a decode_dict functionality
31+
if isinstance(obj, MutableMapping):
32+
if recursive or any(isinstance(k, tuple) for k in obj):
33+
newobj = type(obj)()
34+
for k, v in obj.items():
35+
_key = str(k) if isinstance(k, tuple) else k
36+
_v = encode_dict(v, recursive=recursive)
37+
newobj[_key] = _v if _v is not None else v
38+
39+
return newobj
40+
41+
return None
42+
43+
44+
def encode(obj, **kwargs):
2445
if hasattr(obj, '__rfm_json_encode__'):
2546
return obj.__rfm_json_encode__()
2647

@@ -37,17 +58,27 @@ def encode(obj):
3758
if inspect.istraceback(obj):
3859
return traceback.format_tb(obj)
3960

61+
newobj = encode_dict(obj)
62+
if newobj is not None:
63+
return newobj
64+
4065
return None
4166

4267

4368
def dump(obj, fp, **kwargs):
4469
kwargs.setdefault('default', encode)
45-
return json.dump(obj, fp, **kwargs)
70+
try:
71+
return json.dump(obj, fp, **kwargs)
72+
except TypeError:
73+
return json.dump(encode(obj), fp, **kwargs)
4674

4775

4876
def dumps(obj, **kwargs):
4977
kwargs.setdefault('default', encode)
50-
return json.dumps(obj, **kwargs)
78+
try:
79+
return json.dumps(obj, **kwargs)
80+
except TypeError:
81+
return json.dumps(encode(obj), **kwargs)
5182

5283

5384
def _object_hook(json):

unittests/test_utility.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ def test_jsonext_dumps():
15401540
assert '{"foo":["bar"]}' == jsonext.dumps(
15411541
{'foo': sn.defer(['bar']).evaluate()}, separators=(',', ':')
15421542
)
1543-
1543+
assert '{"(1, 2, 3)": 1}' == jsonext.dumps({(1, 2, 3): 1})
15441544

15451545
# Classes to test JSON deserialization
15461546

@@ -1567,6 +1567,9 @@ def __init__(self, x, y):
15671567
self.z = None
15681568
self.w = {1, 2}
15691569

1570+
# Dump dict with tuples as keys
1571+
self.v = {(1, 2): 1}
1572+
15701573
def __rfm_json_decode__(self, json):
15711574
# Sets are converted to lists when encoding, we need to manually
15721575
# change them back to sets

0 commit comments

Comments
 (0)