Skip to content

Commit bfe7f3b

Browse files
authored
Merge branch 'main' into fixes-assembly
2 parents 838dc37 + 44d5f07 commit bfe7f3b

File tree

7 files changed

+110
-30
lines changed

7 files changed

+110
-30
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
* Added optional `path` parameter to `compas.rpc.Proxy` to allow for non-package calls.
1616
* Added Grasshopper component to call RPC functions.
1717
* Added `Mesh.to_lines` method and tests.
18+
* Added `Data.guid` to JSON serialization.
19+
* Added `Data.guid` to pickle state.
1820
* Added `Assembly.find_by_key` to locate parts by key.
1921

2022
### Changed
2123

2224
* Set `jinja >= 3.0` to dev dependencies to fix docs build error.
2325
* Fixed removing of collections for `compas_plotters`.
26+
* Fixed bug in `compas.robots.Configuration`.
2427
* Rebuild part index after deserialization in `Assembly`.
2528

2629
### Removed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
![build](https://github.com/compas-dev/compas/workflows/build/badge.svg)
44
[![GitHub - License](https://img.shields.io/github/license/compas-dev/compas.svg)](https://github.com/compas-dev/compas)
5+
[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/compas)](https://anaconda.org/conda-forge/compas)
6+
[![pip downloads](https://img.shields.io/pypi/dm/compas)](https://pypi.python.org/project/COMPAS)
57
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/COMPAS.svg)](https://pypi.python.org/project/COMPAS)
68
[![PyPI - Latest Release](https://img.shields.io/pypi/v/COMPAS.svg)](https://pypi.python.org/project/COMPAS)
79
[![Conda (channel only)](https://img.shields.io/conda/vn/conda-forge/compas)](https://anaconda.org/conda-forge/compas)

src/compas/data/data.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json
77
import hashlib
88
from uuid import uuid4
9+
from uuid import UUID
910
from copy import deepcopy
1011

1112
import compas
@@ -113,12 +114,19 @@ def __init__(self, name=None):
113114

114115
def __getstate__(self):
115116
"""Return the object data for state serialization with older pickle protocols."""
116-
return {"__dict__": self.__dict__, "dtype": self.dtype, "data": self.data}
117+
return {
118+
"__dict__": self.__dict__,
119+
"dtype": self.dtype,
120+
"data": self.data,
121+
"guid": str(self.guid),
122+
}
117123

118124
def __setstate__(self, state):
119125
"""Assign a deserialized state to the object data to support older pickle protocols."""
120126
self.__dict__.update(state["__dict__"])
121127
self.data = state["data"]
128+
if "guid" in state:
129+
self._guid = UUID(state['guid'])
122130

123131
@property
124132
def DATASCHEMA(self):

src/compas/data/encoders.py

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
import json
66
import platform
7+
import uuid
78

89
from compas.data.exceptions import DecoderError
910

1011
# We don't do this from `compas.IPY` to avoid circular imports
11-
if 'ironpython' == platform.python_implementation().lower():
12+
if "ironpython" == platform.python_implementation().lower():
1213
try:
1314
from System.Collections.Generic import IDictionary
1415
except: # noqa: E722
@@ -40,7 +41,7 @@ def cls_from_dtype(dtype):
4041
If the module doesn't contain the specified data type.
4142
4243
"""
43-
mod_name, attr_name = dtype.split('/')
44+
mod_name, attr_name = dtype.split("/")
4445
module = __import__(mod_name, fromlist=[attr_name])
4546
return getattr(module, attr_name)
4647

@@ -97,18 +98,19 @@ def default(self, o):
9798
The serialized object.
9899
99100
"""
100-
if hasattr(o, 'to_data'):
101+
if hasattr(o, "to_data"):
101102
value = o.to_data()
102-
if hasattr(o, 'dtype'):
103+
if hasattr(o, "dtype"):
103104
dtype = o.dtype
104105
else:
105-
dtype = '{}/{}'.format('.'.join(o.__class__.__module__.split('.')[:-1]), o.__class__.__name__)
106-
return {
107-
'dtype': dtype,
108-
'value': value
109-
}
106+
dtype = "{}/{}".format(
107+
".".join(o.__class__.__module__.split(".")[:-1]),
108+
o.__class__.__name__,
109+
)
110110

111-
if hasattr(o, '__next__'):
111+
return {"dtype": dtype, "value": value, "guid": str(o.guid)}
112+
113+
if hasattr(o, "__next__"):
112114
return list(o)
113115

114116
try:
@@ -118,9 +120,22 @@ def default(self, o):
118120
else:
119121
if isinstance(o, np.ndarray):
120122
return o.tolist()
121-
if isinstance(o, (np.int_, np.intc, np.intp, np.int8,
122-
np.int16, np.int32, np.int64, np.uint8,
123-
np.uint16, np.uint32, np.uint64)):
123+
if isinstance(
124+
o,
125+
(
126+
np.int_,
127+
np.intc,
128+
np.intp,
129+
np.int8,
130+
np.int16,
131+
np.int32,
132+
np.int64,
133+
np.uint8,
134+
np.uint16,
135+
np.uint32,
136+
np.uint64,
137+
),
138+
):
124139
return int(o)
125140
if isinstance(o, (np.float_, np.float16, np.float32, np.float64)):
126141
return float(o)
@@ -185,25 +200,39 @@ def object_hook(self, o):
185200
A (reconstructed), deserialized object.
186201
187202
"""
188-
if 'dtype' not in o:
203+
if "dtype" not in o:
189204
return o
190205

191206
try:
192-
cls = cls_from_dtype(o['dtype'])
207+
cls = cls_from_dtype(o["dtype"])
193208

194209
except ValueError:
195-
raise DecoderError("The data type of the object should be in the following format: '{}/{}'".format(o.__class__.__module__, o.__class__.__name__))
210+
raise DecoderError(
211+
"The data type of the object should be in the following format: '{}/{}'".format(
212+
o.__class__.__module__, o.__class__.__name__
213+
)
214+
)
196215

197216
except ImportError:
198-
raise DecoderError("The module of the data type can't be found: {}.".format(o['dtype']))
217+
raise DecoderError(
218+
"The module of the data type can't be found: {}.".format(o["dtype"])
219+
)
199220

200221
except AttributeError:
201-
raise DecoderError("The data type can't be found in the specified module: {}.".format(o['dtype']))
222+
raise DecoderError(
223+
"The data type can't be found in the specified module: {}.".format(
224+
o["dtype"]
225+
)
226+
)
202227

203-
obj_value = o['value']
228+
obj_value = o["value"]
204229

205230
# Kick-off from_data from a rebuilt Python dictionary instead of the C# data type
206231
if IDictionary and isinstance(o, IDictionary[str, object]):
207232
obj_value = {key: obj_value[key] for key in obj_value.Keys}
208233

209-
return cls.from_data(obj_value)
234+
obj = cls.from_data(obj_value)
235+
if "guid" in o:
236+
obj._guid = uuid.UUID(o["guid"])
237+
238+
return obj

src/compas/robots/configuration.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ class Configuration(Data):
134134
135135
"""
136136

137-
def __init__(self, joint_values=None, joint_types=None, joint_names=None):
137+
def __init__(self, joint_values=None, joint_types=None, joint_names=None, **kwargs):
138+
super(Configuration, self).__init__(**kwargs)
139+
138140
joint_values = FixedLengthList(joint_values or [])
139141
joint_types = FixedLengthList(joint_types or [])
140142
joint_names = FixedLengthList(joint_names or [], validator=joint_names_validator)

tests/compas/data/test_json.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@
1010

1111

1212
def test_json_native():
13-
before = [[], (), {}, '', 1, 1.0, True, None]
13+
before = [[], (), {}, "", 1, 1.0, True, None]
1414
after = compas.json_loads(compas.json_dumps(before))
15-
assert after == [[], [], {}, '', 1, 1.0, True, None]
15+
assert after == [[], [], {}, "", 1, 1.0, True, None]
1616

1717

1818
if not compas.IPY:
1919
import numpy as np
2020

2121
def test_json_numpy():
22-
before = [np.array([1, 2, 3]), np.array([1.0, 2.0, 3.0]), np.float64(1.0), np.int32(1)]
22+
before = [
23+
np.array([1, 2, 3]),
24+
np.array([1.0, 2.0, 3.0]),
25+
np.float64(1.0),
26+
np.int32(1),
27+
]
2328
after = compas.json_loads(compas.json_dumps(before))
2429
assert after == [[1, 2, 3], [1.0, 2.0, 3.0], 1.0, 1]
2530

@@ -29,20 +34,23 @@ def test_json_primitive():
2934
after = compas.json_loads(compas.json_dumps(before))
3035
assert before.dtype == after.dtype
3136
assert all(a == b for a, b in zip(before, after))
37+
assert before.guid == after.guid
3238

3339

3440
def test_json_shape():
3541
before = Box(Frame(Point(0, 0, 0), Vector(1, 0, 0), Vector(0, 1, 0)), 1, 1, 1)
3642
after = compas.json_loads(compas.json_dumps(before))
3743
assert before.dtype == after.dtype
3844
assert all(a == b for a, b in zip(before.vertices, after.vertices))
45+
assert before.guid == after.guid
3946

4047

4148
def test_json_xform():
4249
before = Transformation.from_frame_to_frame(Frame.worldXY(), Frame.worldXY())
4350
after = compas.json_loads(compas.json_dumps(before))
4451
assert before.dtype == after.dtype
4552
assert all(a == b for a, b in zip(before, after))
53+
assert before.guid == after.guid
4654

4755

4856
def test_json_network():
@@ -57,10 +65,13 @@ def test_json_network():
5765
assert all(after.has_node(node) for node in before.nodes())
5866
assert all(before.has_edge(*edge) for edge in after.edges())
5967
assert all(after.has_edge(*edge) for edge in before.edges())
68+
assert before.guid == after.guid
6069

6170

6271
def test_json_mesh():
63-
before = Mesh.from_vertices_and_faces([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], [[0, 1, 2, 3]])
72+
before = Mesh.from_vertices_and_faces(
73+
[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], [[0, 1, 2, 3]]
74+
)
6475
after = compas.json_loads(compas.json_dumps(before))
6576
assert before.dtype == after.dtype
6677
assert before.attributes == after.attributes
@@ -70,13 +81,36 @@ def test_json_mesh():
7081
assert all(after.has_face(face) for face in before.faces())
7182
assert all(before.has_edge(edge) for edge in after.edges())
7283
assert all(after.has_edge(edge) for edge in before.edges())
73-
assert all(before.face_vertices(a) == after.face_vertices(b) for a, b in zip(before.faces(), after.faces()))
84+
assert all(
85+
before.face_vertices(a) == after.face_vertices(b)
86+
for a, b in zip(before.faces(), after.faces())
87+
)
88+
assert before.guid == after.guid
7489

7590

7691
def test_json_volmesh():
7792
before = VolMesh.from_vertices_and_cells(
78-
[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]],
79-
[[[0, 1, 5, 4], [1, 2, 5, 6], [2, 3, 7, 6], [3, 0, 4, 7], [4, 5, 6, 7], [0, 3, 2, 1]]])
93+
[
94+
[0, 0, 0],
95+
[1, 0, 0],
96+
[1, 1, 0],
97+
[0, 1, 0],
98+
[0, 0, 1],
99+
[1, 0, 1],
100+
[1, 1, 1],
101+
[0, 1, 1],
102+
],
103+
[
104+
[
105+
[0, 1, 5, 4],
106+
[1, 2, 5, 6],
107+
[2, 3, 7, 6],
108+
[3, 0, 4, 7],
109+
[4, 5, 6, 7],
110+
[0, 3, 2, 1],
111+
]
112+
],
113+
)
80114
after = compas.json_loads(compas.json_dumps(before))
81115
assert before.dtype == after.dtype
82116
assert before.attributes == after.attributes
@@ -88,12 +122,13 @@ def test_json_volmesh():
88122
# assert all(after.has_cell(cell) for cell in before.cells())
89123
# assert all(before.has_edge(edge) for edge in after.edges())
90124
# assert all(after.has_edge(edge) for edge in before.edges())
125+
assert before.guid == after.guid
91126

92127

93128
def test_json_pretty():
94129
result = compas.json_dumps(dict(a=12, b=6565), pretty=True)
95130
# strip some spacing to make the test pass on ironpython
96-
result = '\n'.join([line.strip() for line in result.split('\n')])
131+
result = "\n".join([line.strip() for line in result.split("\n")])
97132
assert result == """{\n"a": 12,\n"b": 6565\n}"""
98133

99134

tests/compas/data/test_pickle.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ def test_pickling():
1010
assert all(a == b for a, b in zip(f1.xaxis, f2.xaxis))
1111
assert all(a == b for a, b in zip(f1.yaxis, f2.yaxis))
1212
assert all(a == b for a, b in zip(f1.zaxis, f2.zaxis))
13+
assert f1.guid == f2.guid

0 commit comments

Comments
 (0)