Compare commits

..

43 Commits

Author SHA1 Message Date
Gergő Jedlicska 1b59f0b026 feat: fix authenticate with token mechanism (#330) 2024-02-26 16:30:28 +00:00
Jedd Morgan 78123936d2 Merge pull request #329 from specklesystems/jrm/spirals-fix
fix(objects): [CNX-9014] Fixed issue with Spiral turns not deserializing in SpecklePy
2024-02-19 13:05:38 +00:00
Jedd Morgan dbc1aefed3 Fixed issue with Spiral turns not deserializing in SpecklePy 2024-02-19 12:41:35 +00:00
Gergő Jedlicska e726345b0c Merge pull request #328 from mortenengen/fix-gis-dict-type
fix: dict type of renderer in Layer
2024-02-13 08:30:48 +01:00
Morten Engen e074dbcced gis/layers: fix dict type of renderer in Layer 2024-02-12 20:54:38 +01:00
Gergő Jedlicska 62e342b2cb Merge pull request #302 from specklesystems/gergo/objects_init
fix: object initialization
2024-02-12 17:38:04 +01:00
Gergő Jedlicska 804dd37639 Merge branch 'main' into gergo/objects_init 2024-02-12 17:30:03 +01:00
Gergő Jedlicska 64b61f54f5 Merge pull request #322 from specklesystems/kate-2.17-hotfix
.url attribute was used before assignment; assign account to unauthen…
2024-02-12 17:29:50 +01:00
KatKatKateryna 58789ab234 undo 2024-02-12 15:33:21 +00:00
KatKatKateryna 2696fb74ba raise instream of returning Exception 2024-02-12 15:30:00 +00:00
KatKatKateryna 57e176af91 typo 2024-02-12 15:23:37 +00:00
KatKatKateryna 437483641c extra test 2024-02-12 15:23:01 +00:00
KatKatKateryna 1e971b57c3 formatting 2024-02-12 15:14:10 +00:00
KatKatKateryna f04be12ec8 formatting 2024-02-12 15:11:11 +00:00
KatKatKateryna 51242928ca remove circular import 2024-02-12 14:56:47 +00:00
KatKatKateryna 77b3be9145 formatting 2024-02-12 14:52:31 +00:00
KatKatKateryna cc5abdf9cb Merge branch 'main' into kate-2.17-hotfix 2024-02-12 13:52:34 +00:00
KatKatKateryna 4eca5144a8 unused import 2024-02-12 13:52:29 +00:00
KatKatKateryna 8589663049 don't get default account 2024-02-12 13:47:24 +00:00
KatKatKateryna 956f72dd6a formatting 2024-02-12 13:09:09 +00:00
KatKatKateryna a2daa68c1c Merge branch 'main' into gergo/objects_init 2024-02-12 13:00:00 +00:00
Gergő Jedlicska d60feb73a2 Merge pull request #259 from specklesystems/kate/branch_create_fix
gql minimum characters restriction for consistent behavior with frontend
2024-02-12 13:42:32 +01:00
KatKatKateryna a0ca10ad20 add to_string; add cases for object url in fe2 (#327)
* add to_string; add cases for object url in fe2

* cover exceptions

* add federated model exception, reorder conditions

* formatting

* reformatting

* update black formatter

* resolving dependencies
2024-02-09 18:35:20 +01:00
Gergő Jedlicska f6118f3336 Merge pull request #326 from specklesystems/isFrontend2
add frontend2 property to ServerInfo
2024-02-05 13:25:33 +01:00
KatKatKateryna c7cd2f3e91 test 2024-02-05 11:23:22 +00:00
KatKatKateryna b374bfefd0 reorder import 2024-02-05 11:17:51 +00:00
KatKatKateryna d716db251f add frontend2 property to ServerInfo 2024-02-05 10:40:13 +00:00
Gergő Jedlicska 6d7e7c5c4b Merge pull request #324 from specklesystems/gergo/expose_ssl_verification
Update client.py
2023-12-15 16:15:45 +01:00
Gergő Jedlicska 7dcd9288ca Merge branch 'main' into gergo/expose_ssl_verification 2023-12-15 11:19:09 +01:00
Jedd Morgan 7d99f48def Merge pull request #323 from specklesystems/gergo/init_subclass_fix
fix: CNX-8350 remove unnecessary kwargs from init subclass call chain
2023-12-14 14:46:40 +00:00
Jedd Morgan 4332a8faef Merge branch 'main' into gergo/init_subclass_fix 2023-12-14 14:44:13 +00:00
Gergő Jedlicska a1aee8b3fa Merge pull request #325 from specklesystems/gergo/file_based_automate_function_inputs
feat: read automation function inputs from file
2023-12-13 15:16:59 +01:00
Gergő Jedlicska deb8ad50c5 fix: client certificate verification 2023-12-11 17:34:26 +01:00
Gergő Jedlicska 558b25b1d1 feat: read automation function inputs from file 2023-12-11 17:30:08 +01:00
Gergő Jedlicska 4db0fa69fa Update client.py 2023-12-11 17:15:07 +01:00
Gergő Jedlicska 1eca211c96 fix: remove debug print statement 2023-12-06 11:50:07 +01:00
Gergő Jedlicska f65173581a fix: pre-commit config 2023-12-05 16:07:23 +01:00
Gergő Jedlicska 223c776c63 fix: remove unnecessary kwargs from init subclass call chain 2023-12-05 15:03:25 +01:00
KatKatKateryna ccccc53f59 .url attribute was used before assignment; assign account to unauthenticated client to get token 2023-12-05 05:21:28 +08:00
KatKatKateryna 541e3d961f moving restrictions to core 2023-09-19 10:56:39 +01:00
KatKatKateryna b02f183533 Merge branch 'main' into kate/branch_create_fix 2023-09-19 10:52:12 +01:00
Gergő Jedlicska 589198f5f1 fix: object initialization
now every time specklepy is imported, all object definitions are initialized.
 This ensures, that all speckle types are registered.
2023-09-19 11:31:57 +02:00
KatKatKateryna 6a9f4bf89b gql minimum characters restriction for consistent behavior with frontend 2023-02-08 07:29:02 +08:00
36 changed files with 788 additions and 535 deletions
+5 -5
View File
@@ -6,11 +6,11 @@ repos:
- repo: https://github.com/commitizen-tools/commitizen
hooks:
- id: commitizen
- id: commitizen-branch
stages:
- push
rev: 3.12.0
- id: commitizen
- id: commitizen-branch
stages:
- push
rev: v3.13.0
- repo: https://github.com/pycqa/isort
rev: 5.12.0
Generated
+460 -421
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "specklepy"
version = "2.17.8"
version = "2.17.14"
description = "The Python SDK for Speckle 2.0"
readme = "README.md"
authors = ["Speckle Systems <devops@speckle.systems>"]
@@ -16,9 +16,9 @@ packages = [
[tool.poetry.dependencies]
python = ">=3.8.0, <4.0"
pydantic = "^2.0"
pydantic = "^2.5"
appdirs = "^1.4.4"
gql = {extras = ["requests", "websockets"], version = "^3.3.0"}
gql = { extras = ["requests", "websockets"], version = "^3.3.0" }
ujson = "^5.3.0"
Deprecated = "^1.2.13"
stringcase = "^1.2.0"
@@ -26,7 +26,7 @@ attrs = "^23.1.0"
httpx = "^0.25.0"
[tool.poetry.group.dev.dependencies]
black = "^22.8.0"
black = "23.11.0"
isort = "^5.7.0"
pytest = "^7.1.3"
pytest-ordering = "^0.6"
+1 -1
View File
@@ -18,7 +18,7 @@ from speckle_automate.schema import (
from specklepy.api import operations
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException
from specklepy.objects import Base
from specklepy.objects.base import Base
from specklepy.transports.memory import MemoryTransport
from specklepy.transports.server import ServerTransport
+65 -26
View File
@@ -4,14 +4,16 @@ Provides mechanisms to execute any function,
that conforms to the AutomateFunction "interface"
"""
import json
import os
import sys
import traceback
from pathlib import Path
from typing import Callable, Optional, TypeVar, Union, overload
from typing import Callable, Optional, Tuple, TypeVar, Union, overload
from pydantic import create_model
from pydantic.json_schema import GenerateJsonSchema
from speckle_automate.automation_context import AutomationContext
from speckle_automate.schema import AutomateBase, AutomationStatus
from speckle_automate.schema import AutomateBase, AutomationRunData, AutomationStatus
T = TypeVar("T", bound=AutomateBase)
@@ -19,6 +21,41 @@ AutomateFunction = Callable[[AutomationContext, T], None]
AutomateFunctionWithoutInputs = Callable[[AutomationContext], None]
def _read_input_data(inputs_location: str) -> str:
input_path = Path(inputs_location)
if not input_path.exists():
raise ValueError(f"Cannot find the function inputs file at {input_path}")
return input_path.read_text()
def _parse_input_data(
input_location: str, input_schema: Optional[type[T]]
) -> Tuple[AutomationRunData, Optional[T], str]:
input_json_string = _read_input_data(input_location)
class FunctionRunData(AutomateBase):
speckle_token: str
automation_run_data: AutomationRunData
function_inputs: None = None
parser_model = FunctionRunData
if input_schema:
parser_model = create_model(
"FunctionRunDataWithInputs",
function_inputs=(input_schema, ...),
__base__=FunctionRunData,
)
input_data = parser_model.model_validate_json(input_json_string)
return (
input_data.automation_run_data,
input_data.function_inputs,
input_data.speckle_token,
)
@overload
def execute_automate_function(
automate_function: AutomateFunction[T],
@@ -32,6 +69,13 @@ def execute_automate_function(automate_function: AutomateFunctionWithoutInputs)
...
class AutomateGenerateJsonSchema(GenerateJsonSchema):
def generate(self, schema, mode="validation"):
json_schema = super().generate(schema, mode=mode)
json_schema["$schema"] = self.schema_dialect
return json_schema
def execute_automate_function(
automate_function: Union[AutomateFunction[T], AutomateFunctionWithoutInputs],
input_schema: Optional[type[T]] = None,
@@ -40,49 +84,44 @@ def execute_automate_function(
# first arg is the python file name, we do not need that
args = sys.argv[1:]
if len(args) < 2:
raise ValueError("too few arguments specified need minimum 2")
if len(args) > 4:
raise ValueError("too many arguments specified, max supported is 4")
if len(args) != 2:
raise ValueError("Incorrect number of arguments specified need 2")
# we rely on a command name convention to decide what to do.
# this is here, so that the function authors do not see any of this
command = args[0]
command, argument = args
if command == "generate_schema":
path = Path(args[1])
path = Path(argument)
schema = json.dumps(
input_schema.model_json_schema(by_alias=True) if input_schema else {}
input_schema.model_json_schema(
by_alias=True, schema_generator=AutomateGenerateJsonSchema
)
if input_schema
else {}
)
path.write_text(schema)
elif command == "run":
automation_run_data = args[1]
function_inputs = args[2]
automation_run_data, function_inputs, speckle_token = _parse_input_data(
argument, input_schema
)
speckle_token = os.environ.get("SPECKLE_TOKEN", None)
if not speckle_token and len(args) != 4:
raise ValueError("Cannot get speckle token from arguments or environment")
speckle_token = speckle_token if speckle_token else args[3]
automation_context = AutomationContext.initialize(
automation_run_data, speckle_token
)
inputs = (
input_schema.model_validate_json(function_inputs)
if input_schema
else input_schema
)
if inputs:
if function_inputs:
automation_context = run_function(
automation_context,
automate_function, # type: ignore
inputs,
function_inputs, # type: ignore
)
else:
automation_context = AutomationContext.initialize(
automation_run_data, speckle_token
)
automation_context = run_function(
automation_context,
automate_function, # type: ignore
+3
View File
@@ -0,0 +1,3 @@
from specklepy import objects
__all__ = ["objects"]
+7 -1
View File
@@ -50,10 +50,16 @@ class SpeckleClient(CoreSpeckleClient):
DEFAULT_HOST = "speckle.xyz"
USE_SSL = True
def __init__(self, host: str = DEFAULT_HOST, use_ssl: bool = USE_SSL) -> None:
def __init__(
self,
host: str = DEFAULT_HOST,
use_ssl: bool = USE_SSL,
verify_certificate: bool = True,
) -> None:
super().__init__(
host=host,
use_ssl=use_ssl,
verify_certificate=verify_certificate,
)
self.account = Account()
+3 -5
View File
@@ -58,13 +58,12 @@ class SpeckleClient:
DEFAULT_HOST = "speckle.xyz"
USE_SSL = True
VERIFY_CERTIFICATE = True
def __init__(
self,
host: str = DEFAULT_HOST,
use_ssl: bool = USE_SSL,
verify_certificate: bool = VERIFY_CERTIFICATE,
verify_certificate: bool = True,
) -> None:
ws_protocol = "ws"
http_protocol = "http"
@@ -124,8 +123,7 @@ class SpeckleClient:
Arguments:
token {str} -- an api token
"""
self.authenticate_with_token(token)
self._set_up_client()
self.authenticate_with_account(get_account_from_token(token))
def authenticate_with_token(self, token: str) -> None:
"""
@@ -136,7 +134,7 @@ class SpeckleClient:
Arguments:
token {str} -- an api token
"""
self.account = get_account_from_token(token, self.url)
self.account = Account.from_token(token, self.url)
self._set_up_client()
def authenticate_with_account(self, account: Account) -> None:
+1
View File
@@ -196,3 +196,4 @@ class ServerInfo(BaseModel):
scopes: Optional[List[dict]] = None
authStrategies: Optional[List[dict]] = None
version: Optional[str] = None
frontend2: Optional[bool] = None
@@ -4,6 +4,7 @@ from gql import gql
from specklepy.core.api.models import Branch
from specklepy.core.api.resource import ResourceBase
from specklepy.logging.exceptions import SpeckleException
NAME = "branch"
@@ -39,6 +40,8 @@ class Resource(ResourceBase):
}
"""
)
if len(name) < 3:
return SpeckleException(message="Branch Name must be at least 3 characters")
params = {
"branch": {
"streamId": stream_id,
+14 -1
View File
@@ -1,6 +1,7 @@
import re
from typing import Any, Dict, List, Tuple
import requests
from gql import gql
from specklepy.core.api.models import ServerInfo
@@ -56,9 +57,21 @@ class Resource(ResourceBase):
"""
)
return self.make_request(
server_info = self.make_request(
query=query, return_type="serverInfo", schema=ServerInfo
)
if isinstance(server_info, ServerInfo) and isinstance(
server_info.canonicalUrl, str
):
r = requests.get(
server_info.canonicalUrl, headers={"User-Agent": "specklepy SDK"}
)
if "x-speckle-frontend-2" in r.headers:
server_info.frontend2 = True
else:
server_info.frontend2 = False
return server_info
def version(self) -> Tuple[Any, ...]:
"""Get the server version
+8 -7
View File
@@ -166,7 +166,8 @@ class Resource(ResourceBase):
}
"""
)
if len(name) < 3 and len(name) != 0:
return SpeckleException(message="Stream Name must be at least 3 characters")
params = {
"stream": {"name": name, "description": description, "isPublic": is_public}
}
@@ -730,13 +731,13 @@ class Resource(ResourceBase):
"stream_id": stream_id,
"limit": limit,
"action_type": action_type,
"before": before.astimezone(timezone.utc).isoformat()
if before
else before,
"before": (
before.astimezone(timezone.utc).isoformat() if before else before
),
"after": after.astimezone(timezone.utc).isoformat() if after else after,
"cursor": cursor.astimezone(timezone.utc).isoformat()
if cursor
else cursor,
"cursor": (
cursor.astimezone(timezone.utc).isoformat() if cursor else cursor
),
}
except AttributeError as e:
raise SpeckleException(
+91 -34
View File
@@ -1,4 +1,4 @@
from urllib.parse import unquote, urlparse
from urllib.parse import quote, unquote, urlparse
from warnings import warn
from gql import gql
@@ -47,6 +47,7 @@ class StreamWrapper:
commit_id: str = None
object_id: str = None
branch_name: str = None
model_id: str = None
_client: SpeckleClient = None
_account: Account = None
@@ -86,45 +87,58 @@ class StreamWrapper:
# check for fe2 URL
if "/projects/" in parsed.path:
use_fe2 = True
key_stream = "project"
else:
use_fe2 = False
key_stream = "stream"
while segments:
segment = segments.pop(0)
if segments and (
(use_fe2 is False and segment.lower() == "streams")
or (use_fe2 is True and segment.lower() == "projects")
):
self.stream_id = segments.pop(0)
elif segments and segment.lower() == "commits":
self.commit_id = segments.pop(0)
elif segments and (
(use_fe2 is False and segment.lower() == "branches")
or (use_fe2 is True and segment.lower() == "models")
):
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:
if use_fe2 is False:
if segments and segment.lower() == "streams":
self.stream_id = segments.pop(0)
elif segments and segment.lower() == "commits":
self.commit_id = segments.pop(0)
else:
raise SpeckleException(
f"Cannot parse {url} into a stream wrapper class - invalid URL"
" provided."
)
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."
)
elif segments and use_fe2 is True:
if segment.lower() == "projects":
self.stream_id = segments.pop(0)
elif segment.lower() == "models":
next_segment = segments.pop(0)
if "," in next_segment:
raise SpeckleException("Multi-model urls are not supported yet")
elif unquote(next_segment).startswith("$"):
raise SpeckleException(
"Federation model urls are not supported"
)
elif len(next_segment) == 32:
self.object_id = next_segment
else:
self.branch_name = unquote(next_segment).split("@")[0]
if "@" in unquote(next_segment):
self.commit_id = unquote(next_segment).split("@")[1]
else:
raise SpeckleException(
f"Cannot parse {url} into a stream wrapper class - invalid URL"
" provided."
)
if use_fe2 is True and self.branch_name is not None:
if "," in self.branch_name:
raise SpeckleException("Multi-model urls are not supported yet")
if "@" in self.branch_name:
model_id = self.branch_name.split("@")[0]
self.commit_id = self.branch_name.split("@")[1]
else:
model_id = self.branch_name
self.model_id = self.branch_name
# get branch name
query = gql(
"""
@@ -139,7 +153,7 @@ class StreamWrapper:
"""
)
self._client = self.get_client()
params = {"project_id": self.stream_id, "model_id": model_id}
params = {"project_id": self.stream_id, "model_id": self.model_id}
project = self._client.httpclient.execute(query, params)
try:
@@ -149,7 +163,7 @@ class StreamWrapper:
if not self.stream_id:
raise SpeckleException(
f"Cannot parse {url} into a stream wrapper class - no stream id found."
f"Cannot parse {url} into a stream wrapper class - no {key_stream} id found."
)
@property
@@ -230,3 +244,46 @@ class StreamWrapper:
if not self._account or not self._account.token:
self.get_account(token)
return ServerTransport(self.stream_id, account=self._account)
def to_string(self) -> str:
"""
Constructs a URL depending on the StreamWrapper type and FE version.
"""
use_fe2 = False
key_streams = "/streams/"
key_branches = "/branches/"
if isinstance(self.branch_name, str):
value_branch = quote(self.branch_name)
if self.branch_name == "globals":
key_branches = "/"
key_commits = "/commits/"
if isinstance(self.commit_id, str) and self.branch_name == "globals":
key_commits = "/globals/"
key_objects = "/objects/"
if "/projects/" in self.stream_url:
use_fe2 = True
key_streams = "/projects/"
key_branches = "/models/"
value_branch = self.model_id
key_commits = "@"
key_objects = "/models/"
wrapper_type = self.type
if use_fe2 is False or (use_fe2 is True and not self.model_id):
base_url = f"{self.server_url}{key_streams}{self.stream_id}"
else: # fe2 is True and model_id available
base_url = f"{self.server_url}{key_streams}{self.stream_id}{key_branches}{value_branch}"
if wrapper_type == "object":
return f"{base_url}{key_objects}{self.object_id}"
elif wrapper_type == "commit":
return f"{base_url}{key_commits}{self.commit_id}"
elif wrapper_type == "branch":
return f"{self.server_url}{key_streams}{self.stream_id}{key_branches}{value_branch}"
elif wrapper_type == "stream":
return f"{self.server_url}{key_streams}{self.stream_id}"
else:
raise SpeckleException(
f"Cannot parse StreamWrapper of type '{wrapper_type}'"
)
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Optional
from specklepy.objects import Base
from specklepy.objects.base import Base
class CRS(Base, speckle_type="Objects.GIS.CRS"):
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import List, Optional, Union
from specklepy.objects import Base
from specklepy.objects.base import Base
from specklepy.objects.geometry import (
Arc,
Circle,
+3 -7
View File
@@ -13,13 +13,13 @@ class Layer(Base, detachable={"features"}):
def __init__(
self,
name: str = None,
crs: CRS = None,
name: Optional[str] = None,
crs: Optional[CRS] = None,
units: str = "m",
features: Optional[List[Base]] = None,
layerType: str = "None",
geomType: str = "None",
renderer: Optional[dict[str, Any]] = None,
renderer: Optional[Dict[str, Any]] = None,
**kwargs,
) -> None:
super().__init__(**kwargs)
@@ -39,7 +39,6 @@ class VectorLayer(
speckle_type="VectorLayer",
serialize_ignore={"features"},
):
"""GIS Vector Layer"""
name: Optional[str] = None
@@ -68,7 +67,6 @@ class RasterLayer(
speckle_type="RasterLayer",
serialize_ignore={"features"},
):
"""GIS Raster Layer"""
name: Optional[str] = None
@@ -96,7 +94,6 @@ class VectorLayer( # noqa: F811
speckle_type="Objects.GIS.VectorLayer",
serialize_ignore={"features"},
):
"""GIS Vector Layer"""
name: Optional[str] = None
@@ -124,7 +121,6 @@ class RasterLayer( # noqa: F811
speckle_type="Objects.GIS.RasterLayer",
serialize_ignore={"features"},
):
"""GIS Raster Layer"""
name: Optional[str] = None
+19 -2
View File
@@ -1,6 +1,23 @@
"""Builtin Speckle object kit."""
from specklepy.objects import encoding, geometry, other, primitive, structural, units
from specklepy.objects import (
GIS,
encoding,
geometry,
other,
primitive,
structural,
units,
)
from specklepy.objects.base import Base
__all__ = ["Base", "encoding", "geometry", "other", "units", "structural", "primitive"]
__all__ = [
"Base",
"encoding",
"geometry",
"other",
"units",
"structural",
"primitive",
"GIS",
]
+2 -1
View File
@@ -188,7 +188,8 @@ class _RegisteringBase:
cls._detachable = cls._detachable.union(detachable)
if serialize_ignore:
cls._serialize_ignore = cls._serialize_ignore.union(serialize_ignore)
super().__init_subclass__(**kwargs)
# we know, that the super here is object, that takes no args on init subclass
return super().__init_subclass__()
# T = TypeVar("T")
+9 -9
View File
@@ -303,15 +303,15 @@ class Polyline(Base, speckle_type=GEOMETRY + "Polyline", chunkable={"value": 200
class SpiralType(Enum):
Biquadratic = (0,)
BiquadraticParabola = (1,)
Bloss = (2,)
Clothoid = (3,)
Cosine = (4,)
Cubic = (5,)
CubicParabola = (6,)
Radioid = (7,)
Sinusoid = (8,)
Biquadratic = 0
BiquadraticParabola = 1
Bloss = 2
Clothoid = 3
Cosine = 4
Cubic = 5
CubicParabola = 6
Radioid = 7
Sinusoid = 8
Unknown = 9
@@ -3,7 +3,7 @@ from typing import Any, Callable, Collection, Iterable, Iterator, List, Optional
from attrs import define
from typing_extensions import Protocol, final
from specklepy.objects import Base
from specklepy.objects.base import Base
class ITraversalRule(Protocol):
+12 -8
View File
@@ -73,7 +73,7 @@ class ServerTransport(AbstractTransport):
warn(
SpeckleWarning(
"Unauthenticated Speckle Client provided to Server Transport"
f" for {self.url}. Receiving from private streams will fail."
f" for {url}. Receiving from private streams will fail."
)
)
else:
@@ -84,14 +84,18 @@ class ServerTransport(AbstractTransport):
self.stream_id = stream_id
self.url = url
self._batch_sender = BatchSender(
self.url, self.stream_id, self.account.token, max_batch_size_mb=1
)
self.session = requests.Session()
self.session.headers.update(
{"Authorization": f"Bearer {self.account.token}", "Accept": "text/plain"}
)
if self.account is not None:
self._batch_sender = BatchSender(
self.url, self.stream_id, self.account.token, max_batch_size_mb=1
)
self.session.headers.update(
{
"Authorization": f"Bearer {self.account.token}",
"Accept": "text/plain",
}
)
@property
def name(self) -> str:
@@ -18,6 +18,7 @@ class TestServer:
server = client.server.get()
assert isinstance(server, ServerInfo)
assert isinstance(server.frontend2, bool)
def test_server_version(self, client: SpeckleClient):
version = client.server.version()
@@ -44,6 +44,14 @@ class TestStream:
assert isinstance(stream.id, str)
def test_stream_create_short_name(self, client, stream, updated_stream):
new_stream_id = client.stream.create(
name="x",
description=stream.description,
is_public=stream.isPublic,
)
assert isinstance(new_stream_id, SpeckleException)
def test_stream_get(self, client, stream):
fetched_stream = client.stream.get(stream.id)
@@ -2,11 +2,13 @@ import json
import tempfile
from pathlib import Path
from typing import Iterable
from urllib.parse import unquote
import pytest
from specklepy.api.wrapper import StreamWrapper
from specklepy.core.helpers import speckle_path_provider
from specklepy.logging.exceptions import SpeckleException
@pytest.fixture(scope="module", autouse=True)
@@ -29,6 +31,22 @@ def user_path() -> Iterable[Path]:
speckle_path_provider.override_application_data_path(None)
def test_parse_empty():
try:
StreamWrapper("https://testing.speckle.dev/streams")
assert False
except SpeckleException:
assert True
def test_parse_empty_fe2():
try:
StreamWrapper("https://latest.speckle.systems/projects")
assert False
except SpeckleException:
assert True
def test_parse_stream():
wrap = StreamWrapper("https://testing.speckle.dev/streams/a75ab4f10f")
assert wrap.type == "stream"
@@ -142,8 +160,56 @@ def test_parse_model():
assert wrap.type == "branch"
def test_parse_federated_model():
try:
StreamWrapper("https://latest.speckle.systems/projects/843d07eb10/models/$main")
assert False
except SpeckleException:
assert True
def test_parse_multi_model():
try:
StreamWrapper(
"https://latest.speckle.systems/projects/2099ac4b5f/models/1870f279e3,a9cfdddc79"
)
assert False
except SpeckleException:
assert True
def test_parse_object_fe2():
wrap = StreamWrapper(
"https://latest.speckle.systems/projects/24c3741255/models/b48d1b10f5a732f4ca4144286391282c"
)
assert wrap.type == "object"
def test_parse_version():
wrap = StreamWrapper(
"https://latest.speckle.systems/projects/843d07eb10/models/4e7345c838@c42d5cbac1"
)
wrap_quoted = StreamWrapper(
"https://latest.speckle.systems/projects/843d07eb10/models/4e7345c838%40c42d5cbac1"
)
assert wrap.type == "commit"
assert wrap_quoted.type == "commit"
def test_to_string():
urls = [
"https://testing.speckle.dev/streams/a75ab4f10f",
"https://testing.speckle.dev/streams/4c3ce1459c/branches/%F0%9F%8D%95%E2%AC%85%F0%9F%8C%9F%20you%20wat%3F",
"https://testing.speckle.dev/streams/0c6ad366c4/globals",
"https://testing.speckle.dev/streams/0c6ad366c4/globals/abd3787893",
"https://testing.speckle.dev/streams/4c3ce1459c/commits/8b9b831792",
"https://testing.speckle.dev/streams/a75ab4f10f/objects/5530363e6d51c904903dafc3ea1d2ec6",
"https://latest.speckle.systems/projects/843d07eb10",
"https://latest.speckle.systems/projects/843d07eb10/models/4e7345c838",
"https://latest.speckle.systems/projects/843d07eb10/models/4e7345c838@c42d5cbac1",
"https://latest.speckle.systems/projects/843d07eb10/models/4e7345c838%40c42d5cbac1",
"https://latest.speckle.systems/projects/24c3741255/models/b48d1b10f5a732f4ca4144286391282c",
]
for url in urls:
wrap = StreamWrapper(url)
assert unquote(wrap.to_string()) == unquote(url)