Compare commits

..

1 Commits

Author SHA1 Message Date
izzy lyseggen 9f5631cd90 feat(objects): mesh transform helper 2021-12-13 12:37:53 +00:00
11 changed files with 65 additions and 138 deletions
+4 -16
View File
@@ -1,10 +1,6 @@
import re
from warnings import warn
from specklepy.logging.exceptions import (
GraphQLException,
SpeckleException,
SpeckleWarning,
)
from gql.client import SyncClientSession
from specklepy.logging.exceptions import SpeckleException
from typing import Dict
from specklepy.api import resources
@@ -18,8 +14,9 @@ from specklepy.api.resources import (
subscriptions,
)
from specklepy.api.models import ServerInfo
from gql import Client
from gql import Client, gql
from gql.transport.requests import RequestsHTTPTransport
from gql.transport.aiohttp import AIOHTTPTransport
from gql.transport.websockets import WebsocketsTransport
@@ -80,8 +77,6 @@ class SpeckleClient:
# Check compatibility with the server
try:
serverInfo = self.server.get()
if isinstance(serverInfo, Exception):
raise serverInfo
if not isinstance(serverInfo, ServerInfo):
raise Exception("Couldn't get ServerInfo")
except Exception as ex:
@@ -116,13 +111,6 @@ class SpeckleClient:
self._init_resources()
if isinstance(self.user.get(), GraphQLException):
warn(
SpeckleWarning(
f"Invalid token - could not authenticate Speckle Client for server {self.url}"
)
)
def execute_query(self, query: str) -> Dict:
return self.httpclient.execute(query)
+17 -17
View File
@@ -118,8 +118,8 @@ class StreamWrapper:
commit_id: str = None
object_id: str = None
branch_name: str = None
_client: SpeckleClient = None
_account: Account = None
client: SpeckleClient = None
account: Account = None
def __repr__(self):
return f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type: {self.type} )"
@@ -179,15 +179,15 @@ class StreamWrapper:
"""
Gets an account object for this server from the local accounts db (added via Speckle Manager or a json file)
"""
if self._account:
return self._account
if self.account:
return self.account
self._account = next(
self.account = next(
(a for a in get_local_accounts() if self.host in a.serverInfo.url),
None,
)
return self._account
return self.account
def get_client(self, token: str = None) -> SpeckleClient:
"""
@@ -200,22 +200,22 @@ class StreamWrapper:
Returns:
SpeckleClient -- authenticated with a corresponding local account or the provided token
"""
if self._client and token is None:
return self._client
if self.client and token is None:
return self.client
if not self._account:
if not self.account:
self.get_account()
if not self._client:
self._client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
if not self.client:
self.client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
if self._account is None and token is None:
if self.account is None and token is None:
warn(f"No local account found for server {self.host}", SpeckleWarning)
return self._client
return self.client
self._client.authenticate(self._account.token if self._account else token)
self.client.authenticate(self.account.token if self.account else token)
return self._client
return self.client
def get_transport(self, token: str = None) -> ServerTransport:
"""
@@ -225,6 +225,6 @@ class StreamWrapper:
Returns:
ServerTransport -- constructed for this stream with a pre-authenticated client
"""
if not self._client or not self._client.me:
if not self.client or not self.client.me:
self.get_client(token)
return ServerTransport(self.stream_id, self._client)
return ServerTransport(self.stream_id, self.client)
+1 -6
View File
@@ -252,9 +252,6 @@ class Base(_RegisteringBase):
if t is None:
return value
if value is None:
return None
if t.__module__ == "typing":
origin = getattr(t, "__origin__")
t = (
@@ -313,9 +310,7 @@ class Base(_RegisteringBase):
@units.setter
def units(self, value: str):
units = get_units_from_string(value)
if units:
self._units = units
self._units = get_units_from_string(value)
def get_member_names(self) -> List[str]:
"""Get all of the property names on this object, dynamic or not"""
+9
View File
@@ -391,6 +391,15 @@ class Mesh(
area: float = None
volume: float = None
def transform_to(self, transform: "Transform") -> "Mesh":
mesh = Mesh(vertices=transform.apply_to_points_values(self.vertices))
for attr in set(self.get_serializable_attributes()) - {"vertices"}:
orig_val = getattr(self, attr, None)
if orig_val:
setattr(mesh, attr, orig_val)
return mesh
class Surface(Base, speckle_type=GEOMETRY + "Surface"):
degreeU: int = None
+1 -8
View File
@@ -1,5 +1,4 @@
from warnings import warn
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
from specklepy.logging.exceptions import SpeckleException
UNITS = ["mm", "cm", "m", "in", "ft", "yd", "mi"]
@@ -29,12 +28,6 @@ UNITS_ENCODINGS = {
def get_units_from_string(unit: str):
if not isinstance(unit, str):
warn(
f"Invalid units: expected type str but received {type(unit)} ({unit}). Skipping - no units will be set.",
SpeckleWarning,
)
return
unit = str.lower(unit)
for name, alternates in UNITS_STRINGS.items():
if unit in alternates:
@@ -1,15 +1,11 @@
import ujson
import hashlib
import re
from uuid import uuid4
from warnings import warn
from typing import Any, Dict, List, Tuple
from specklepy.objects.base import Base, DataChunk
from specklepy.logging.exceptions import (
SerializationException,
SpeckleException,
SpeckleWarning,
)
from specklepy.logging.exceptions import SerializationException, SpeckleException
from specklepy.transports.abstract_transport import AbstractTransport
import specklepy.objects.geometry
import specklepy.objects.other
@@ -21,19 +17,6 @@ def hash_obj(obj: Any) -> str:
return hashlib.sha256(ujson.dumps(obj).encode()).hexdigest()[:32]
def safe_json_loads(obj: str, obj_id=None) -> Any:
try:
return ujson.loads(obj)
except ValueError as err:
import json
warn(
f"Failed to deserialise object (id: {obj_id}). This is likely a ujson big int error - falling back to json. \nError: {err}",
SpeckleWarning,
)
return json.loads(obj)
class BaseObjectSerializer:
read_transport: AbstractTransport
write_transports: List[AbstractTransport]
@@ -77,19 +60,14 @@ class BaseObjectSerializer:
chunkable = False
detach = False
# skip props marked to be ignored with "__" or "_"
if prop.startswith(("__", "_")):
# skip nulls or props marked to be ignored with "__" or "_"
if value is None or prop.startswith(("__", "_")):
continue
# don't prepopulate id as this will mess up hashing
if prop == "id":
continue
# allow serialisation of nulls
if value is None:
object_builder[prop] = value
continue
# only bother with chunking and detaching if there is a write transport
if self.write_transports:
dynamic_chunk_match = prop.startswith("@") and re.match(
@@ -260,7 +238,7 @@ class BaseObjectSerializer:
"""
if not obj_string:
return None
obj = safe_json_loads(obj_string)
obj = ujson.loads(obj_string)
return self.recompose_base(obj=obj)
def recompose_base(self, obj: dict) -> Base:
@@ -276,7 +254,7 @@ class BaseObjectSerializer:
if not obj:
return
if isinstance(obj, str):
obj = safe_json_loads(obj)
obj = ujson.loads(obj)
if "speckle_type" in obj and obj["speckle_type"] == "reference":
obj = self.get_child(obj=obj)
@@ -314,7 +292,7 @@ class BaseObjectSerializer:
raise SpeckleException(
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
)
ref_obj = safe_json_loads(ref_obj_str, ref_hash)
ref_obj = ujson.loads(ref_obj_str)
base.__setattr__(prop, self.recompose_base(obj=ref_obj))
# 3. handle all other cases (base objects, lists, and dicts)
@@ -372,5 +350,4 @@ class BaseObjectSerializer:
raise SpeckleException(
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
)
return safe_json_loads(ref_obj_str, ref_hash)
return ujson.loads(ref_obj_str)
+4 -9
View File
@@ -92,15 +92,10 @@ class BatchSender(object):
def _bg_send_batch(self, session, batch):
object_ids = [obj[0] for obj in batch]
try:
server_has_object = session.post(
url=f"{self.server_url}/api/diff/{self.stream_id}",
data={"objects": json.dumps(object_ids)},
).json()
except Exception as ex:
raise SpeckleException(
f"Invalid credentials - cannot send objects to server {self.server_url}"
) from ex
server_has_object = session.post(
url=f"{self.server_url}/api/diff/{self.stream_id}",
data={"objects": json.dumps(object_ids)},
).json()
new_object_ids = [x for x in object_ids if not server_has_object[x]]
new_object_ids = set(new_object_ids)
-12
View File
@@ -77,18 +77,6 @@ def test_speckle_type_cannot_be_set(base: Base) -> None:
assert base.speckle_type == "Base"
def test_setting_units():
b = Base(units="foot")
assert b.units == "ft"
with pytest.raises(SpeckleException):
b.units = "big"
b.units = None # invalid args are skipped
b.units = 7
assert b.units == "ft"
def test_base_of_custom_speckle_type() -> None:
b1 = Base.of_type("BirdHouse", name="Tweety's Crib")
assert b1.speckle_type == "BirdHouse"
-31
View File
@@ -1,31 +0,0 @@
import pytest
from specklepy.api import operations
from specklepy.api.client import SpeckleClient
from specklepy.objects.base import Base
from specklepy.transports.server import ServerTransport
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
def test_invalid_authentication():
client = SpeckleClient()
with pytest.warns(SpeckleWarning):
client.authenticate("fake token")
def test_invalid_send():
client = SpeckleClient()
client.me = {"token": "fake token"}
transport = ServerTransport("3073b96e86", client)
with pytest.raises(SpeckleException):
operations.send(Base(), [transport])
def test_invalid_receive():
client = SpeckleClient()
client.me = {"token": "fake token"}
transport = ServerTransport("fake stream", client)
with pytest.raises(SpeckleException):
operations.receive("fake object", transport)
-6
View File
@@ -89,9 +89,3 @@ class TestSerialization:
deserialised = operations.deserialize(untyped)
assert deserialised == {"foo": "bar"}
def test_big_int(self):
big_int = '{"big": ' + str(2 ** 64) + "}"
deserialised = operations.deserialize(big_int)
assert deserialised == {"big": 2 ** 64}
+21 -2
View File
@@ -1,7 +1,7 @@
from typing import List
import pytest
from specklepy.api import operations
from specklepy.objects.geometry import Point, Vector
from specklepy.objects.geometry import Mesh, Point, Vector
from specklepy.objects.other import (
Transform,
BlockInstance,
@@ -43,6 +43,15 @@ def vector_value():
return [1, 1, 2]
@pytest.fixture()
def mesh():
return Mesh(
vertices=[-7, 5, 1, -8, 4, 0, -7, 3, 0, -6, 4, 0],
faces=[1, 1, 2, 3, 0],
units="feet",
)
@pytest.fixture()
def transform():
"""Translates to [1, 2, 0] and scales z by 0.5"""
@@ -129,4 +138,14 @@ def test_transform_serialisation(transform: Transform):
serialized = operations.serialize(transform)
deserialized = operations.deserialize(serialized)
assert transform.get_id() == deserialized.get_id()
assert transform.get_id() == deserialized.get_id()
def test_mesh_transform(mesh: Mesh, transform: Transform):
new_mesh = mesh.transform_to(transform)
assert mesh.vertices != new_mesh.vertices
new_mesh.vertices = mesh.vertices
assert mesh.get_id() == new_mesh.get_id()