Skip to content

Commit 90f0a1d

Browse files
committed
Fix minor serialization bugs
1 parent 54df335 commit 90f0a1d

File tree

4 files changed

+49
-9
lines changed

4 files changed

+49
-9
lines changed

gemd/json/gemd_encoder.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from json import JSONEncoder
2+
from uuid import UUID
23

34
from gemd.entity.dict_serializable import DictSerializable
45
from gemd.enumeration.base_enumeration import BaseEnumeration
@@ -13,5 +14,7 @@ def default(self, o):
1314
return o.as_dict()
1415
elif isinstance(o, BaseEnumeration):
1516
return o.value
17+
elif isinstance(o, UUID):
18+
return str(o)
1619
else:
1720
return JSONEncoder.default(self, o)

gemd/json/gemd_json.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22
from deprecation import deprecated
3+
from typing import Dict, Any, Type
34

45
from gemd.entity.dict_serializable import DictSerializable
56
from gemd.entity.base_entity import BaseEntity
@@ -21,7 +22,7 @@ class GEMDJson(object):
2122

2223
def __init__(self, scope: str = 'auto'):
2324
self._scope = scope
24-
self._clazz_index = DictSerializable.class_mapping
25+
self._clazz_index = dict()
2526

2627
@property
2728
def scope(self) -> str:
@@ -74,8 +75,15 @@ def loads(self, json_str: str, **kwargs):
7475
# Create an index to hold the objects by their uid reference
7576
# so we can replace links with pointers
7677
index = {}
78+
clazz_index = DictSerializable.class_mapping
79+
clazz_index.update(self._clazz_index)
7780
raw = json_builtin.loads(
78-
json_str, object_hook=lambda x: self._load_and_index(x, index, True), **kwargs)
81+
json_str,
82+
object_hook=lambda x: self._load_and_index(x,
83+
index,
84+
clazz_index=clazz_index,
85+
substitute=True),
86+
**kwargs)
7987
# the return value is in the 2nd position.
8088
return raw["object"]
8189

@@ -196,8 +204,12 @@ def raw_loads(self, json_str, **kwargs):
196204
# Create an index to hold the objects by their uid reference
197205
# so we can replace links with pointers
198206
index = {}
207+
clazz_index = DictSerializable.class_mapping
208+
clazz_index.update(self._clazz_index)
199209
return json_builtin.loads(
200-
json_str, object_hook=lambda x: self._load_and_index(x, index), **kwargs)
210+
json_str,
211+
object_hook=lambda x: self._load_and_index(x, index, clazz_index=clazz_index),
212+
**kwargs)
201213

202214
@deprecated(deprecated_in="1.13.0", removed_in="2.0.0",
203215
details="Classes are now automatically registered when extending BaseEntity")
@@ -229,7 +241,12 @@ def register_classes(self, classes):
229241

230242
self._clazz_index.update(classes)
231243

232-
def _load_and_index(self, d, object_index, substitute=False):
244+
@staticmethod
245+
def _load_and_index(
246+
d: Dict[str, Any],
247+
object_index: Dict[str, DictSerializable],
248+
clazz_index: Dict[str, Type],
249+
substitute: bool = False) -> DictSerializable:
233250
"""
234251
Load the class based on the type string and index it, if a BaseEntity.
235252
@@ -254,10 +271,10 @@ def _load_and_index(self, d, object_index, substitute=False):
254271
return d
255272
typ = d.pop("type")
256273

257-
if typ not in self._clazz_index:
274+
if typ not in clazz_index:
258275
raise TypeError("Unexpected base object type: {}".format(typ))
259276

260-
clz = self._clazz_index[typ]
277+
clz = clazz_index[typ]
261278
obj = clz.from_dict(d)
262279

263280
if isinstance(obj, BaseEntity): # Add it to the object index

gemd/json/tests/test_json.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Test serialization and deserialization of gemd objects."""
22
import json
33
from copy import deepcopy
4+
from uuid import uuid4
45

56
import pytest
67

@@ -10,6 +11,7 @@
1011
from gemd.entity.case_insensitive_dict import CaseInsensitiveDict
1112
from gemd.entity.attribute.condition import Condition
1213
from gemd.entity.attribute.parameter import Parameter
14+
from gemd.entity.dict_serializable import DictSerializable
1315
from gemd.entity.link_by_uid import LinkByUID
1416
from gemd.entity.object import MeasurementRun, MaterialRun, ProcessRun
1517
from gemd.entity.object import MeasurementSpec, MaterialSpec, ProcessSpec
@@ -51,14 +53,27 @@ def test_deserialize():
5153
"""Round-trip serde should leave the object unchanged."""
5254
condition = Condition(name="A condition", value=NominalReal(7, ''))
5355
parameter = Parameter(name="A parameter", value=NormalReal(mean=17, std=1, units=''))
54-
measurement = MeasurementRun("name", tags="A tag on a measurement", conditions=condition,
56+
measurement = MeasurementRun("name",
57+
tags="A tag on a measurement",
58+
conditions=condition,
5559
parameters=parameter)
5660
copy_meas = GEMDJson().copy(measurement)
5761
assert(copy_meas.conditions[0].value == measurement.conditions[0].value)
5862
assert(copy_meas.parameters[0].value == measurement.parameters[0].value)
5963
assert(copy_meas.uids["auto"] == measurement.uids["auto"])
6064

6165

66+
def test_uuid_serde():
67+
"""Any UUIDs in uids & LinkByUIDs shouldn't break stuff."""
68+
process = ProcessSpec(name="A process", uids={"uuid": uuid4(), "word": "turnbuckle"})
69+
copy_proc = GEMDJson().copy(process)
70+
assert all(copy_proc.uids[scope] == str(process.uids.get(scope)) for scope in copy_proc.uids)
71+
assert len(copy_proc.uids) == len(process.uids)
72+
73+
link = LinkByUID(id=uuid4(), scope="mine")
74+
assert GEMDJson().copy(link).id == str(link.id)
75+
76+
6277
def test_scope_control():
6378
"""Serializing a nested object should be identical to individually serializing each piece."""
6479
input_material = MaterialSpec("input_material")
@@ -232,7 +247,12 @@ def test_pure_substitutions():
232247
]
233248
'''
234249
index = {}
235-
original = json.loads(json_str, object_hook=lambda x: GEMDJson()._load_and_index(x, index))
250+
clazz_index = DictSerializable.class_mapping
251+
original = json.loads(json_str,
252+
object_hook=lambda x: GEMDJson()._load_and_index(x,
253+
index,
254+
clazz_index)
255+
)
236256
frozen = deepcopy(original)
237257
loaded = substitute_objects(original, index)
238258
assert original == frozen

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
setup(name='gemd',
5-
version='1.13.1',
5+
version='1.13.2',
66
url='http://github.com/CitrineInformatics/gemd-python',
77
description="Python binding for Citrine's GEMD data model",
88
author='Citrine Informatics',

0 commit comments

Comments
 (0)