Compare commits

...

22 Commits

Author SHA1 Message Date
izzy lyseggen 5dde1bfcf1 Merge pull request #136 from specklesystems/izzy/stream-wrapper-fix
fix(wrapper): fix for nested branches
2021-11-17 16:12:42 +00:00
izzy lyseggen 82c9d874c9 test(branches): server now returns main first 2021-11-17 16:11:22 +00:00
izzy lyseggen 9acf2c8a92 fix(wrapper): fix for nested branches 2021-11-17 15:59:51 +00:00
Gergő Jedlicska 95012e60c1 Merge pull request #132 from specklesystems/commit-recieved
Commit recieved
2021-10-29 11:01:21 +02:00
Gergő Jedlicska 19b6500bbd Merge branch 'main' of github.com:specklesystems/specklepy into commit-recieved 2021-10-29 10:53:28 +02:00
Gergő Jedlicska 47a06e4630 version bump 2021-10-29 10:49:36 +02:00
Alan Rynne e5a8b40bb2 Merge pull request #131 from specklesystems/commit-recieved
commit recieved service implementation
2021-10-29 10:48:55 +02:00
Gergő Jedlicska 219456f5f8 circleci py310 is not working 2021-10-27 20:28:01 +02:00
Gergő Jedlicska d1544ae89f Merge branch 'commit-recieved' of github.com:specklesystems/specklepy into commit-recieved 2021-10-27 19:20:15 +02:00
Gergő Jedlicska 8f7d4b2ca7 add 3.10 as target 2021-10-27 19:20:04 +02:00
Gergő Jedlicska a7d31d4983 Delete stream_copy.py 2021-10-27 19:18:45 +02:00
Gergő Jedlicska a89b12a02c remove receive server test 2021-10-27 19:16:08 +02:00
Gergő Jedlicska 15ae68f5d7 commit received implementation 2021-10-27 14:13:49 +02:00
izzy lyseggen 0709cd99b5 Merge pull request #129 from specklesystems/izzy/token-auth
feat(wrapper/transport): token auth
2021-10-22 11:21:23 +02:00
izzy lyseggen faf06f7141 fix(server): set transport url 2021-10-21 13:12:19 +01:00
izzy lyseggen b54e09f811 test: server transport & wrapper token auth 2021-10-21 12:20:09 +01:00
izzy lyseggen 55b7e0d732 feat(wrapper): token auth for client and transport 2021-10-21 12:19:49 +01:00
izzy lyseggen 45c922679b feat(server): allow construction with token & url 2021-10-21 12:19:19 +01:00
izzy lyseggen b1c149382a Merge pull request #128 from specklesystems/izzy/cereal-fixes
fix(deserialisation): type check bug and brep encoding hotfix
2021-10-14 17:09:27 +02:00
izzy lyseggen 393e98c8c2 fix(encoding): add none unit type 2021-10-14 16:07:34 +01:00
izzy lyseggen 8376329cbb fix(base): type check error with optional generics
reported by rob on the forum:
https://speckle.community/t/issue-with-type-checking-in-pyhton/1861
2021-10-14 15:41:30 +01:00
izzy lyseggen 1567fe9e68 fix(breps): temp hotfix for curve encoding fail
addresses 🥒 Bug with brep receiving (curve encoding) #127
2021-10-14 15:38:51 +01:00
17 changed files with 400 additions and 135 deletions
+2
View File
@@ -1,3 +1,5 @@
.tool-versions
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "specklepy"
version = "2.1.0"
version = "2.4.0"
description = "The Python SDK for Speckle 2.0"
readme = "README.md"
authors = ["Speckle Systems <devops@speckle.systems>"]
+55 -11
View File
@@ -89,6 +89,28 @@ def get_default_account(base_path: str = None) -> Account:
class StreamWrapper:
"""
The `StreamWrapper` gives you some handy helpers to deal with urls and get authenticated clients and transports.
Construct a `StreamWrapper` with a stream, branch, commit, or object URL. The corresponding ids will be stored
in the wrapper. If you have local accounts on the machine, you can use the `get_account` and `get_client` methods
to get a local account for the server. You can also pass a token into `get_client` if you don't have a corresponding
local account for the server.
```py
from specklepy.api.credentials import StreamWrapper
# provide any stream, branch, commit, object, or globals url
wrapper = StreamWrapper("https://speckle.xyz/streams/3073b96e86/commits/604bea8cc6")
# get an authenticated SpeckleClient if you have a local account for the server
client = wrapper.get_client()
# get an authenticated ServerTransport if you have a local account for the server
transport = wrapper.get_transport()
```
"""
stream_url: str = None
use_ssl: bool = True
host: str = None
@@ -122,9 +144,9 @@ class StreamWrapper:
parsed = urlparse(url)
self.host = parsed.netloc
self.use_ssl = parsed.scheme == "https"
segments = parsed.path.strip("/").split("/")
segments = parsed.path.strip("/").split("/", 3)
if not segments or len(segments) > 4 or len(segments) < 2:
if not segments or len(segments) < 2:
raise SpeckleException(
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
)
@@ -154,6 +176,9 @@ class StreamWrapper:
)
def get_account(self) -> Account:
"""
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
@@ -164,23 +189,42 @@ class StreamWrapper:
return self.account
def get_client(self) -> SpeckleClient:
if self.client:
def get_client(self, token: str = None) -> SpeckleClient:
"""
Gets an authenticated client for this server. You may provide a token if there aren't any local accounts on this
machine. If no account is found and no token is provided, an unauthenticated client is returned.
Arguments:
token {str} -- optional token if no local account is available (defaults to None)
Returns:
SpeckleClient -- authenticated with a corresponding local account or the provided token
"""
if self.client and token is None:
return self.client
if not self.account:
self.get_account()
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:
if self.account is None and token is None:
warn(f"No local account found for server {self.host}", SpeckleWarning)
return self.client
self.client.authenticate(self.account.token)
self.client.authenticate(self.account.token if self.account else token)
return self.client
def get_transport(self) -> ServerTransport:
if not self.client:
self.get_client()
return ServerTransport(self.client, self.stream_id)
def get_transport(self, token: str = None) -> ServerTransport:
"""
Gets a server transport for this stream using an authenticated client. If there is no local account for this
server and the client was not authenticated with a token, this will throw an exception.
Returns:
ServerTransport -- constructed for this stream with a pre-authenticated client
"""
if not self.client or not self.client.me:
self.get_client(token)
return ServerTransport(self.stream_id, self.client)
+37
View File
@@ -185,3 +185,40 @@ class Resource(ResourceBase):
return self.make_request(
query=query, params=params, return_type="commitDelete", parse_response=False
)
def received(
self,
stream_id: str,
commit_id: str,
source_application: str = "python",
message: Optional[str] = None,
) -> bool:
"""
Mark a commit object a received by the source application.
"""
query = gql(
"""
mutation CommitReceive($receivedInput:CommitReceivedInput!){
commitReceive(input:$receivedInput)
}
"""
)
params = {
"receivedInput": {
"sourceApplication": source_application,
"streamId": stream_id,
"commitId": commit_id,
"message": "message",
}
}
try:
return self.make_request(
query=query,
params=params,
return_type="commitReceive",
parse_response=False,
)
except Exception as ex:
print(ex.with_traceback)
return False
+1 -1
View File
@@ -32,4 +32,4 @@ class GraphQLException(SpeckleException):
class SpeckleWarning(Warning):
def __init__(self, *args: object) -> None:
super().__init__(*args)
super().__init__(*args)
+1 -1
View File
@@ -122,4 +122,4 @@ class MetricsTracker(metaclass=Singleton):
except Exception as ex:
LOG.error("Error sending metrics request: " + str(ex))
self.queue.task_done()
self.queue.task_done()
+33 -20
View File
@@ -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:
+35 -26
View File
@@ -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()
+1
View File
@@ -15,6 +15,7 @@ UNITS_STRINGS = {
}
UNITS_ENCODINGS = {
"none": 0,
"mm": 1,
"cm": 2,
"m": 3,
+53 -6
View File
@@ -13,21 +13,68 @@ from .batch_sender import BatchSender
class ServerTransport(AbstractTransport):
"""
The `ServerTransport` is the vehicle through which you transport objects to and from a Speckle Server. Provide it to
`operations.send()` or `operations.receive()`.
The `ServerTransport` can be authenticted two different ways:
1. by providing a `SpeckleClient`
2. by providing a `token` and `url`
```py
from specklepy.api import operations
from specklepy.transports.server import ServerTransport
# here's the data you want to send
block = Block(length=2, height=4)
# next create the server transport - this is the vehicle through which you will send and receive
transport = ServerTransport(stream_id=new_stream_id, client=client)
# this serialises the block and sends it to the transport
hash = operations.send(base=block, transports=[transport])
# you can now create a commit on your stream with this object
commid_id = client.commit.create(
stream_id=new_stream_id,
obj_id=hash,
message="this is a block I made in speckle-py",
)
```
"""
_name = "RemoteTransport"
url: str = None
stream_id: str = None
saved_obj_count: int = 0
session: requests.Session = None
def __init__(self, client: SpeckleClient, stream_id: str, **data: Any) -> None:
def __init__(
self,
stream_id: str,
client: SpeckleClient = None,
token: str = None,
url: str = None,
**data: Any,
) -> None:
super().__init__(**data)
# TODO: replace client with account or some other auth avenue
if not client.me:
raise SpeckleException("The provided SpeckleClient was not authenticated.")
self.url = client.url
self.stream_id = stream_id
if client is None and token is None and url is None:
raise SpeckleException(
"You must provide either a client or a token and url to construct a ServerTransport."
)
if client:
if not client.me:
raise SpeckleException(
"The provided SpeckleClient was not authenticated."
)
token = client.me["token"]
url = client.url
self.stream_id = stream_id
self.url = url
token = client.me["token"]
self._batch_sender = BatchSender(
self.url, self.stream_id, token, max_batch_size_mb=1
)
+3 -5
View File
@@ -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
+1 -1
View File
@@ -58,7 +58,7 @@ class TestBranch:
assert isinstance(branches, list)
assert len(branches) == 2
assert isinstance(branches[0], Branch)
assert branches[0].name == branch.name
assert branches[1].name == branch.name
def test_branch_update(self, client, stream, branch, updated_branch):
updated = client.branch.update(
+17
View File
@@ -68,3 +68,20 @@ class TestCommit:
deleted = client.commit.delete(stream_id=stream.id, commit_id=commit_id)
assert deleted is True
def test_commit_marked_as_received(self, client, stream, mesh) -> None:
commit = Commit(message="this commit should be received")
commit.id = client.commit.create(
stream_id=stream.id,
object_id=mesh.id,
message=commit.message,
)
commit_marked_received = client.commit.received(
stream.id,
commit.id,
source_application="pytest",
message="testing received",
)
assert commit_marked_received == True
+113 -50
View File
@@ -5,11 +5,28 @@ import pytest
from specklepy.api import operations
from specklepy.objects.base import Base
from specklepy.objects.encoding import CurveArray, ObjectArray
from specklepy.objects.geometry import (Arc, Box, Brep, BrepEdge, BrepFace,
BrepLoop, BrepTrim, BrepTrimTypeEnum,
Circle, Curve, Ellipse, Interval, Line,
Mesh, Plane, Point, Polycurve,
Polyline, Surface, Vector)
from specklepy.objects.geometry import (
Arc,
Box,
Brep,
BrepEdge,
BrepFace,
BrepLoop,
BrepTrim,
BrepTrimTypeEnum,
Circle,
Curve,
Ellipse,
Interval,
Line,
Mesh,
Plane,
Point,
Polycurve,
Polyline,
Surface,
Vector,
)
from specklepy.transports.memory import MemoryTransport
@@ -71,7 +88,7 @@ def arc(plane, interval):
angleRadians=33,
plane=plane,
domain=interval,
units='m',
units="m",
# These attributes are not handled in C#
# bbox=None,
# area=None,
@@ -88,7 +105,7 @@ def circle(plane, interval):
radius=22,
plane=plane,
domain=interval,
units='m',
units="m",
# These attributes are not handled in C#
# bbox=None,
# area=None,
@@ -103,7 +120,7 @@ def ellipse(plane, interval):
secondRadius=22,
plane=plane,
domain=interval,
units='m',
units="m",
# These attributes are not handled in C#
# trimDomain=None,
# bbox=None,
@@ -118,7 +135,7 @@ def polyline(interval):
value=[22, 44, 54.3, 99, 232, 21],
closed=True,
domain=interval,
units='m',
units="m",
# These attributes are not handled in C#
# bbox=None,
# area=None,
@@ -137,7 +154,7 @@ def curve(interval):
points=[23, 21, 44, 43, 56, 76, 1, 3, 2],
weights=[23, 11, 23],
knots=[22, 45, 76, 11],
units='m',
units="m",
# These attributes are not handled in C#
# displayValue=None,
# bbox=None,
@@ -152,7 +169,7 @@ def polycurve(interval, curve, polyline):
segments=[curve, polyline],
domain=interval,
closed=True,
units='m',
units="m",
# These attributes are not handled in C#
# bbox=None,
# area=None,
@@ -187,7 +204,7 @@ def surface(interval):
domainV=interval,
knotsU=[1.1, 2.2, 3.3, 4.4],
knotsV=[9, 8, 7, 6, 5, 4.4],
units='m',
units="m",
# These attributes are not handled in C#
# bbox=None,
# area=None,
@@ -218,11 +235,7 @@ def brep_edge(interval):
@pytest.fixture()
def brep_loop():
return BrepLoop(
FaceIndex=5,
TrimIndices=[3, 4, 5],
Type='unknown'
)
return BrepLoop(FaceIndex=5, TrimIndices=[3, 4, 5], Type="unknown")
@pytest.fixture()
@@ -235,7 +248,7 @@ def brep_trim():
LoopIndex=4,
CurveIndex=7,
IsoStatus=6,
TrimType='Mated',
TrimType="Mated",
IsReversed=False,
# These attributes are not handled in C#
# Domain=None,
@@ -243,10 +256,21 @@ def brep_trim():
@pytest.fixture
def brep(mesh, box, surface, curve, polyline, circle, point,
brep_edge, brep_loop, brep_trim, brep_face):
def brep(
mesh,
box,
surface,
curve,
polyline,
circle,
point,
brep_edge,
brep_loop,
brep_trim,
brep_face,
):
return Brep(
provenance='pytest',
provenance="pytest",
bbox=box,
area=32,
volume=54,
@@ -265,33 +289,57 @@ def brep(mesh, box, surface, curve, polyline, circle, point,
@pytest.fixture
def geometry_objects_dict(point, vector, plane, line, arc,
circle, ellipse, polyline, curve,
polycurve, surface, brep_trim):
def geometry_objects_dict(
point,
vector,
plane,
line,
arc,
circle,
ellipse,
polyline,
curve,
polycurve,
surface,
brep_trim,
):
return {
'point': point,
'vector': vector,
'plane': plane,
'line': line,
'arc': arc,
'circle': circle,
'ellipse': ellipse,
'polyline': polyline,
'curve': curve,
'polycurve': polycurve,
'surface': surface,
'brep_trim': brep_trim
"point": point,
"vector": vector,
"plane": plane,
"line": line,
"arc": arc,
"circle": circle,
"ellipse": ellipse,
"polyline": polyline,
"curve": curve,
"polycurve": polycurve,
"surface": surface,
"brep_trim": brep_trim,
}
@pytest.mark.parametrize('object_name', [
'point', 'vector', 'plane', 'line', 'arc', 'circle',
'ellipse', 'polyline', 'curve', 'polycurve', 'surface', 'brep_trim'
])
@pytest.mark.parametrize(
"object_name",
[
"point",
"vector",
"plane",
"line",
"arc",
"circle",
"ellipse",
"polyline",
"curve",
"polycurve",
"surface",
"brep_trim",
],
)
def test_to_and_from_list(object_name: str, geometry_objects_dict):
object = geometry_objects_dict[object_name]
assert hasattr(object, 'to_list')
assert hasattr(object, 'from_list')
assert hasattr(object, "to_list")
assert hasattr(object, "from_list")
chunks = object.to_list()
assert isinstance(chunks, list)
@@ -306,8 +354,7 @@ def test_brep_surfaces_value_serialization(surface):
assert brep.Surfaces == None
assert brep.SurfacesValue == None
brep.Surfaces = [surface, surface]
assert brep.SurfacesValue == ObjectArray.from_objects(
[surface, surface]).data
assert brep.SurfacesValue == ObjectArray.from_objects([surface, surface]).data
brep.SurfacesValue = ObjectArray.from_objects([surface]).data
assert len(brep.Surfaces) == 1
@@ -341,16 +388,32 @@ def test_brep_curve3d_values_serialization(curve, polyline, circle):
def test_brep_vertices_values_serialization():
brep = Brep()
brep.VerticesValue = [1, 1, 1, 1, 2, 2, 2, 3, 3, 3]
brep.Vertices[0].get_id() == Point(x=1, y=1, z=1, _units='mm').get_id()
brep.Vertices[1].get_id() == Point(x=2, y=2, z=2, _units='mm').get_id()
brep.Vertices[2].get_id() == Point(x=3, y=3, z=3, _units='mm').get_id()
brep.Vertices[0].get_id() == Point(x=1, y=1, z=1, _units="mm").get_id()
brep.Vertices[1].get_id() == Point(x=2, y=2, z=2, _units="mm").get_id()
brep.Vertices[2].get_id() == Point(x=3, y=3, z=3, _units="mm").get_id()
def test_trims_value_serialization():
brep = Brep()
brep.TrimsValue = [
0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 0, 0, 0, 0, 1, 2, 1, 0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
0,
0,
0,
0,
1,
2,
1,
0,
]
brep.Trims[0].get_id() == BrepTrim(
@@ -383,7 +446,7 @@ def test_serialized_brep_attributes(brep: Brep):
serialized = operations.serialize(brep, [transport])
serialized_dict = json.loads(serialized)
removed_keys = ['Surfaces', 'Curve3D', 'Curve2D', 'Vertices', 'Trims']
removed_keys = ["Surfaces", "Curve3D", "Curve2D", "Vertices", "Trims"]
for k in removed_keys:
assert k not in serialized_dict.keys()
+3 -11
View File
@@ -21,11 +21,9 @@ class TestObject:
def test_object_create(self, client, stream, base):
transport = SQLiteTransport()
s = BaseObjectSerializer(
write_transports=[transport], read_transport=transport)
s = BaseObjectSerializer(write_transports=[transport], read_transport=transport)
_, base_dict = s.traverse_base(base)
obj_id = client.object.create(
stream_id=stream.id, objects=[base_dict])[0]
obj_id = client.object.create(stream_id=stream.id, objects=[base_dict])[0]
assert isinstance(obj_id, str)
assert base_dict["@detach"]["speckle_type"] == "reference"
@@ -43,12 +41,6 @@ class TestObject:
def test_object_array_decoder(self):
array = ObjectArray()
array.data = [
5, 1, 1, 1, 1, 1,
4, 1, 1, 1, 1,
3, 1, 1, 1,
2, 1, 1,
1, 1
]
array.data = [5, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 1]
assert array.decode(decoder=sum) == [5, 4, 3, 2, 1]
+6 -2
View File
@@ -1,5 +1,4 @@
import json
from attr import has
import pytest
from specklepy.api import operations
from specklepy.transports.server import ServerTransport
@@ -53,6 +52,11 @@ class TestSerialization:
def test_send_and_receive(self, client, sample_stream, mesh):
transport = ServerTransport(client=client, stream_id=sample_stream.id)
hash = operations.send(mesh, transports=[transport])
# also try constructing server transport with token and url
transport = ServerTransport(
stream_id=sample_stream.id, token=client.me["token"], url=client.url
)
# use a fresh memory transport to force receiving from remote
received = operations.receive(
hash, remote_transport=transport, local_transport=MemoryTransport()
@@ -84,4 +88,4 @@ class TestSerialization:
untyped = '{"foo": "bar"}'
deserialised = operations.deserialize(untyped)
assert deserialised == {"foo": "bar"}
assert deserialised == {"foo": "bar"}
+38
View File
@@ -18,6 +18,14 @@ class TestWrapper:
assert wacky_wrap.branch_name == "🍕⬅🌟 you wat?"
assert wrap.type == "branch"
def test_parse_nested_branch(self):
wrap = StreamWrapper(
"https://testing.speckle.dev/streams/4c3ce1459c/branches/izzy/dev"
)
assert wrap.branch_name == "izzy/dev"
assert wrap.type == "branch"
def test_parse_commit(self):
wrap = StreamWrapper(
"https://testing.speckle.dev/streams/4c3ce1459c/commits/8b9b831792"
@@ -39,3 +47,33 @@ class TestWrapper:
"https://testing.speckle.dev/streams/0c6ad366c4/globals/abd3787893"
)
assert wrap.type == "commit"
#! NOTE: the following three tests may not pass locally if you have a `speckle.xyz` account in manager
def test_get_client_without_auth(self):
wrap = StreamWrapper(
"https://speckle.xyz/streams/4c3ce1459c/commits/8b9b831792"
)
client = wrap.get_client()
assert client is not None
def test_get_new_client_with_token(self):
wrap = StreamWrapper(
"https://speckle.xyz/streams/4c3ce1459c/commits/8b9b831792"
)
client = wrap.get_client()
client = wrap.get_client(token="super-secret-token")
assert client.me["token"] == "super-secret-token"
def test_get_transport_with_token(self):
wrap = StreamWrapper(
"https://speckle.xyz/streams/4c3ce1459c/commits/8b9b831792"
)
client = wrap.get_client()
assert not client.me # unauthenticated bc no local accounts
transport = wrap.get_transport(token="super-secret-token")
assert transport is not None
assert client.me["token"] == "super-secret-token"