Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3246edda8f |
+8
-11
@@ -4,7 +4,7 @@ orbs:
|
||||
python: circleci/python@1.3.2
|
||||
|
||||
jobs:
|
||||
test:
|
||||
build:
|
||||
docker:
|
||||
- image: "cimg/python:<<parameters.tag>>"
|
||||
- image: "circleci/node:12"
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
POSTGRES_DB: speckle2_test
|
||||
POSTGRES_PASSWORD: speckle
|
||||
POSTGRES_USER: speckle
|
||||
- image: "speckle/speckle-server"
|
||||
- image: "speckle/speckle-server:5f8cf11cba07ea6a54000243f9cb343b61cbba13"
|
||||
command: ["bash", "-c", "/wait && node bin/www"]
|
||||
environment:
|
||||
POSTGRES_URL: "localhost"
|
||||
@@ -38,30 +38,27 @@ jobs:
|
||||
name: upgrade pip
|
||||
- python/install-packages:
|
||||
pkg-manager: poetry
|
||||
- run: poetry run pytest
|
||||
- run: poetry run pytest --version
|
||||
|
||||
deploy:
|
||||
docker:
|
||||
- image: "circleci/python:3.8"
|
||||
steps:
|
||||
- checkout
|
||||
- run: python patch_version.py $CIRCLE_TAG
|
||||
- run: poetry build
|
||||
- run: poetry publish -u specklesystems -p $PYPI_PASSWORD
|
||||
|
||||
workflows:
|
||||
main:
|
||||
jobs:
|
||||
- test:
|
||||
jobs:
|
||||
- build:
|
||||
matrix:
|
||||
parameters:
|
||||
tag: ["3.6", "3.7", "3.8", "3.9"]
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- deploy:
|
||||
requires:
|
||||
- test
|
||||
filters:
|
||||
tags:
|
||||
only: /[0-9]+(\.[0-9]+)*/
|
||||
|
||||
@@ -45,7 +45,7 @@ from specklepy.api.credentials import get_default_account, get_local_accounts
|
||||
all_accounts = get_local_accounts() # get back a list
|
||||
account = get_default_account()
|
||||
|
||||
client = SpeckleClient(host="speckle.xyz")
|
||||
client = SpeckleClient(host="localhost:3000", use_ssl=False)
|
||||
# client = SpeckleClient(host="yourserver.com") or whatever your host is
|
||||
|
||||
client.authenticate(account.token)
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def patch(tag):
|
||||
print(f"Patching version: {tag}")
|
||||
|
||||
with open("pyproject.toml", "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
if "version" not in lines[2]:
|
||||
raise Exception(f"Invalid pyproject.toml. Could not patch version.")
|
||||
|
||||
lines[2] = f'version = "{tag}"\n'
|
||||
with open("pyproject.toml", "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
return
|
||||
|
||||
tag = sys.argv[1]
|
||||
if not re.match(r"[0-9]+(\.[0-9]+)*$", tag):
|
||||
raise ValueError(f"Invalid tag provided: {tag}")
|
||||
|
||||
patch(tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "specklepy"
|
||||
version = "2.1.0"
|
||||
version = "2.2.3"
|
||||
description = "The Python SDK for Speckle 2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Speckle Systems <devops@speckle.systems>"]
|
||||
|
||||
@@ -21,33 +21,6 @@ from gql.transport.websockets import WebsocketsTransport
|
||||
|
||||
|
||||
class SpeckleClient:
|
||||
"""
|
||||
The `SpeckleClient` is your entry point for interacting with your Speckle Server's GraphQL API.
|
||||
You'll need to have access to a server to use it, or you can use our public server `speckle.xyz`.
|
||||
|
||||
To authenticate the client, you'll need to have downloaded the [Speckle Manager](https://speckle.guide/#speckle-manager)
|
||||
and added your account.
|
||||
|
||||
```py
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.credentials import get_default_account
|
||||
|
||||
# initialise the client
|
||||
client = SpeckleClient(host="speckle.xyz") # or whatever your host is
|
||||
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
|
||||
|
||||
# authenticate the client with a token (account has been added in Speckle Manager)
|
||||
account = get_default_account()
|
||||
client.authenticate(token=account.token)
|
||||
|
||||
# create a new stream. this returns the stream id
|
||||
new_stream_id = client.stream.create(name="a shiny new stream")
|
||||
|
||||
# use that stream id to get the stream from the server
|
||||
new_stream = client.stream.get(id=new_stream_id)
|
||||
```
|
||||
"""
|
||||
|
||||
DEFAULT_HOST = "speckle.xyz"
|
||||
USE_SSL = True
|
||||
|
||||
@@ -82,11 +55,6 @@ class SpeckleClient:
|
||||
except Exception as ex:
|
||||
raise SpeckleException(f"{self.url} is not a compatible Speckle Server", ex)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"SpeckleClient( server: {self.url}, authenticated: {self.me is not None} )"
|
||||
)
|
||||
|
||||
def authenticate(self, token: str) -> None:
|
||||
"""Authenticate the client using a personal access token
|
||||
The token is saved in the client object and a synchronous GraphQL entrypoint is created
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import os
|
||||
from specklepy.transports.server.server import ServerTransport
|
||||
from warnings import warn
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from urllib.parse import urlparse, unquote
|
||||
from pydantic import BaseModel
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
@@ -83,100 +79,3 @@ def get_default_account(base_path: str = None) -> Account:
|
||||
default.isDefault = True
|
||||
|
||||
return default
|
||||
|
||||
|
||||
class StreamWrapper:
|
||||
stream_url: str = None
|
||||
use_ssl: bool = True
|
||||
host: str = None
|
||||
stream_id: str = None
|
||||
commit_id: str = None
|
||||
object_id: str = None
|
||||
branch_name: str = None
|
||||
client: SpeckleClient = None
|
||||
account: Account = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type: {self.type} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
if self.object_id:
|
||||
return "object"
|
||||
elif self.commit_id:
|
||||
return "commit"
|
||||
elif self.branch_name:
|
||||
return "branch"
|
||||
else:
|
||||
return "stream" if self.stream_id else "invalid"
|
||||
|
||||
def __init__(self, url: str) -> None:
|
||||
self.stream_url = url
|
||||
parsed = urlparse(url)
|
||||
self.host = parsed.netloc
|
||||
self.use_ssl = parsed.scheme == "https"
|
||||
segments = parsed.path.strip("/").split("/")
|
||||
|
||||
if not segments or len(segments) > 4 or len(segments) < 2:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
while segments:
|
||||
segment = segments.pop(0)
|
||||
if segments and segment.lower() == "streams":
|
||||
self.stream_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "commits":
|
||||
self.commit_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "branches":
|
||||
self.branch_name = unquote(segments.pop(0))
|
||||
elif segments and segment.lower() == "objects":
|
||||
self.object_id = segments.pop(0)
|
||||
elif segment.lower() == "globals":
|
||||
self.branch_name = "globals"
|
||||
if segments:
|
||||
self.commit_id = segments.pop(0)
|
||||
else:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
if not self.stream_id:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - no stream id found."
|
||||
)
|
||||
|
||||
def get_account(self) -> Account:
|
||||
if self.account:
|
||||
return self.account
|
||||
|
||||
self.account = next(
|
||||
(a for a in get_local_accounts() if self.host in a.serverInfo.url),
|
||||
None,
|
||||
)
|
||||
|
||||
return self.account
|
||||
|
||||
def get_client(self) -> SpeckleClient:
|
||||
if self.client:
|
||||
return self.client
|
||||
|
||||
if not self.account:
|
||||
self.get_account()
|
||||
|
||||
self.client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
|
||||
|
||||
if self.account is None:
|
||||
warn(f"No local account found for server {self.host}", SpeckleWarning)
|
||||
return self.client
|
||||
|
||||
self.client.authenticate(acct.token)
|
||||
return self.client
|
||||
|
||||
def get_transport(self) -> ServerTransport:
|
||||
if not self.client:
|
||||
self.get_client()
|
||||
return ServerTransport(self.client, self.stream_id)
|
||||
@@ -22,7 +22,6 @@ class Commit(BaseModel):
|
||||
authorName: Optional[str]
|
||||
authorId: Optional[str]
|
||||
authorAvatar: Optional[str]
|
||||
branchName: Optional[str]
|
||||
createdAt: Optional[str]
|
||||
sourceApplication: Optional[str]
|
||||
referencedObject: Optional[str]
|
||||
|
||||
@@ -52,8 +52,7 @@ def receive(
|
||||
Arguments:
|
||||
obj_id {str} -- the id of the object to receive
|
||||
remote_transport {Transport} -- the transport to receive from
|
||||
local_transport {Transport} -- the local cache to check for existing objects
|
||||
(defaults to `SQLiteTransport`)
|
||||
local_transport {Transport} -- the transport to send from
|
||||
|
||||
Returns:
|
||||
Base -- the base object
|
||||
@@ -98,7 +97,9 @@ def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str
|
||||
return serializer.write_json(base)[1]
|
||||
|
||||
|
||||
def deserialize(obj_string: str, read_transport: AbstractTransport = None) -> Base:
|
||||
def deserialize(
|
||||
obj_string: str, read_transport: AbstractTransport = SQLiteTransport()
|
||||
) -> Base:
|
||||
"""
|
||||
Deserialize a string object into a Base object. If the object contains referenced child objects that are not stored in the local db, a read transport needs to be provided in order to recompose the base with the children objects.
|
||||
|
||||
@@ -110,9 +111,6 @@ def deserialize(obj_string: str, read_transport: AbstractTransport = None) -> Ba
|
||||
Returns:
|
||||
Base -- the deserialized object
|
||||
"""
|
||||
if not read_transport:
|
||||
read_transport = SQLiteTransport()
|
||||
|
||||
serializer = BaseObjectSerializer(read_transport=read_transport)
|
||||
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
|
||||
@@ -40,7 +40,6 @@ class Resource(ResourceBase):
|
||||
authorId
|
||||
authorName
|
||||
authorAvatar
|
||||
branchName
|
||||
createdAt
|
||||
sourceApplication
|
||||
totalChildrenCount
|
||||
|
||||
@@ -28,8 +28,3 @@ class GraphQLException(SpeckleException):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"GraphQLException: {self.message}"
|
||||
|
||||
|
||||
class SpeckleWarning(Warning):
|
||||
def __init__(self, *args: object) -> None:
|
||||
super().__init__(*args)
|
||||
@@ -98,7 +98,7 @@ class Base(_RegisteringBase):
|
||||
@classmethod
|
||||
def validate_prop_name(cls, name: str) -> None:
|
||||
"""Validator for dynamic attribute names."""
|
||||
if name in {"", "@"}:
|
||||
if name in ("", "@"):
|
||||
raise ValueError("Invalid Name: Base member names cannot be empty strings")
|
||||
if name.startswith("@@"):
|
||||
raise ValueError(
|
||||
@@ -172,7 +172,7 @@ class Base(_RegisteringBase):
|
||||
if not name.startswith("_")
|
||||
and name
|
||||
!= "fields" # soon to be removed as this pydantic prop is depreciated
|
||||
and isinstance(getattr(type(self), name, None), property)
|
||||
and isinstance(getattr(self, name, None), property)
|
||||
]
|
||||
return attrs + properties
|
||||
|
||||
@@ -215,11 +215,15 @@ class Base(_RegisteringBase):
|
||||
return 0
|
||||
parsed.append(base)
|
||||
|
||||
return sum(
|
||||
self._handle_object_count(value, parsed)
|
||||
for name, value in base.__dict__.items()
|
||||
if not name.startswith("@")
|
||||
)
|
||||
count = 0
|
||||
|
||||
for name, value in base.__dict__.items():
|
||||
if name.startswith("@"):
|
||||
continue
|
||||
else:
|
||||
count += self._handle_object_count(value, parsed)
|
||||
|
||||
return count
|
||||
|
||||
def _handle_object_count(self, obj: Any, parsed: List) -> int:
|
||||
count = 0
|
||||
|
||||
@@ -24,6 +24,10 @@ class Point(Base, speckle_type=GEOMETRY + "Point"):
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, id: {self.id}, speckle_type: {self.speckle_type})"
|
||||
|
||||
@property
|
||||
def value(self) -> List[float]:
|
||||
return [self.x, self.y, self.z]
|
||||
|
||||
|
||||
class Vector(Point, speckle_type=GEOMETRY + "Vector"):
|
||||
pass
|
||||
@@ -93,7 +97,7 @@ class Ellipse(Base, speckle_type=GEOMETRY + "Ellipse"):
|
||||
|
||||
|
||||
class Polyline(Base, speckle_type=GEOMETRY + "Polyline"):
|
||||
value: List[float] = []
|
||||
value: List[float] = None
|
||||
closed: bool = None
|
||||
domain: Interval = None
|
||||
bbox: Box = None
|
||||
|
||||
@@ -6,7 +6,6 @@ UNITS_STRINGS = {
|
||||
"mm": ["mm", "mil", "millimeters", "millimetres"],
|
||||
"cm": ["cm", "centimetre", "centimeter", "centimetres", "centimeters"],
|
||||
"m": ["m", "meter", "meters", "metre", "metres"],
|
||||
"km": ["km", "kilometer", "kilometre", "kilometers", "kilometres"],
|
||||
"in": ["in", "inch", "inches"],
|
||||
"ft": ["ft", "foot", "feet"],
|
||||
"yd": ["yd", "yard", "yards"],
|
||||
|
||||
@@ -22,15 +22,11 @@ class ServerTransport(AbstractTransport):
|
||||
def __init__(self, client: SpeckleClient, stream_id: str, **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
|
||||
|
||||
token = client.me["token"]
|
||||
self._batch_sender = BatchSender(
|
||||
self.url, self.stream_id, token, max_batch_size_mb=1
|
||||
)
|
||||
self._batch_sender = BatchSender(self.url, self.stream_id, token, max_batch_size_mb=1)
|
||||
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update(
|
||||
@@ -77,25 +73,19 @@ class ServerTransport(AbstractTransport):
|
||||
r.encoding = "utf-8"
|
||||
|
||||
if r.status_code != 200:
|
||||
raise SpeckleException(
|
||||
f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})"
|
||||
)
|
||||
raise SpeckleException(f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})")
|
||||
root_obj_serialized = r.text
|
||||
root_obj = json.loads(root_obj_serialized)
|
||||
closures = root_obj.get("__closure", {})
|
||||
closures = root_obj.get('__closure', {})
|
||||
|
||||
# Check which children are not already in the target transport
|
||||
children_ids = list(closures.keys())
|
||||
children_found_map = target_transport.has_objects(children_ids)
|
||||
new_children_ids = [
|
||||
id for id in children_found_map if not children_found_map[id]
|
||||
]
|
||||
new_children_ids = [id for id in children_found_map if not children_found_map[id]]
|
||||
|
||||
# Get the new children
|
||||
endpoint = f"{self.url}/api/getobjects/{self.stream_id}"
|
||||
r = self.session.post(
|
||||
endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True
|
||||
)
|
||||
r = self.session.post(endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True)
|
||||
if r.encoding is None:
|
||||
r.encoding = "utf-8"
|
||||
lines = r.iter_lines(decode_unicode=True)
|
||||
|
||||
@@ -6,6 +6,7 @@ import sqlite3
|
||||
from typing import Any, List, Dict
|
||||
from appdirs import user_data_dir
|
||||
from contextlib import closing
|
||||
from multiprocessing import Process, Queue
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -18,6 +19,7 @@ class SQLiteTransport(AbstractTransport):
|
||||
_scheduler = sched.scheduler(time.time, time.sleep)
|
||||
_polling_interval = 0.5 # seconds
|
||||
__connection: sqlite3.Connection = None
|
||||
__queue: Queue = Queue()
|
||||
app_name: str = ""
|
||||
scope: str = ""
|
||||
saved_obj_count: int = 0
|
||||
@@ -34,18 +36,12 @@ class SQLiteTransport(AbstractTransport):
|
||||
self.scope = scope or "Objects"
|
||||
self._base_path = base_path or self.__get_base_path()
|
||||
|
||||
try:
|
||||
os.makedirs(self._base_path, exist_ok=True)
|
||||
os.makedirs(self._base_path, exist_ok=True)
|
||||
|
||||
self._root_path = os.path.join(
|
||||
os.path.join(self._base_path, f"{self.scope}.db")
|
||||
)
|
||||
self.__initialise()
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
f"SQLiteTransport could not initialise {self.scope}.db at {self._base_path}. Either provide a different `base_path` or use an alternative transport.",
|
||||
ex,
|
||||
)
|
||||
self._root_path = os.path.join(
|
||||
os.path.join(self._base_path, f"{self.scope}.db")
|
||||
)
|
||||
self.__initialise()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"SQLiteTransport(app: '{self.app_name}', scope: '{self.scope}')"
|
||||
@@ -67,26 +63,26 @@ class SQLiteTransport(AbstractTransport):
|
||||
if os_name.startswith("Mac"):
|
||||
system = "darwin"
|
||||
|
||||
if system != "darwin":
|
||||
if system == "darwin":
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, self.app_name)
|
||||
else:
|
||||
return user_data_dir(appname=self.app_name, appauthor=False, roaming=True)
|
||||
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, self.app_name)
|
||||
def __consume_queue(self):
|
||||
if self._is_writing or self.__queue.empty():
|
||||
return
|
||||
print("CONSUME QUEUE")
|
||||
self._is_writing = True
|
||||
while not self.__queue.empty():
|
||||
data = self.__queue.get()
|
||||
self.save_object(data[0], data[1])
|
||||
self._is_writing = False
|
||||
|
||||
# def __consume_queue(self):
|
||||
# if self._is_writing or self.__queue.empty():
|
||||
# return
|
||||
# print("CONSUME QUEUE")
|
||||
# self._is_writing = True
|
||||
# while not self.__queue.empty():
|
||||
# data = self.__queue.get()
|
||||
# self.save_object(data[0], data[1])
|
||||
# self._is_writing = False
|
||||
|
||||
# self._scheduler.enter(
|
||||
# delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
# )
|
||||
# self._scheduler.run(blocking=True)
|
||||
self._scheduler.enter(
|
||||
delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
)
|
||||
self._scheduler.run(blocking=True)
|
||||
|
||||
# def save_object(self, id: str, serialized_object: str) -> None:
|
||||
# """Adds an object to the queue and schedules it to be saved.
|
||||
@@ -106,14 +102,15 @@ class SQLiteTransport(AbstractTransport):
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
) -> None:
|
||||
"""Adds an object from the given transport to the the local db
|
||||
"""Adds an object from the given transport to the queue and schedules it to be saved.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the object id
|
||||
source_transport {AbstractTransport) -- the transport through which the object can be found
|
||||
"""
|
||||
serialized_object = source_transport.get_object(id)
|
||||
self.save_object(id, serialized_object)
|
||||
self.__queue.put((id, serialized_object))
|
||||
raise NotImplementedError
|
||||
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
"""Directly saves an object into the database.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import pytest
|
||||
from specklepy.api.credentials import StreamWrapper
|
||||
|
||||
|
||||
class TestWrapper:
|
||||
def test_parse_stream(self):
|
||||
wrap = StreamWrapper("https://testing.speckle.dev/streams/a75ab4f10f")
|
||||
assert wrap.type == "stream"
|
||||
|
||||
def test_parse_branch(self):
|
||||
wacky_wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/branches/%F0%9F%8D%95%E2%AC%85%F0%9F%8C%9F%20you%20wat%3F"
|
||||
)
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/branches/next%20level"
|
||||
)
|
||||
assert wacky_wrap.type == "branch"
|
||||
assert wacky_wrap.branch_name == "🍕⬅🌟 you wat?"
|
||||
assert wrap.type == "branch"
|
||||
|
||||
def test_parse_commit(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/commits/8b9b831792"
|
||||
)
|
||||
assert wrap.type == "commit"
|
||||
|
||||
def test_parse_object(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/a75ab4f10f/objects/5530363e6d51c904903dafc3ea1d2ec6"
|
||||
)
|
||||
assert wrap.type == "object"
|
||||
|
||||
def test_parse_globals_as_branch(self):
|
||||
wrap = StreamWrapper("https://testing.speckle.dev/streams/0c6ad366c4/globals/")
|
||||
assert wrap.type == "branch"
|
||||
|
||||
def test_parse_globals_as_commit(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/0c6ad366c4/globals/abd3787893"
|
||||
)
|
||||
assert wrap.type == "commit"
|
||||
Reference in New Issue
Block a user