Skip to content

Commit 3417557

Browse files
feat: BlenderObject (#423)
* adds blenderobject * exports the classes * tests added
1 parent 4ce61f4 commit 3417557

File tree

3 files changed

+281
-7
lines changed

3 files changed

+281
-7
lines changed

src/specklepy/objects/__init__.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
from .data_objects import Base, DataObject, QgisObject
1+
from .data_objects import Base, DataObject, QgisObject, BlenderObject # noqa: I001
22

3-
__all__ = [
4-
"Base",
5-
"DataObject",
6-
"QgisObject",
7-
]
3+
__all__ = ["Base", "DataObject", "QgisObject", "BlenderObject"]

src/specklepy/objects/data_objects.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33

44
from specklepy.logging.exceptions import SpeckleException
55
from specklepy.objects.base import Base
6-
from specklepy.objects.interfaces import IDataObject, IGisObject, IHasUnits
6+
from specklepy.objects.interfaces import (
7+
IBlenderObject,
8+
IDataObject,
9+
IGisObject,
10+
IHasUnits,
11+
)
712

813

914
@dataclass(kw_only=True)
@@ -79,3 +84,24 @@ def type(self, value: str):
7984
raise SpeckleException(
8085
f"'type' value should be string, received {type(value)}"
8186
)
87+
88+
89+
@dataclass(kw_only=True)
90+
class BlenderObject(
91+
DataObject, IBlenderObject, IHasUnits, speckle_type="Objects.Data.BlenderObject"
92+
):
93+
type: str
94+
_type: str = field(repr=False, init=False)
95+
96+
@property
97+
def type(self) -> str:
98+
return self._type
99+
100+
@type.setter
101+
def type(self, value: str):
102+
if isinstance(value, str):
103+
self._type = value
104+
else:
105+
raise SpeckleException(
106+
f"'type' value should be string, received {type(value)}"
107+
)

tests/objects/test_data_objects.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import pytest
2+
3+
from specklepy.core.api.operations import deserialize, serialize
4+
from specklepy.logging.exceptions import SpeckleException
5+
from specklepy.objects.base import Base
6+
from specklepy.objects.data_objects import BlenderObject, DataObject, QgisObject
7+
from specklepy.objects.interfaces import (
8+
IBlenderObject,
9+
IDataObject,
10+
IGisObject,
11+
IHasUnits,
12+
)
13+
from specklepy.objects.models.units import Units
14+
15+
16+
def test_data_object_creation():
17+
display_value = [Base()]
18+
data_obj = DataObject(
19+
name="Test Data Object",
20+
properties={"key1": "value1", "key2": 2},
21+
displayValue=display_value,
22+
)
23+
24+
assert data_obj.name == "Test Data Object"
25+
assert data_obj.properties == {"key1": "value1", "key2": 2}
26+
assert data_obj.displayValue == display_value
27+
assert data_obj.speckle_type == "Objects.Data.DataObject"
28+
29+
30+
def test_inheritance_relationships():
31+
data_obj = DataObject(
32+
name="Test Data Object",
33+
properties={"key": "value"},
34+
displayValue=[Base()],
35+
)
36+
assert isinstance(data_obj, DataObject)
37+
assert isinstance(data_obj, Base)
38+
assert isinstance(data_obj, IDataObject)
39+
40+
qgis_obj = QgisObject(
41+
name="Test QGIS Object",
42+
properties={"key": "value"},
43+
displayValue=[Base()],
44+
type="Feature",
45+
units=Units.m,
46+
)
47+
assert isinstance(qgis_obj, QgisObject)
48+
assert isinstance(qgis_obj, DataObject)
49+
assert isinstance(qgis_obj, Base)
50+
assert isinstance(qgis_obj, IDataObject)
51+
assert isinstance(qgis_obj, IGisObject)
52+
assert isinstance(qgis_obj, IHasUnits)
53+
54+
blender_obj = BlenderObject(
55+
name="Test Blender Object",
56+
properties={"key": "value"},
57+
displayValue=[Base()],
58+
type="Mesh",
59+
units=Units.m,
60+
)
61+
assert isinstance(blender_obj, BlenderObject)
62+
assert isinstance(blender_obj, DataObject)
63+
assert isinstance(blender_obj, Base)
64+
assert isinstance(blender_obj, IDataObject)
65+
assert isinstance(blender_obj, IBlenderObject)
66+
assert isinstance(blender_obj, IHasUnits)
67+
68+
69+
def test_data_object_invalid_types():
70+
data_obj = DataObject(
71+
name="Test Object",
72+
properties={"key": "value"},
73+
displayValue=[Base()],
74+
)
75+
76+
class ComplexObject:
77+
def __str__(self):
78+
raise ValueError("Can't convert to string")
79+
80+
complex_obj = ComplexObject()
81+
82+
with pytest.raises((ValueError, SpeckleException)):
83+
data_obj.name = complex_obj # should be string
84+
85+
with pytest.raises(SpeckleException):
86+
data_obj.properties = [1, 2, 3] # should be dict, not list
87+
88+
with pytest.raises(SpeckleException):
89+
data_obj.displayValue = {"key": "value"} # should be list, not dict
90+
91+
92+
def test_data_object_serialization():
93+
display_value = [Base()]
94+
data_obj = DataObject(
95+
name="Test Data Object",
96+
properties={"key1": "value1", "key2": 2},
97+
displayValue=display_value,
98+
)
99+
100+
serialized = serialize(data_obj)
101+
deserialized = deserialize(serialized)
102+
103+
assert isinstance(deserialized, DataObject)
104+
assert deserialized.name == data_obj.name
105+
assert deserialized.properties == data_obj.properties
106+
assert len(deserialized.displayValue) == len(data_obj.displayValue)
107+
assert deserialized.speckle_type == data_obj.speckle_type
108+
109+
110+
def test_qgis_object_creation():
111+
display_value = [Base()]
112+
qgis_obj = QgisObject(
113+
name="Test QGIS Object",
114+
properties={"key1": "value1"},
115+
displayValue=display_value,
116+
type="Feature",
117+
units=Units.m,
118+
)
119+
120+
assert qgis_obj.name == "Test QGIS Object"
121+
assert qgis_obj.properties == {"key1": "value1"}
122+
assert qgis_obj.displayValue == display_value
123+
assert qgis_obj.type == "Feature"
124+
assert qgis_obj.units == Units.m.value
125+
assert "Objects.Data.QgisObject" in qgis_obj.speckle_type
126+
127+
128+
def test_qgis_object_serialization():
129+
display_value = [Base()]
130+
qgis_obj = QgisObject(
131+
name="Test QGIS Object",
132+
properties={"key1": "value1"},
133+
displayValue=display_value,
134+
type="Feature",
135+
units=Units.m,
136+
)
137+
138+
serialized = serialize(qgis_obj)
139+
deserialized = deserialize(serialized)
140+
141+
assert isinstance(deserialized, QgisObject)
142+
assert deserialized.name == qgis_obj.name
143+
assert deserialized.properties == qgis_obj.properties
144+
assert len(deserialized.displayValue) == len(qgis_obj.displayValue)
145+
assert deserialized.type == qgis_obj.type
146+
assert deserialized.units == qgis_obj.units
147+
assert "Objects.Data.QgisObject" in deserialized.speckle_type
148+
149+
150+
def test_blender_object_creation():
151+
display_value = [Base()]
152+
blender_obj = BlenderObject(
153+
name="Test Blender Object",
154+
properties={"key1": "value1"},
155+
displayValue=display_value,
156+
type="Mesh",
157+
units=Units.m,
158+
)
159+
160+
assert blender_obj.name == "Test Blender Object"
161+
assert blender_obj.properties == {"key1": "value1"}
162+
assert blender_obj.displayValue == display_value
163+
assert blender_obj.type == "Mesh"
164+
assert blender_obj.units == Units.m.value
165+
assert "Objects.Data.BlenderObject" in blender_obj.speckle_type
166+
167+
168+
def test_blender_object_invalid_types():
169+
blender_obj = BlenderObject(
170+
name="Test Object",
171+
properties={"key": "value"},
172+
displayValue=[Base()],
173+
type="Mesh",
174+
units=Units.m,
175+
)
176+
177+
class ComplexObject:
178+
def __str__(self):
179+
raise ValueError("Can't convert to string")
180+
181+
complex_obj = ComplexObject()
182+
183+
with pytest.raises((ValueError, SpeckleException)):
184+
blender_obj.type = complex_obj # should be string
185+
186+
187+
def test_blender_object_serialization():
188+
display_value = [Base()]
189+
blender_obj = BlenderObject(
190+
name="Test Blender Object",
191+
properties={"key1": "value1"},
192+
displayValue=display_value,
193+
type="Mesh",
194+
units=Units.m,
195+
)
196+
197+
serialized = serialize(blender_obj)
198+
deserialized = deserialize(serialized)
199+
200+
assert isinstance(deserialized, BlenderObject)
201+
assert deserialized.name == blender_obj.name
202+
assert deserialized.properties == blender_obj.properties
203+
assert len(deserialized.displayValue) == len(blender_obj.displayValue)
204+
assert deserialized.type == blender_obj.type
205+
assert deserialized.units == blender_obj.units
206+
assert "Objects.Data.BlenderObject" in deserialized.speckle_type
207+
208+
209+
def test_data_object_property_modification():
210+
data_obj = DataObject(
211+
name="Original Name",
212+
properties={"original": "value"},
213+
displayValue=[Base()],
214+
)
215+
216+
data_obj.name = "Updated Name"
217+
data_obj.properties = {"updated": "property"}
218+
new_display_value = [Base(), Base()]
219+
data_obj.displayValue = new_display_value
220+
221+
assert data_obj.name == "Updated Name"
222+
assert data_obj.properties == {"updated": "property"}
223+
assert data_obj.displayValue == new_display_value
224+
225+
226+
def test_qgis_object_property_modification():
227+
"""Test modification of QgisObject properties after creation."""
228+
qgis_obj = QgisObject(
229+
name="Original Name",
230+
properties={"original": "value"},
231+
displayValue=[Base()],
232+
type="OriginalType",
233+
units=Units.m,
234+
)
235+
236+
qgis_obj.type = "UpdatedType"
237+
238+
assert qgis_obj.type == "UpdatedType"
239+
240+
241+
def test_blender_object_property_modification():
242+
blender_obj = BlenderObject(
243+
name="Original Name",
244+
properties={"original": "value"},
245+
displayValue=[Base()],
246+
type="OriginalType",
247+
units=Units.m,
248+
)
249+
250+
blender_obj.type = "UpdatedType"
251+
252+
assert blender_obj.type == "UpdatedType"

0 commit comments

Comments
 (0)