Skip to content

Commit 03f29e6

Browse files
authored
Merge pull request #1264 from compas-dev/private-data-api
Make serialisation infrastructure private
2 parents 90cfd64 + 69e672d commit 03f29e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1204
-1269
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
* Merged `compas.datastructures.Network` into `compas.datastructures.Graph`.
1919
* Merged `compas.datastructures.Halfface` into `compas.datastructures.VolMesh`.
2020
* Fixed `RhinoBrep` doesn't get capped after trimming.
21+
* Changed `compas.data.Data.data` to `compas.data.Data.__data__`.
22+
* Changed `compas.data.Data.dtype` to `compas.data.Data.__dtype__`.
23+
* Changed `compas.data.Data.from_data` to `compas.data.Data.__from_data__`.
2124

2225
### Removed
2326

@@ -32,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3235
* Removed `attributes` from `compas.datastructures.Mesh.data`.
3336
* Removed `attributes` from `compas.datastructures.Tree.data`.
3437
* Removed `attributes` from `compas.datastructures.VolMesh.data`.
38+
* Removed `compas.data.Data.to_data`.
3539
* Removed `compas.rpc.XFunc`.
3640

3741
## [2.0.0-beta.2] 2024-01-12

src/compas/colors/color.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class Color(Data):
4040
Transparency setting.
4141
If ``alpha = 0.0``, the color is fully transparent.
4242
If ``alpha = 1.0``, the color is fully opaque.
43+
name : str, optional
44+
The name of the color.
4345
4446
Attributes
4547
----------
@@ -152,8 +154,12 @@ class Color(Data):
152154
"required": ["red", "green", "blue", "alpha"],
153155
}
154156

155-
def __init__(self, red, green, blue, alpha=1.0, **kwargs):
156-
super(Color, self).__init__(**kwargs)
157+
@property
158+
def __data__(self):
159+
return {"red": self.r, "green": self.g, "blue": self.b, "alpha": self.a}
160+
161+
def __init__(self, red, green, blue, alpha=1.0, name=None):
162+
super(Color, self).__init__(name=name)
157163
self._r = 1.0
158164
self._g = 1.0
159165
self._b = 1.0
@@ -184,14 +190,6 @@ def __iter__(self):
184190
def __eq__(self, other):
185191
return all(a == b for a, b in zip(self, other))
186192

187-
# --------------------------------------------------------------------------
188-
# Data
189-
# --------------------------------------------------------------------------
190-
191-
@property
192-
def data(self):
193-
return {"red": self.r, "green": self.g, "blue": self.b, "alpha": self.a}
194-
195193
# --------------------------------------------------------------------------
196194
# Properties
197195
# --------------------------------------------------------------------------

src/compas/colors/colordict.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class ColorDict(Data):
1313
----------
1414
default : :class:`compas.colors.Color`
1515
The default color to use if the requested key is not in the dictionary.
16+
name : str, optional
17+
The name of the color dictionary.
1618
1719
Attributes
1820
----------
@@ -21,8 +23,12 @@ class ColorDict(Data):
2123
2224
"""
2325

24-
def __init__(self, default, **kwargs):
25-
super(ColorDict, self).__init__(**kwargs)
26+
@property
27+
def __data__(self):
28+
return {"default": self.default.__data__, "dict": self._dict}
29+
30+
def __init__(self, default, name=None):
31+
super(ColorDict, self).__init__(name=name)
2632
self._default = None
2733
self.default = default
2834
self._dict = {}
@@ -57,10 +63,6 @@ def __len__(self):
5763
def __contains__(self, key):
5864
return key in self._dict
5965

60-
@property
61-
def data(self):
62-
return {"default": self.default.data, "dict": self._dict}
63-
6466
def items(self):
6567
return self._dict.items()
6668

src/compas/data/data.py

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ def __init__(self, name=None):
7272
if name:
7373
self.name = name
7474

75+
@property
76+
def __dtype__(self):
77+
return "{}/{}".format(".".join(self.__class__.__module__.split(".")[:2]), self.__class__.__name__)
78+
79+
@property
80+
def __data__(self):
81+
raise NotImplementedError
82+
7583
def __jsondump__(self, minimal=False):
7684
"""Return the required information for serialization with the COMPAS JSON serializer.
7785
@@ -86,8 +94,8 @@ def __jsondump__(self, minimal=False):
8694
8795
"""
8896
state = {
89-
"dtype": self.dtype,
90-
"data": self.data,
97+
"dtype": self.__dtype__,
98+
"data": self.__data__,
9199
}
92100
if minimal:
93101
return state
@@ -114,7 +122,7 @@ def __jsonload__(cls, data, guid=None, name=None):
114122
object
115123
116124
"""
117-
obj = cls.from_data(data)
125+
obj = cls.__from_data__(data)
118126
if guid is not None:
119127
obj._guid = UUID(guid)
120128
if name is not None:
@@ -133,13 +141,22 @@ def __setstate__(self, state):
133141
if "name" in state:
134142
self.name = state["name"]
135143

136-
@property
137-
def dtype(self):
138-
return "{}/{}".format(".".join(self.__class__.__module__.split(".")[:2]), self.__class__.__name__)
144+
@classmethod
145+
def __from_data__(cls, data): # type: (dict) -> Data
146+
"""Construct an object of this type from the provided data.
139147
140-
@property
141-
def data(self):
142-
raise NotImplementedError
148+
Parameters
149+
----------
150+
data : dict
151+
The data dictionary.
152+
153+
Returns
154+
-------
155+
:class:`compas.data.Data`
156+
An instance of this object type if the data contained in the dict has the correct schema.
157+
158+
"""
159+
return cls(**data)
143160

144161
def ToString(self):
145162
"""Converts the instance to a string.
@@ -169,34 +186,6 @@ def name(self):
169186
def name(self, name):
170187
self._name = name
171188

172-
@classmethod
173-
def from_data(cls, data): # type: (dict) -> Data
174-
"""Construct an object of this type from the provided data.
175-
176-
Parameters
177-
----------
178-
data : dict
179-
The data dictionary.
180-
181-
Returns
182-
-------
183-
:class:`compas.data.Data`
184-
An instance of this object type if the data contained in the dict has the correct schema.
185-
186-
"""
187-
return cls(**data)
188-
189-
def to_data(self):
190-
"""Convert an object to its native data representation.
191-
192-
Returns
193-
-------
194-
dict
195-
The data representation of the object as described by the schema.
196-
197-
"""
198-
return self.data
199-
200189
@classmethod
201190
def from_json(cls, filepath): # type: (...) -> Data
202191
"""Construct an object of this type from a JSON file.
@@ -295,7 +284,7 @@ def copy(self, cls=None): # type: (...) -> D
295284
"""
296285
if not cls:
297286
cls = type(self)
298-
obj = cls.from_data(deepcopy(self.data))
287+
obj = cls.__from_data__(deepcopy(self.__data__))
299288
obj.name = self.name
300289
return obj # type: ignore
301290

@@ -335,7 +324,7 @@ def sha256(self, as_string=False):
335324
def validate_data(cls, data):
336325
"""Validate the data against the object's data schema.
337326
338-
The data is the raw data that can be used to construct an object of this type with the classmethod ``from_data``.
327+
The data is the raw data that can be used to construct an object of this type with the classmethod ``__from_data__``.
339328
340329
Parameters
341330
----------

src/compas/data/encoders.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class DataEncoder(json.JSONEncoder):
7272
* iterables to lists; and
7373
* :class:`compas.data.Data` objects,
7474
such as geometric primitives and shapes, data structures, robots, ...,
75-
to a dict with the following structure: ``{'dtype': o.dtype, 'value': o.data}``
75+
to a dict with the following structure: ``{'dtype': o.__dtype__, 'data': o.__data__}``
7676
7777
See Also
7878
--------
@@ -167,9 +167,9 @@ class DataDecoder(json.JSONDecoder):
167167
168168
The reconstruction is possible if
169169
170-
* the serialized data has the following structure: ``{"dtype": "...", 'value': {...}}``;
170+
* the serialized data has the following structure: ``{"dtype": "...", 'data': {...}}``;
171171
* a class can be imported into the current scope from the info in ``o["dtype"]``; and
172-
* the imported class has a method ``from_data``.
172+
* the imported class has a method ``__from_data__``.
173173
174174
See Also
175175
--------
@@ -233,7 +233,7 @@ def object_hook(self, o):
233233
guid = o.get("guid")
234234
name = o.get("name")
235235

236-
# Kick-off from_data from a rebuilt Python dictionary instead of the C# data type
236+
# Kick-off __from_data__ from a rebuilt Python dictionary instead of the C# data type
237237
if IDictionary and isinstance(o, IDictionary[str, object]):
238238
data = {key: data[key] for key in data.Keys}
239239

src/compas/datastructures/assembly/assembly.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class Assembly(Datastructure):
1414
----------
1515
name : str, optional
1616
The name of the assembly.
17+
**kwargs : dict, optional
18+
Additional keyword arguments, which are stored in the attributes dict.
1719
1820
Attributes
1921
----------
@@ -32,36 +34,35 @@ class Assembly(Datastructure):
3234
"type": "object",
3335
"properties": {
3436
"graph": Graph.DATASCHEMA,
37+
"attributes": {"type": "object"},
3538
},
36-
"required": ["graph"],
39+
"required": ["graph", "attributes"],
3740
}
3841

39-
def __init__(self, name=None):
40-
super(Assembly, self).__init__(name=name)
41-
self.graph = Graph()
42-
self._parts = {}
43-
44-
def __str__(self):
45-
tpl = "<Assembly with {} parts and {} connections>"
46-
return tpl.format(self.graph.number_of_nodes(), self.graph.number_of_edges())
47-
48-
# ==========================================================================
49-
# Data
50-
# ==========================================================================
51-
5242
@property
53-
def data(self):
43+
def __data__(self):
5444
return {
55-
"graph": self.graph.data,
45+
"graph": self.graph.__data__,
46+
"attributes": self.attributes,
5647
}
5748

5849
@classmethod
59-
def from_data(cls, data):
50+
def __from_data__(cls, data):
6051
assembly = cls()
61-
assembly.graph = Graph.from_data(data["graph"])
52+
assembly.attributes.update(data["attributes"] or {})
53+
assembly.graph = Graph.__from_data__(data["graph"])
6254
assembly._parts = {part.guid: part.key for part in assembly.parts()} # type: ignore
6355
return assembly
6456

57+
def __init__(self, name=None, **kwargs):
58+
super(Assembly, self).__init__(kwargs, name=name)
59+
self.graph = Graph()
60+
self._parts = {}
61+
62+
def __str__(self):
63+
tpl = "<Assembly with {} parts and {} connections>"
64+
return tpl.format(self.graph.number_of_nodes(), self.graph.number_of_edges())
65+
6566
# ==========================================================================
6667
# Constructors
6768
# ==========================================================================

src/compas/datastructures/assembly/part.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ def __init__(self, *args, **kwargs):
6565
self._geometry = None
6666

6767
@property
68-
def data(self):
68+
def __data__(self):
6969
return {"geometry": self._geometry}
7070

7171
@classmethod
72-
def from_data(cls, data):
72+
def __from_data__(cls, data):
7373
feature = cls()
7474
feature._geometry = data["geometry"] # this will work but is not consistent with validation
7575
return feature
@@ -163,32 +163,32 @@ class Part(Datastructure):
163163
"required": ["key", "frame"],
164164
}
165165

166-
def __init__(self, name=None, frame=None, **kwargs):
167-
super(Part, self).__init__()
168-
self.attributes = {"name": name or "Part"}
169-
self.attributes.update(kwargs)
170-
self.key = None
171-
self.frame = frame or Frame.worldXY()
172-
self.features = []
173-
174166
@property
175-
def data(self):
167+
def __data__(self):
176168
return {
177169
"attributes": self.attributes,
178170
"key": self.key,
179-
"frame": self.frame.data,
171+
"frame": self.frame.__data__,
180172
"features": self.features,
181173
}
182174

183175
@classmethod
184-
def from_data(cls, data):
176+
def __from_data__(cls, data):
185177
part = cls()
186178
part.attributes.update(data["attributes"] or {})
187179
part.key = data["key"]
188-
part.frame = Frame.from_data(data["frame"])
180+
part.frame = Frame.__from_data__(data["frame"])
189181
part.features = data["features"] or []
190182
return part
191183

184+
def __init__(self, name=None, frame=None, **kwargs):
185+
super(Part, self).__init__()
186+
self.attributes = {"name": name or "Part"}
187+
self.attributes.update(kwargs)
188+
self.key = None
189+
self.frame = frame or Frame.worldXY()
190+
self.features = []
191+
192192
def get_geometry(self, with_features=False):
193193
"""
194194
Returns a transformed copy of the part's geometry.

0 commit comments

Comments
 (0)