Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1c149382a | |||
| 393e98c8c2 | |||
| 8376329cbb | |||
| 1567fe9e68 | |||
| 364b826a1b | |||
| 297dbab479 |
@@ -22,7 +22,7 @@ def send(
|
||||
Returns:
|
||||
str -- the object id of the sent object
|
||||
"""
|
||||
metrics.track(metrics.RECEIVE)
|
||||
metrics.track(metrics.SEND)
|
||||
if not transports and not use_default_cache:
|
||||
raise SpeckleException(
|
||||
message="You need to provide at least one transport: cannot send with an empty transport list and no default cache"
|
||||
|
||||
+33
-20
@@ -1,6 +1,15 @@
|
||||
import typing
|
||||
from typing import (Any, Callable, ClassVar, Dict, List, Optional, Set, Type,
|
||||
get_type_hints)
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
Type,
|
||||
get_type_hints,
|
||||
)
|
||||
from warnings import warn
|
||||
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
@@ -118,14 +127,12 @@ class _RegisteringBase:
|
||||
except Exception:
|
||||
cls._attr_types = getattr(cls, "__annotations__", {})
|
||||
if chunkable:
|
||||
chunkable = {k: v for k, v in chunkable.items()
|
||||
if isinstance(v, int)}
|
||||
chunkable = {k: v for k, v in chunkable.items() if isinstance(v, int)}
|
||||
cls._chunkable = dict(cls._chunkable, **chunkable)
|
||||
if detachable:
|
||||
cls._detachable = cls._detachable.union(detachable)
|
||||
if serialize_ignore:
|
||||
cls._serialize_ignore = cls._serialize_ignore.union(
|
||||
serialize_ignore)
|
||||
cls._serialize_ignore = cls._serialize_ignore.union(serialize_ignore)
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
||||
|
||||
@@ -215,15 +222,13 @@ class Base(_RegisteringBase):
|
||||
try:
|
||||
cls._attr_types = get_type_hints(cls)
|
||||
except Exception as e:
|
||||
warn(
|
||||
f"Could not update forward refs for class {cls.__name__}: {e}")
|
||||
warn(f"Could not update forward refs for class {cls.__name__}: {e}")
|
||||
|
||||
@classmethod
|
||||
def validate_prop_name(cls, name: str) -> None:
|
||||
"""Validator for dynamic attribute names."""
|
||||
if name in {"", "@"}:
|
||||
raise ValueError(
|
||||
"Invalid Name: Base member names cannot be empty strings")
|
||||
raise ValueError("Invalid Name: Base member names cannot be empty strings")
|
||||
if name.startswith("@@"):
|
||||
raise ValueError(
|
||||
"Invalid Name: Base member names cannot start with more than one '@'",
|
||||
@@ -249,7 +254,12 @@ class Base(_RegisteringBase):
|
||||
|
||||
if t.__module__ == "typing":
|
||||
origin = getattr(t, "__origin__")
|
||||
t = t.__args__ if origin is typing.Union else origin
|
||||
t = (
|
||||
tuple(getattr(sub_t, "__origin__", sub_t) for sub_t in t.__args__)
|
||||
if origin is typing.Union
|
||||
else origin
|
||||
)
|
||||
|
||||
if not isinstance(t, (type, tuple)):
|
||||
warn(
|
||||
f"Unrecognised type '{t}' provided for attribute '{name}'. Type will not been validated."
|
||||
@@ -260,13 +270,16 @@ class Base(_RegisteringBase):
|
||||
|
||||
# to be friendly, we'll parse ints and strs into floats, but not the other way around
|
||||
# (to avoid unexpected rounding)
|
||||
if t is float and isinstance(value, (int, str, float)):
|
||||
try:
|
||||
if isinstance(t, tuple):
|
||||
t = t[0]
|
||||
|
||||
try:
|
||||
if t is float:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if t is str and value is not None:
|
||||
return str(value)
|
||||
if t is str and value:
|
||||
return str(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
raise SpeckleException(
|
||||
f"Cannot set '{self.__class__.__name__}.{name}': it expects type '{t.__name__}', but received type '{type(value).__name__}'"
|
||||
@@ -327,7 +340,8 @@ class Base(_RegisteringBase):
|
||||
|
||||
def get_id(self, decompose: bool = False) -> str:
|
||||
"""
|
||||
Gets the id (a unique hash) of this object. ⚠️ This method fully serializes the object, which in the case of large objects (with many sub-objects), has a tangible cost. Avoid using it!
|
||||
Gets the id (a unique hash) of this object. ⚠️ This method fully serializes the object which,
|
||||
in the case of large objects (with many sub-objects), has a tangible cost. Avoid using it!
|
||||
|
||||
Note: the hash of a decomposed object differs from that of a non-decomposed object
|
||||
|
||||
@@ -337,8 +351,7 @@ class Base(_RegisteringBase):
|
||||
Returns:
|
||||
str -- the hash (id) of the fully serialized object
|
||||
"""
|
||||
from specklepy.serialization.base_object_serializer import \
|
||||
BaseObjectSerializer
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
|
||||
serializer = BaseObjectSerializer()
|
||||
if decompose:
|
||||
|
||||
@@ -17,6 +17,7 @@ class CurveTypeEncoding(int, Enum):
|
||||
@property
|
||||
def object_class(self) -> Type:
|
||||
from . import geometry
|
||||
|
||||
if self == self.Arc:
|
||||
return geometry.Arc
|
||||
elif self == self.Circle:
|
||||
@@ -32,7 +33,8 @@ class CurveTypeEncoding(int, Enum):
|
||||
elif self == self.Polycurve:
|
||||
return geometry.Polycurve
|
||||
raise SpeckleException(
|
||||
f'No corresponding object class for CurveTypeEncoding: {self}')
|
||||
f"No corresponding object class for CurveTypeEncoding: {self}"
|
||||
)
|
||||
|
||||
|
||||
def curve_from_list(args: List[float]):
|
||||
@@ -41,61 +43,68 @@ def curve_from_list(args: List[float]):
|
||||
|
||||
|
||||
class ObjectArray:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.data = []
|
||||
|
||||
@classmethod
|
||||
def from_objects(cls, objects: List[Base]) -> 'ObjectArray':
|
||||
data_chunk = cls()
|
||||
if len(objects) == 0:
|
||||
return data_chunk
|
||||
def from_objects(cls, objects: List[Base]) -> "ObjectArray":
|
||||
data_list = cls()
|
||||
if not objects:
|
||||
return data_list
|
||||
|
||||
speckle_type = objects[0].speckle_type
|
||||
|
||||
for obj in objects:
|
||||
if speckle_type != obj.speckle_type:
|
||||
raise SpeckleException(
|
||||
'All objects in chunk should have the same speckle_type. '
|
||||
f'Found {speckle_type} and {obj.speckle_type}'
|
||||
"All objects in chunk should have the same speckle_type. "
|
||||
f"Found {speckle_type} and {obj.speckle_type}"
|
||||
)
|
||||
data_chunk.encode_object(object=obj)
|
||||
data_list.encode_object(object=obj)
|
||||
|
||||
return data_chunk
|
||||
return data_list
|
||||
|
||||
@staticmethod
|
||||
def decode_data(data: List[Any], decoder: Callable[[List[Any]], Base]) -> List[Base]:
|
||||
def decode_data(
|
||||
data: List[Any], decoder: Callable[[List[Any]], Base]
|
||||
) -> List[Base]:
|
||||
bases = []
|
||||
if not data:
|
||||
return bases
|
||||
|
||||
index = 0
|
||||
unchunked_data = []
|
||||
while index < len(data):
|
||||
chunk_length = data[index]
|
||||
chunk_start = int(index + 1)
|
||||
chunk_end = int(chunk_start + chunk_length)
|
||||
chunk_data = data[chunk_start:chunk_end]
|
||||
decoded_data = decoder(chunk_data)
|
||||
unchunked_data.append(decoded_data)
|
||||
index = chunk_end
|
||||
return unchunked_data
|
||||
item_length = data[index]
|
||||
item_start = index + 1
|
||||
item_end = item_start + item_length
|
||||
item_data = data[item_start:item_end]
|
||||
index = item_end
|
||||
# TODO: investigate what's going on w this fail
|
||||
try:
|
||||
decoded_data = decoder(item_data)
|
||||
bases.append(decoded_data)
|
||||
except ValueError:
|
||||
continue
|
||||
return bases
|
||||
|
||||
def decode(self, decoder: Callable[[List[Any]], Any]):
|
||||
return self.decode_data(data=self.data, decoder=decoder)
|
||||
|
||||
def encode_object(self, object: Base):
|
||||
chunk = object.to_list()
|
||||
chunk.insert(0, len(chunk))
|
||||
self.data.extend(chunk)
|
||||
encoded = object.to_list()
|
||||
encoded.insert(0, len(encoded))
|
||||
self.data.extend(encoded)
|
||||
|
||||
|
||||
class CurveArray(ObjectArray):
|
||||
|
||||
@classmethod
|
||||
def from_curve(cls, curve: Base) -> 'CurveArray':
|
||||
def from_curve(cls, curve: Base) -> "CurveArray":
|
||||
crv_array = cls()
|
||||
crv_array.data = curve.to_list()
|
||||
return crv_array
|
||||
|
||||
@classmethod
|
||||
def from_curves(cls, curves: List[Base]) -> 'CurveArray':
|
||||
def from_curves(cls, curves: List[Base]) -> "CurveArray":
|
||||
data = []
|
||||
for curve in curves:
|
||||
curve_list = curve.to_list()
|
||||
|
||||
@@ -15,6 +15,7 @@ UNITS_STRINGS = {
|
||||
}
|
||||
|
||||
UNITS_ENCODINGS = {
|
||||
"none": 0,
|
||||
"mm": 1,
|
||||
"cm": 2,
|
||||
"m": 3,
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
from typing import Dict, List
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import pytest
|
||||
from specklepy.api import operations
|
||||
@@ -87,9 +87,9 @@ class FrozenYoghurt(Base):
|
||||
"""Testing type checking"""
|
||||
|
||||
servings: int
|
||||
flavours: List[str] = None # list item types won't be checked
|
||||
flavours: List[str] # list item types won't be checked
|
||||
customer: str
|
||||
add_ons: Dict[str, float] # dict item types won't be checked
|
||||
add_ons: Optional[Dict[str, float]] # dict item types won't be checked
|
||||
price: float = 0.0
|
||||
|
||||
|
||||
@@ -111,5 +111,3 @@ def test_type_checking() -> None:
|
||||
order.flavours = ["strawberry", "lychee", "peach", "pineapple"]
|
||||
|
||||
assert order.price == 7.0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user