Compare commits
45 Commits
2.15.2
...
gergo/fixTest
| Author | SHA1 | Date | |
|---|---|---|---|
| 13e0b65c31 | |||
| 1e6321c7f1 | |||
| b5fb684864 | |||
| 65048cd01b | |||
| 9d2fd5bc42 | |||
| bd35fb59c3 | |||
| 4931c95d7c | |||
| 52d53db661 | |||
| 23ee28f851 | |||
| 791190a38c | |||
| 3c7feb0bec | |||
| 2b583fd822 | |||
| 8244e3ecc7 | |||
| 5ac9d80cbc | |||
| 5e2fbaa7c2 | |||
| 703ceaf369 | |||
| a5096c41ca | |||
| 972339454d | |||
| 34c11d5931 | |||
| 854ce9f77f | |||
| 7f926cf547 | |||
| 5e8b54e3b7 | |||
| 8bd46e4e64 | |||
| 91edd4f85b | |||
| 0cb6c7f682 | |||
| 125a4bbeed | |||
| 76c4074aed | |||
| 16164a57da | |||
| 3a225fa935 | |||
| 102850b894 | |||
| 5ac85c541b | |||
| cca7b18119 | |||
| 8a34b95128 | |||
| 46d7abbaee | |||
| 67e95caf5f | |||
| 04532ed645 | |||
| 7df175d9bb | |||
| 3912fa8860 | |||
| 34de2928ae | |||
| ec651a9237 | |||
| edf2afaa89 | |||
| e0b1b272c0 | |||
| 682e82057e | |||
| 473e5cfddb | |||
| 03cd989165 |
Generated
+716
-640
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -15,12 +15,13 @@ packages = [
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.7.2, <4.0"
|
||||
pydantic = "^1.9"
|
||||
pydantic = "^2.0"
|
||||
appdirs = "^1.4.4"
|
||||
gql = {extras = ["requests", "websockets"], version = "^3.3.0"}
|
||||
ujson = "^5.3.0"
|
||||
Deprecated = "^1.2.13"
|
||||
stringcase = "^1.2.0"
|
||||
attrs = "^23.1.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^22.8.0"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
|
||||
@@ -57,7 +58,7 @@ def get_local_accounts(base_path: Optional[str] = None) -> List[Account]:
|
||||
res = account_storage.get_all_objects()
|
||||
account_storage.close()
|
||||
if res:
|
||||
accounts.extend(Account.parse_raw(r[1]) for r in res)
|
||||
accounts.extend(Account.model_validate_json(r[1]) for r in res)
|
||||
except SpeckleException:
|
||||
# cannot open SQLiteTransport, probably because of the lack
|
||||
# of disk write permissions
|
||||
@@ -78,7 +79,8 @@ def get_local_accounts(base_path: Optional[str] = None) -> List[Account]:
|
||||
if json_acct_files:
|
||||
try:
|
||||
accounts.extend(
|
||||
Account.parse_file(os.path.join(json_path, json_file))
|
||||
Account.model_validate_json(Path(json_path, json_file).read_text())
|
||||
# Account.parse_file(os.path.join(json_path, json_file))
|
||||
for json_file in json_acct_files
|
||||
)
|
||||
except Exception as ex:
|
||||
|
||||
@@ -5,24 +5,24 @@ from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Collaborator(BaseModel):
|
||||
id: Optional[str]
|
||||
name: Optional[str]
|
||||
role: Optional[str]
|
||||
avatar: Optional[str]
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
|
||||
|
||||
class Commit(BaseModel):
|
||||
id: Optional[str]
|
||||
message: Optional[str]
|
||||
authorName: Optional[str]
|
||||
authorId: Optional[str]
|
||||
authorAvatar: Optional[str]
|
||||
branchName: Optional[str]
|
||||
createdAt: Optional[datetime]
|
||||
sourceApplication: Optional[str]
|
||||
referencedObject: Optional[str]
|
||||
totalChildrenCount: Optional[int]
|
||||
parents: Optional[List[str]]
|
||||
id: Optional[str] = None
|
||||
message: Optional[str] = None
|
||||
authorName: Optional[str] = None
|
||||
authorId: Optional[str] = None
|
||||
authorAvatar: Optional[str] = None
|
||||
branchName: Optional[str] = None
|
||||
createdAt: Optional[datetime] = None
|
||||
sourceApplication: Optional[str] = None
|
||||
referencedObject: Optional[str] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
parents: Optional[List[str]] = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@@ -36,35 +36,35 @@ class Commit(BaseModel):
|
||||
|
||||
|
||||
class Commits(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
cursor: Optional[datetime]
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Commit] = []
|
||||
|
||||
|
||||
class Object(BaseModel):
|
||||
id: Optional[str]
|
||||
speckleType: Optional[str]
|
||||
applicationId: Optional[str]
|
||||
totalChildrenCount: Optional[int]
|
||||
createdAt: Optional[datetime]
|
||||
id: Optional[str] = None
|
||||
speckleType: Optional[str] = None
|
||||
applicationId: Optional[str] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
createdAt: Optional[datetime] = None
|
||||
|
||||
|
||||
class Branch(BaseModel):
|
||||
id: Optional[str]
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
commits: Optional[Commits]
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
commits: Optional[Commits] = None
|
||||
|
||||
|
||||
class Branches(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
cursor: Optional[datetime]
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Branch] = []
|
||||
|
||||
|
||||
class Stream(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str]
|
||||
name: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
isPublic: Optional[bool] = None
|
||||
description: Optional[str] = None
|
||||
@@ -89,21 +89,21 @@ class Stream(BaseModel):
|
||||
|
||||
|
||||
class Streams(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
cursor: Optional[datetime]
|
||||
totalCount: Optional[int] = None
|
||||
cursor: Optional[datetime] = None
|
||||
items: List[Stream] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: Optional[str]
|
||||
email: Optional[str]
|
||||
name: Optional[str]
|
||||
bio: Optional[str]
|
||||
company: Optional[str]
|
||||
avatar: Optional[str]
|
||||
verified: Optional[bool]
|
||||
role: Optional[str]
|
||||
streams: Optional[Streams]
|
||||
id: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
verified: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
streams: Optional[Streams] = None
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
@@ -119,24 +119,24 @@ class LimitedUser(BaseModel):
|
||||
"""Limited user type, for showing public info about a user to another user."""
|
||||
|
||||
id: str
|
||||
name: Optional[str]
|
||||
bio: Optional[str]
|
||||
company: Optional[str]
|
||||
avatar: Optional[str]
|
||||
verified: Optional[bool]
|
||||
role: Optional[str]
|
||||
name: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
company: Optional[str] = None
|
||||
avatar: Optional[str] = None
|
||||
verified: Optional[bool] = None
|
||||
role: Optional[str] = None
|
||||
|
||||
|
||||
class PendingStreamCollaborator(BaseModel):
|
||||
id: Optional[str]
|
||||
inviteId: Optional[str]
|
||||
streamId: Optional[str]
|
||||
streamName: Optional[str]
|
||||
title: Optional[str]
|
||||
role: Optional[str]
|
||||
invitedBy: Optional[User]
|
||||
user: Optional[User]
|
||||
token: Optional[str]
|
||||
id: Optional[str] = None
|
||||
inviteId: Optional[str] = None
|
||||
streamId: Optional[str] = None
|
||||
streamName: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
invitedBy: Optional[User] = None
|
||||
user: Optional[User] = None
|
||||
token: Optional[str] = None
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
@@ -150,14 +150,14 @@ class PendingStreamCollaborator(BaseModel):
|
||||
|
||||
|
||||
class Activity(BaseModel):
|
||||
actionType: Optional[str]
|
||||
info: Optional[dict]
|
||||
userId: Optional[str]
|
||||
streamId: Optional[str]
|
||||
resourceId: Optional[str]
|
||||
resourceType: Optional[str]
|
||||
message: Optional[str]
|
||||
time: Optional[datetime]
|
||||
actionType: Optional[str] = None
|
||||
info: Optional[dict] = None
|
||||
userId: Optional[str] = None
|
||||
streamId: Optional[str] = None
|
||||
resourceId: Optional[str] = None
|
||||
resourceType: Optional[str] = None
|
||||
message: Optional[str] = None
|
||||
time: Optional[datetime] = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@@ -170,9 +170,9 @@ class Activity(BaseModel):
|
||||
|
||||
|
||||
class ActivityCollection(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
items: Optional[List[Activity]]
|
||||
cursor: Optional[datetime]
|
||||
totalCount: Optional[int] = None
|
||||
items: Optional[List[Activity]] = None
|
||||
cursor: Optional[datetime] = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
|
||||
@@ -50,10 +50,10 @@ class ResourceBase(object):
|
||||
if isinstance(response, list):
|
||||
return [self._parse_response(response=r, schema=schema) for r in response]
|
||||
if schema:
|
||||
return schema.parse_obj(response)
|
||||
return schema.model_validate(response)
|
||||
elif self.schema:
|
||||
try:
|
||||
return self.schema.parse_obj(response)
|
||||
return self.schema.model_validate(response)
|
||||
except Exception:
|
||||
s = BaseObjectSerializer(read_transport=SQLiteTransport())
|
||||
return s.recompose_base(response)
|
||||
|
||||
@@ -23,7 +23,8 @@ LOG = logging.getLogger(__name__)
|
||||
METRICS_TRACKER = None
|
||||
|
||||
# actions
|
||||
SDK = "SDK Actions"
|
||||
SDK = "SDK Action"
|
||||
CONNECTOR = "Connector Action"
|
||||
RECEIVE = "Receive"
|
||||
SEND = "Send"
|
||||
|
||||
|
||||
@@ -5,26 +5,12 @@ from specklepy.objects import Base
|
||||
class CRS(Base, speckle_type="Objects.GIS.CRS"):
|
||||
"""A Coordinate Reference System stored in wkt format"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
authority_id: Optional[str] = None,
|
||||
wkt: Optional[str] = None,
|
||||
units: Optional[str] = None,
|
||||
units_native: Optional[str] = None,
|
||||
offset_x: Optional[float] = None,
|
||||
offset_y: Optional[float] = None,
|
||||
rotation: Optional[float] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
name: Optional[str] = None
|
||||
authority_id: Optional[str] = None
|
||||
wkt: Optional[str] = None
|
||||
units_native: Optional[str] = None
|
||||
offset_x: Optional[float] = None
|
||||
offset_y: Optional[float] = None
|
||||
rotation: Optional[float] = None
|
||||
|
||||
self.name = name
|
||||
self.authority_id = authority_id
|
||||
self.wkt = wkt
|
||||
self.units = units or "m"
|
||||
self.units_native = units_native
|
||||
self.offset_x = offset_x
|
||||
self.offset_y = offset_y
|
||||
self.rotation = rotation
|
||||
|
||||
|
||||
@@ -6,99 +6,48 @@ from deprecated import deprecated
|
||||
|
||||
class GisPolygonGeometry(Base, speckle_type="Objects.GIS.PolygonGeometry", detachable={"displayValue"}):
|
||||
"""GIS Polygon Geometry"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
boundary: Optional[Union[Polyline, Arc, Line, Circle, Polycurve]] = None,
|
||||
voids: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]] ] = None,
|
||||
displayValue: Optional[List[Mesh]] = None,
|
||||
units: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.boundary = boundary
|
||||
self.voids = voids
|
||||
self.displayValue = displayValue
|
||||
self.units = units or "m"
|
||||
boundary: Optional[Union[Polyline, Arc, Line, Circle, Polycurve]] = None
|
||||
voids: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]] ] = None
|
||||
displayValue: Optional[List[Mesh]] = None
|
||||
|
||||
class GisPolygonElement(Base, speckle_type="Objects.GIS.PolygonElement"):
|
||||
"""GIS Polygon element"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry: Optional[List[GisPolygonGeometry]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
units: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.geometry = geometry
|
||||
self.attributes = attributes
|
||||
self.units = units or "m"
|
||||
geometry: Optional[List[GisPolygonGeometry]] = None
|
||||
attributes: Optional[Base] = None
|
||||
|
||||
class GisLineElement(Base, speckle_type="Objects.GIS.LineElement"):
|
||||
"""GIS Polyline element"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
units: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.geometry = geometry
|
||||
self.attributes = attributes
|
||||
self.units = units or "m"
|
||||
|
||||
geometry: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
|
||||
class GisPointElement(Base, speckle_type="Objects.GIS.PointElement"):
|
||||
"""GIS Point element"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
geometry: Optional[List[Point]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
units: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.geometry = geometry
|
||||
self.attributes = attributes
|
||||
self.units = units or "m"
|
||||
|
||||
geometry: Optional[List[Point]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
|
||||
class GisRasterElement(Base, speckle_type="Objects.GIS.RasterElement", detachable={"displayValue"}):
|
||||
"""GIS Raster element"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
band_count: Optional[int] = None,
|
||||
band_names: Optional[List[str]] = None,
|
||||
x_origin: Optional[float] = None,
|
||||
y_origin: Optional[float] = None,
|
||||
x_size: Optional[int] = None,
|
||||
y_size: Optional[int] = None,
|
||||
x_resolution: Optional[float] = None,
|
||||
y_resolution: Optional[float] = None,
|
||||
noDataValue: Optional[List[float]] = None,
|
||||
displayValue: Optional[List[Mesh]] = None,
|
||||
units: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.band_count = band_count
|
||||
self.band_names = band_names
|
||||
self.x_origin = x_origin
|
||||
self.y_origin = y_origin
|
||||
self.x_size = x_size
|
||||
self.y_size = y_size
|
||||
self.x_resolution = x_resolution
|
||||
self.y_resolution = y_resolution
|
||||
self.noDataValue = noDataValue
|
||||
self.displayValue = displayValue
|
||||
self.units = units or "m"
|
||||
band_count: Optional[int] = None
|
||||
band_names: Optional[List[str]] = None
|
||||
x_origin: Optional[float] = None
|
||||
y_origin: Optional[float] = None
|
||||
x_size: Optional[int] = None
|
||||
y_size: Optional[int] = None
|
||||
x_resolution: Optional[float] = None
|
||||
y_resolution: Optional[float] = None
|
||||
noDataValue: Optional[List[float]] = None
|
||||
displayValue: Optional[List[Mesh]] = None
|
||||
|
||||
class GisTopography(GisRasterElement, speckle_type="Objects.GIS.GisTopography", detachable={"displayValue"}):
|
||||
"""GIS Raster element with 3d Topography representation"""
|
||||
|
||||
class GisNonGeometryElement(Base, speckle_type="Objects.GIS.NonGeometryElement"):
|
||||
"""GIS Table feature"""
|
||||
|
||||
attributes: Optional[Base] = None
|
||||
|
||||
|
||||
@@ -28,29 +28,22 @@ class Layer(Base, detachable={"features"}):
|
||||
self.geomType = geomType
|
||||
self.renderer = renderer or {}
|
||||
|
||||
class VectorLayer(Collection, detachable={"elements"}, speckle_type="Objects.GIS.VectorLayer", serialize_ignore={"features"}):
|
||||
class VectorLayer(
|
||||
Collection,
|
||||
detachable={"elements"},
|
||||
speckle_type="Objects.GIS.VectorLayer",
|
||||
serialize_ignore={"features"}):
|
||||
|
||||
"""GIS Vector Layer"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str]=None,
|
||||
crs: Optional[CRS]=None,
|
||||
units: Optional[str] = None,
|
||||
elements: Optional[List[Base]] = None,
|
||||
attributes: Optional[Base] = None,
|
||||
geomType: Optional[str] = None,
|
||||
renderer: Optional[Dict[str, Any]] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.name = name or ""
|
||||
self.crs = crs
|
||||
self.units = units
|
||||
self.elements = elements or []
|
||||
self.attributes = attributes
|
||||
self.geomType = geomType or "None"
|
||||
self.renderer = renderer or {}
|
||||
self.collectionType = "VectorLayer"
|
||||
name: Optional[str]=None
|
||||
crs: Optional[CRS]=None
|
||||
units: Optional[str] = None
|
||||
elements: Optional[List[Base]] = None
|
||||
attributes: Optional[Base] = None
|
||||
geomType: Optional[str] = "None"
|
||||
renderer: Optional[Dict[str, Any]] = None
|
||||
collectionType = "VectorLayer"
|
||||
|
||||
@property
|
||||
@deprecated(version="2.14", reason="Use elements")
|
||||
@@ -61,29 +54,23 @@ class VectorLayer(Collection, detachable={"elements"}, speckle_type="Objects.GIS
|
||||
def features(self, value: Optional[List[Base]]) -> None:
|
||||
self.elements = value
|
||||
|
||||
class RasterLayer(Collection, detachable={"elements"}, speckle_type="Objects.GIS.RasterLayer", serialize_ignore={"features"}):
|
||||
class RasterLayer(
|
||||
Collection,
|
||||
detachable={"elements"},
|
||||
speckle_type="Objects.GIS.RasterLayer",
|
||||
serialize_ignore={"features"}):
|
||||
|
||||
"""GIS Raster Layer"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
crs: Optional[CRS]=None,
|
||||
units: Optional[str] = None,
|
||||
rasterCrs: Optional[CRS]=None,
|
||||
elements: Optional[List[Base]] = None,
|
||||
geomType: Optional[str] = None,
|
||||
renderer: Optional[Dict[str, Any]] = None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.name = name or ""
|
||||
self.crs = crs
|
||||
self.units = units
|
||||
self.rasterCrs = rasterCrs
|
||||
self.elements = elements or []
|
||||
self.geomType = geomType or "None"
|
||||
self.renderer = renderer or {}
|
||||
self.collectionType = "RasterLayer"
|
||||
name: Optional[str] = None
|
||||
crs: Optional[CRS]=None
|
||||
units: Optional[str] = None
|
||||
rasterCrs: Optional[CRS]=None
|
||||
elements: Optional[List[Base]] = None
|
||||
geomType: Optional[str] = "None"
|
||||
renderer: Optional[Dict[str, Any]] = None
|
||||
collectionType = "RasterLayer"
|
||||
|
||||
|
||||
@property
|
||||
@deprecated(version="2.14", reason="Use elements")
|
||||
|
||||
@@ -18,7 +18,7 @@ from warnings import warn
|
||||
|
||||
from stringcase import pascalcase
|
||||
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
|
||||
from specklepy.objects.units import Units, get_units_from_string
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
|
||||
@@ -322,7 +322,7 @@ class Base(_RegisteringBase):
|
||||
id: Union[str, None] = None
|
||||
totalChildrenCount: Union[int, None] = None
|
||||
applicationId: Union[str, None] = None
|
||||
_units: Union[Units, None] = None
|
||||
_units: Union[None, str] = None
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__()
|
||||
@@ -463,22 +463,19 @@ class Base(_RegisteringBase):
|
||||
|
||||
@property
|
||||
def units(self) -> Union[str, None]:
|
||||
if self._units:
|
||||
return self._units.value
|
||||
return None
|
||||
return self._units
|
||||
|
||||
@units.setter
|
||||
def units(self, value: Union[str, Units, None]):
|
||||
if value is None:
|
||||
units = value
|
||||
"""While this property accepts any string value, geometry expects units to be specific strings (see Units enum)"""
|
||||
if isinstance(value, str) or value is None:
|
||||
self._units = value
|
||||
elif isinstance(value, Units):
|
||||
units: Units = value
|
||||
self._units = value.value
|
||||
else:
|
||||
units = get_units_from_string(value)
|
||||
self._units = units
|
||||
# except SpeckleInvalidUnitException as ex:
|
||||
# warn(f"Units are reset to None. Reason {ex.message}")
|
||||
# self._units = None
|
||||
raise SpeckleInvalidUnitException(
|
||||
f"Unknown type {type(value)} received for units"
|
||||
)
|
||||
|
||||
def get_member_names(self) -> List[str]:
|
||||
"""Get all of the property names on this object, dynamic or not"""
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Collection, Dict, Generic, Iterable, List, Optional, Tuple, TypeVar
|
||||
from attrs import define
|
||||
from specklepy.objects.base import Base
|
||||
|
||||
ROOT: str = "__Root"
|
||||
|
||||
T = TypeVar('T')
|
||||
PARENT_INFO = Tuple[Optional[str], str]
|
||||
|
||||
@define(slots=True)
|
||||
class CommitObjectBuilder(ABC, Generic[T]):
|
||||
|
||||
converted: Dict[str, Base]
|
||||
_parent_infos: Dict[str, Collection[PARENT_INFO]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.converted = {}
|
||||
self._parent_infos = {}
|
||||
|
||||
@abstractmethod
|
||||
def include_object(self, conversion_result: Base, native_object: T) -> None:
|
||||
pass
|
||||
|
||||
def build_commit_object(self, root_commit_object: Base) -> None:
|
||||
self.apply_relationships(self.converted.values(), root_commit_object)
|
||||
|
||||
def set_relationship(self, app_id: Optional[str], *parent_info : PARENT_INFO) -> None:
|
||||
|
||||
if not app_id:
|
||||
return
|
||||
|
||||
self._parent_infos[app_id] = parent_info
|
||||
|
||||
def apply_relationships(self, to_add: Iterable[Base], root_commit_object: Base) -> None:
|
||||
for c in to_add:
|
||||
try:
|
||||
self.apply_relationship(c, root_commit_object)
|
||||
except Exception as ex:
|
||||
print(f"Failed to add object {type(c)} to commit object: {ex}")
|
||||
|
||||
def apply_relationship(self, current: Base, root_commit_object: Base):
|
||||
if not current.applicationId: raise Exception(f"Expected applicationId to have been set")
|
||||
|
||||
parents = self._parent_infos[current.applicationId]
|
||||
|
||||
for (parent_id, prop_name) in parents:
|
||||
if not parent_id: continue
|
||||
|
||||
parent: Optional[Base]
|
||||
if parent_id == ROOT:
|
||||
parent = root_commit_object
|
||||
else:
|
||||
parent = self.converted[parent_id] if parent_id in self.converted else None
|
||||
|
||||
if not parent: continue
|
||||
|
||||
try:
|
||||
elements = get_detached_prop(parent, prop_name)
|
||||
if not isinstance(elements, list):
|
||||
elements = []
|
||||
set_detached_prop(parent, prop_name, elements)
|
||||
|
||||
elements.append(current)
|
||||
return
|
||||
except Exception as ex:
|
||||
# A parent was found, but it was invalid (Likely because of a type mismatch on a `elements` property)
|
||||
print(f"Failed to add object {type(current)} to a converted parent; {ex}")
|
||||
|
||||
raise Exception(f"Could not find a valid parent for object of type {type(current)}. Checked {len(parents)} potential parent, and non were converted!")
|
||||
|
||||
|
||||
def get_detached_prop(speckle_object: Base, prop_name: str) -> Optional[Any]:
|
||||
detached_prop_name = get_detached_prop_name(speckle_object, prop_name)
|
||||
return getattr(speckle_object, detached_prop_name, None)
|
||||
|
||||
def set_detached_prop(speckle_object: Base, prop_name: str, value: Optional[Any]) -> None:
|
||||
detached_prop_name = get_detached_prop_name(speckle_object, prop_name)
|
||||
setattr(speckle_object, detached_prop_name, value)
|
||||
|
||||
def get_detached_prop_name(speckle_object: Base, prop_name: str) -> str:
|
||||
return prop_name if hasattr(speckle_object, prop_name) else f"@{prop_name}"
|
||||
@@ -0,0 +1,123 @@
|
||||
from typing import Any, Callable, Collection, Iterable, Iterator, List, Optional, Set
|
||||
|
||||
from attrs import define
|
||||
from typing_extensions import Protocol, final
|
||||
|
||||
from specklepy.objects import Base
|
||||
|
||||
|
||||
class ITraversalRule(Protocol):
|
||||
def get_members_to_traverse(self, o: Base) -> Set[str]:
|
||||
"""Get the members to traverse."""
|
||||
pass
|
||||
|
||||
def does_rule_hold(self, o: Base) -> bool:
|
||||
"""Make sure the rule still holds."""
|
||||
pass
|
||||
|
||||
|
||||
@final
|
||||
@define(slots=True, frozen=True)
|
||||
class DefaultRule:
|
||||
def get_members_to_traverse(self, _) -> Set[str]:
|
||||
return set()
|
||||
|
||||
def does_rule_hold(self, _) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
# we're creating a local protected "singleton"
|
||||
_default_rule = DefaultRule()
|
||||
|
||||
|
||||
@final
|
||||
@define(slots=True, frozen=True)
|
||||
class TraversalContext:
|
||||
current: Base
|
||||
member_name: Optional[str] = None
|
||||
parent: Optional["TraversalContext"] = None
|
||||
|
||||
|
||||
@final
|
||||
@define(slots=True, frozen=True)
|
||||
class GraphTraversal:
|
||||
|
||||
_rules: List[ITraversalRule]
|
||||
|
||||
def traverse(self, root: Base) -> Iterator[TraversalContext]:
|
||||
stack: List[TraversalContext] = []
|
||||
|
||||
stack.append(TraversalContext(root))
|
||||
|
||||
while len(stack) > 0:
|
||||
head = stack.pop()
|
||||
yield head
|
||||
|
||||
current = head.current
|
||||
active_rule = self._get_active_rule_or_default_rule(current)
|
||||
members_to_traverse = active_rule.get_members_to_traverse(current)
|
||||
for child_prop in members_to_traverse:
|
||||
try:
|
||||
if child_prop in {"speckle_type", "units", "applicationId"}: continue #debug: to avoid noisy exceptions, explicitly avoid checking ones we know will fail, this is not exhaustive
|
||||
if getattr(current, child_prop, None):
|
||||
value = current[child_prop]
|
||||
self._traverse_member_to_stack(
|
||||
stack, value, child_prop, head
|
||||
)
|
||||
except KeyError as ex:
|
||||
# Unset application ids, and class variables like SpeckleType will throw when __getitem__ is called
|
||||
pass
|
||||
@staticmethod
|
||||
def _traverse_member_to_stack(
|
||||
stack: List[TraversalContext],
|
||||
value: Any,
|
||||
member_name: Optional[str] = None,
|
||||
parent: Optional[TraversalContext] = None,
|
||||
):
|
||||
if isinstance(value, Base):
|
||||
stack.append(TraversalContext(value, member_name, parent))
|
||||
elif isinstance(value, list):
|
||||
for obj in value:
|
||||
GraphTraversal._traverse_member_to_stack(stack, obj, member_name, parent)
|
||||
elif isinstance(value, dict):
|
||||
for obj in value.values():
|
||||
GraphTraversal._traverse_member_to_stack(stack, obj, member_name, parent)
|
||||
|
||||
@staticmethod
|
||||
def traverse_member(value: Optional[Any]) -> Iterator[Base]:
|
||||
if isinstance(value, Base):
|
||||
yield value
|
||||
elif isinstance(value, list):
|
||||
for obj in value:
|
||||
for o in GraphTraversal.traverse_member(obj):
|
||||
yield o
|
||||
elif isinstance(value, dict):
|
||||
for obj in value.values():
|
||||
for o in GraphTraversal.traverse_member(obj):
|
||||
yield o
|
||||
|
||||
|
||||
def _get_active_rule_or_default_rule(self, o: Base) -> ITraversalRule:
|
||||
return self._get_active_rule(o) or _default_rule
|
||||
|
||||
def _get_active_rule(self, o: Base) -> Optional[ITraversalRule]:
|
||||
for rule in self._rules:
|
||||
if rule.does_rule_hold(o):
|
||||
return rule
|
||||
return None
|
||||
|
||||
|
||||
@final
|
||||
@define(slots=True, frozen=True)
|
||||
class TraversalRule:
|
||||
_conditions: Collection[Callable[[Base], bool]]
|
||||
_members_to_traverse: Callable[[Base], Iterable[str]]
|
||||
|
||||
def get_members_to_traverse(self, o: Base) -> Set[str]:
|
||||
return set(self._members_to_traverse(o))
|
||||
|
||||
def does_rule_hold(self, o: Base) -> bool:
|
||||
for condition in self._conditions:
|
||||
if condition(o):
|
||||
return True
|
||||
return False
|
||||
@@ -35,6 +35,7 @@ UNITS_STRINGS = {
|
||||
Units.none: ["none", "null"],
|
||||
}
|
||||
|
||||
|
||||
UNITS_ENCODINGS = {
|
||||
Units.none: 0,
|
||||
None: 0,
|
||||
@@ -49,6 +50,20 @@ UNITS_ENCODINGS = {
|
||||
}
|
||||
|
||||
|
||||
UNIT_SCALE = {
|
||||
Units.none: 1,
|
||||
Units.mm: 0.001,
|
||||
Units.cm: 0.01,
|
||||
Units.m: 1.0,
|
||||
Units.km: 1000.0,
|
||||
Units.inches: 0.0254,
|
||||
Units.feet: 0.3048,
|
||||
Units.yards: 0.9144,
|
||||
Units.miles: 1609.340,
|
||||
}
|
||||
"""Unit scaling factor to meters"""
|
||||
|
||||
|
||||
def get_units_from_string(unit: str) -> Units:
|
||||
if not isinstance(unit, str):
|
||||
raise SpeckleInvalidUnitException(unit)
|
||||
@@ -59,10 +74,10 @@ def get_units_from_string(unit: str) -> Units:
|
||||
raise SpeckleInvalidUnitException(unit)
|
||||
|
||||
|
||||
def get_units_from_encoding(unit: int):
|
||||
def get_units_from_encoding(unit: int) -> Units:
|
||||
for name, encoding in UNITS_ENCODINGS.items():
|
||||
if unit == encoding:
|
||||
return name
|
||||
return name or Units.none
|
||||
|
||||
raise SpeckleException(
|
||||
message=(
|
||||
@@ -72,13 +87,36 @@ def get_units_from_encoding(unit: int):
|
||||
)
|
||||
|
||||
|
||||
def get_encoding_from_units(unit: Union[Units, None]):
|
||||
def get_encoding_from_units(unit: Union[Units, str, None]):
|
||||
maybe_sanitized_unit = unit
|
||||
if isinstance(unit, str):
|
||||
for unit_enum, aliases in UNITS_STRINGS.items():
|
||||
if unit in aliases:
|
||||
maybe_sanitized_unit = unit_enum
|
||||
try:
|
||||
return UNITS_ENCODINGS[unit]
|
||||
return UNITS_ENCODINGS[maybe_sanitized_unit]
|
||||
except KeyError as e:
|
||||
raise SpeckleException(
|
||||
message=(
|
||||
f"No encoding exists for unit {unit}."
|
||||
f"No encoding exists for unit {maybe_sanitized_unit}."
|
||||
f"Please enter a valid unit to encode (eg {UNITS_ENCODINGS})."
|
||||
)
|
||||
) from e
|
||||
|
||||
|
||||
def get_scale_factor_from_string(fromUnits: str, toUnits: str) -> float:
|
||||
"""Returns a scalar to convert distance values from one unit system to another"""
|
||||
return get_scale_factor(get_units_from_string(fromUnits), get_units_from_string(toUnits))
|
||||
|
||||
|
||||
def get_scale_factor(fromUnits: Units, toUnits: Units) -> float:
|
||||
"""Returns a scalar to convert distance values from one unit system to another"""
|
||||
return get_scale_factor_to_meters(fromUnits) / get_scale_factor_to_meters(toUnits)
|
||||
|
||||
|
||||
def get_scale_factor_to_meters(fromUnits: Units) -> float:
|
||||
"""Returns a scalar to convert distance values from one unit system to meters"""
|
||||
if fromUnits not in UNIT_SCALE:
|
||||
raise ValueError(f"Invalid units provided: {fromUnits}")
|
||||
|
||||
return UNIT_SCALE[fromUnits]
|
||||
@@ -1,16 +1,12 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic.config import Extra
|
||||
|
||||
|
||||
class AbstractTransport(ABC, BaseModel):
|
||||
_name: str = "Abstract"
|
||||
|
||||
class AbstractTransport(ABC):
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self):
|
||||
return type(self)._name
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def begin_write(self) -> None:
|
||||
@@ -87,7 +83,3 @@ class AbstractTransport(ABC, BaseModel):
|
||||
str -- the string representation of the root object
|
||||
"""
|
||||
pass
|
||||
|
||||
class Config:
|
||||
extra = Extra.allow
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
@@ -4,14 +4,15 @@ from specklepy.transports.abstract_transport import AbstractTransport
|
||||
|
||||
|
||||
class MemoryTransport(AbstractTransport):
|
||||
_name: str = "Memory"
|
||||
objects: dict = {}
|
||||
saved_object_count: int = 0
|
||||
def __init__(self, name="Memory") -> None:
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self.objects = {}
|
||||
self.saved_object_count = 0
|
||||
|
||||
def __init__(self, name=None, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
if name:
|
||||
self._name = name
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"MemoryTransport(objects: {len(self.objects)})"
|
||||
|
||||
@@ -4,7 +4,7 @@ from warnings import warn
|
||||
|
||||
import requests
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.core.api.client import SpeckleClient
|
||||
from specklepy.core.api.credentials import Account, get_account_from_token
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
@@ -45,13 +45,6 @@ class ServerTransport(AbstractTransport):
|
||||
```
|
||||
"""
|
||||
|
||||
_name = "RemoteTransport"
|
||||
url: Optional[str] = None
|
||||
stream_id: Optional[str] = None
|
||||
account: Optional[Account] = None
|
||||
saved_obj_count: int = 0
|
||||
session: Optional[requests.Session] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
stream_id: str,
|
||||
@@ -59,15 +52,18 @@ class ServerTransport(AbstractTransport):
|
||||
account: Optional[Account] = None,
|
||||
token: Optional[str] = None,
|
||||
url: Optional[str] = None,
|
||||
**data: Any,
|
||||
name: str = "RemoteTransport",
|
||||
) -> None:
|
||||
super().__init__(**data)
|
||||
super().__init__()
|
||||
if client is None and account 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."
|
||||
)
|
||||
|
||||
self._name = name
|
||||
self.account = None
|
||||
self.saved_obj_count = 0
|
||||
if account:
|
||||
self.account = account
|
||||
url = account.serverInfo.url
|
||||
@@ -97,6 +93,10 @@ class ServerTransport(AbstractTransport):
|
||||
{"Authorization": f"Bearer {self.account.token}", "Accept": "text/plain"}
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def begin_write(self) -> None:
|
||||
self.saved_obj_count = 0
|
||||
|
||||
|
||||
@@ -9,31 +9,22 @@ from specklepy.transports.abstract_transport import AbstractTransport
|
||||
|
||||
|
||||
class SQLiteTransport(AbstractTransport):
|
||||
_name = "SQLite"
|
||||
_base_path: Optional[str] = None
|
||||
_root_path: Optional[str] = None
|
||||
__connection: Optional[sqlite3.Connection] = None
|
||||
app_name: str = ""
|
||||
scope: str = ""
|
||||
saved_obj_count: int = 0
|
||||
max_size: Optional[int] = None
|
||||
_current_batch: Optional[List[Tuple[str, str]]] = None
|
||||
_current_batch_size: Optional[int] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_path: Optional[str] = None,
|
||||
app_name: Optional[str] = None,
|
||||
scope: Optional[str] = None,
|
||||
max_batch_size_mb: float = 10.0,
|
||||
**data: Any,
|
||||
name: str = "SQLite",
|
||||
) -> None:
|
||||
super().__init__(**data)
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self.app_name = app_name or "Speckle"
|
||||
self.scope = scope or "Objects"
|
||||
self._base_path = base_path or self.get_base_path(self.app_name)
|
||||
self.max_size = int(max_batch_size_mb * 1000 * 1000)
|
||||
self._current_batch = []
|
||||
self.saved_obj_count = 0
|
||||
self._current_batch: List[Tuple[str, str]] = []
|
||||
self._current_batch_size = 0
|
||||
|
||||
try:
|
||||
@@ -54,24 +45,12 @@ class SQLiteTransport(AbstractTransport):
|
||||
def __repr__(self) -> str:
|
||||
return f"SQLiteTransport(app: '{self.app_name}', scope: '{self.scope}')"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@staticmethod
|
||||
def get_base_path(app_name):
|
||||
# # from appdirs https://github.com/ActiveState/appdirs/blob/master/appdirs.py
|
||||
# # default mac path is not the one we use (we use unix path), so using special case for this
|
||||
# system = sys.platform
|
||||
# if system.startswith("java"):
|
||||
# import platform
|
||||
|
||||
# os_name = platform.java_ver()[3][0]
|
||||
# if os_name.startswith("Mac"):
|
||||
# system = "darwin"
|
||||
|
||||
# if system != "darwin":
|
||||
# return user_data_dir(appname=app_name, appauthor=False, roaming=True)
|
||||
|
||||
# path = os.path.expanduser("~/.config/")
|
||||
# return os.path.join(path, app_name)
|
||||
|
||||
return str(
|
||||
speckle_path_provider.user_application_data_path().joinpath(app_name)
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ class TestUser:
|
||||
assert my_activity.totalCount
|
||||
assert isinstance(their_activity, ActivityCollection)
|
||||
|
||||
older_activity = client.user.activity(before=my_activity.items[0].time)
|
||||
older_activity = client.active_user.activity(before=my_activity.items[0].time)
|
||||
|
||||
assert isinstance(older_activity, ActivityCollection)
|
||||
assert older_activity.totalCount
|
||||
|
||||
@@ -17,7 +17,7 @@ class TestSerialization:
|
||||
deserialized = operations.deserialize(serialized)
|
||||
|
||||
assert base.get_id() == deserialized.get_id()
|
||||
assert base.units == "mm"
|
||||
assert base.units == "millimetres"
|
||||
assert isinstance(base.test_bases[0], Base)
|
||||
assert base["@revit_thing"].speckle_type == "SpecialRevitFamily"
|
||||
assert base["@detach"].name == deserialized["@detach"].name
|
||||
|
||||
@@ -11,8 +11,8 @@ from specklepy.api.models import (
|
||||
from specklepy.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
UnsupportedException,
|
||||
)
|
||||
from devtools import debug
|
||||
|
||||
|
||||
@pytest.mark.run(order=3)
|
||||
@@ -37,7 +37,7 @@ class TestStream:
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def second_user(self, second_client: SpeckleClient):
|
||||
return second_client.user.get()
|
||||
return second_client.active_user.get()
|
||||
|
||||
def test_stream_create(self, client, stream, updated_stream):
|
||||
stream.id = updated_stream.id = client.stream.create(
|
||||
@@ -113,18 +113,18 @@ class TestStream:
|
||||
self, second_client: SpeckleClient, stream: Stream
|
||||
):
|
||||
# NOTE: these are user queries, but testing here to contain the flow
|
||||
invites = second_client.user.get_all_pending_invites()
|
||||
invites = second_client.active_user.get_all_pending_invites()
|
||||
|
||||
assert isinstance(invites, list)
|
||||
assert isinstance(invites[0], PendingStreamCollaborator)
|
||||
assert len(invites) == 1
|
||||
|
||||
invite = second_client.user.get_pending_invite(stream_id=stream.id)
|
||||
invite = second_client.active_user.get_pending_invite(stream_id=stream.id)
|
||||
assert isinstance(invite, PendingStreamCollaborator)
|
||||
|
||||
def test_stream_invite_use(self, second_client: SpeckleClient, stream: Stream):
|
||||
invite: PendingStreamCollaborator = (
|
||||
second_client.user.get_all_pending_invites()[0]
|
||||
second_client.active_user.get_all_pending_invites()[0]
|
||||
)
|
||||
|
||||
accepted = second_client.stream.invite_use(
|
||||
@@ -201,6 +201,9 @@ class TestStream:
|
||||
def test_stream_activity(self, client: SpeckleClient, stream: Stream):
|
||||
activity = client.stream.activity(stream.id)
|
||||
|
||||
if isinstance(activity, SpeckleException):
|
||||
debug(activity.with_traceback())
|
||||
|
||||
older_activity = client.stream.activity(
|
||||
stream.id, before=activity.items[0].time
|
||||
)
|
||||
|
||||
@@ -85,14 +85,15 @@ def test_speckle_type_cannot_be_set(base: Base) -> None:
|
||||
|
||||
def test_setting_units():
|
||||
b = Base(units="foot")
|
||||
assert b.units == "ft"
|
||||
assert b.units == "foot"
|
||||
|
||||
with pytest.raises(SpeckleInvalidUnitException):
|
||||
b.units = "big"
|
||||
# with pytest.raises(SpeckleInvalidUnitException):
|
||||
b.units = "big"
|
||||
assert b.units == "big"
|
||||
|
||||
with pytest.raises(SpeckleInvalidUnitException):
|
||||
b.units = 7 # invalid args are skipped
|
||||
assert b.units == "ft"
|
||||
assert b.units == "big"
|
||||
|
||||
b.units = None # None should be a valid arg
|
||||
assert b.units is None
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List, Optional
|
||||
from unittest import TestCase
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.graph_traversal.traversal import GraphTraversal, TraversalRule
|
||||
|
||||
|
||||
@dataclass()
|
||||
class TraversalMock(Base):
|
||||
child: Optional[Base]
|
||||
list_children: List[Base]
|
||||
dict_children: Dict[str, Base]
|
||||
|
||||
|
||||
class GraphTraversalTests(TestCase):
|
||||
def test_traverse_list_members(self):
|
||||
traverse_lists_rule = TraversalRule(
|
||||
[lambda _: True],
|
||||
lambda x: [
|
||||
item
|
||||
for item in x.get_member_names()
|
||||
if isinstance(getattr(x, item, None), list)
|
||||
],
|
||||
)
|
||||
|
||||
expected_traverse = Base()
|
||||
expected_traverse.id = "List Member"
|
||||
|
||||
expected_ignore = Base()
|
||||
expected_ignore.id = "Not List Member"
|
||||
|
||||
test_case = TraversalMock(
|
||||
list_children=[expected_traverse],
|
||||
dict_children={"myprop": expected_ignore},
|
||||
child=expected_ignore,
|
||||
)
|
||||
|
||||
ret = [
|
||||
context.current
|
||||
for context in GraphTraversal([traverse_lists_rule]).traverse(test_case)
|
||||
]
|
||||
|
||||
self.assertCountEqual(ret, [test_case, expected_traverse])
|
||||
self.assertNotIn(expected_ignore, ret)
|
||||
self.assertEqual(len(ret), 2)
|
||||
|
||||
def test_traverse_dict_members(self):
|
||||
traverse_lists_rule = TraversalRule(
|
||||
[lambda _: True],
|
||||
lambda x: [
|
||||
item
|
||||
for item in x.get_member_names()
|
||||
if isinstance(getattr(x, item, None), dict)
|
||||
],
|
||||
)
|
||||
|
||||
expected_traverse = Base()
|
||||
expected_traverse.id = "Dict Member"
|
||||
|
||||
expected_ignore = Base()
|
||||
expected_ignore.id = "Not Dict Member"
|
||||
|
||||
test_case = TraversalMock(
|
||||
list_children=[expected_ignore],
|
||||
dict_children={"myprop": expected_traverse},
|
||||
child=expected_ignore,
|
||||
)
|
||||
|
||||
ret = [
|
||||
context.current
|
||||
for context in GraphTraversal([traverse_lists_rule]).traverse(test_case)
|
||||
]
|
||||
|
||||
self.assertCountEqual(ret, [test_case, expected_traverse])
|
||||
self.assertNotIn(expected_ignore, ret)
|
||||
self.assertEqual(len(ret), 2)
|
||||
|
||||
def test_traverse_dynamic(self):
|
||||
traverse_lists_rule = TraversalRule(
|
||||
[lambda _: True], lambda x: x.get_dynamic_member_names()
|
||||
)
|
||||
|
||||
expected_traverse = Base()
|
||||
expected_traverse.id = "List Member"
|
||||
|
||||
expected_ignore = Base()
|
||||
expected_ignore.id = "Not List Member"
|
||||
|
||||
test_case = TraversalMock(
|
||||
child=expected_ignore,
|
||||
list_children=[expected_ignore],
|
||||
dict_children={"myprop": expected_ignore},
|
||||
)
|
||||
test_case["dynamicChild"] = expected_traverse
|
||||
test_case["dynamicListChild"] = [expected_traverse]
|
||||
|
||||
ret = [
|
||||
context.current
|
||||
for context in GraphTraversal([traverse_lists_rule]).traverse(test_case)
|
||||
]
|
||||
|
||||
self.assertCountEqual(ret, [test_case, expected_traverse, expected_traverse])
|
||||
self.assertNotIn(expected_ignore, ret)
|
||||
self.assertEqual(len(ret), 3)
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
from specklepy.objects.units import Units, get_scale_factor
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fromUnits, toUnits, inValue, expectedOutValue",
|
||||
[
|
||||
#To self
|
||||
(Units.km, Units.km, 1.5, 1.5),
|
||||
(Units.km, Units.km, 0, 0),
|
||||
(Units.m, Units.m, 1.5, 1.5),
|
||||
(Units.m, Units.m, 0, 0),
|
||||
(Units.cm, Units.cm, 1.5, 1.5),
|
||||
(Units.cm, Units.cm, 0, 0),
|
||||
(Units.mm, Units.mm, 1.5, 1.5),
|
||||
(Units.mm, Units.mm, 0, 0),
|
||||
(Units.miles, Units.miles, 1.5, 1.5),
|
||||
(Units.miles, Units.miles, 0, 0),
|
||||
(Units.yards, Units.yards, 1.5, 1.5),
|
||||
(Units.yards, Units.yards, 0, 0),
|
||||
(Units.feet, Units.feet, 1.5, 1.5),
|
||||
(Units.feet, Units.feet, 0, 0),
|
||||
|
||||
#To Meters
|
||||
(Units.km, Units.m, 987654.321, 987654321),
|
||||
(Units.m, Units.m, 987654.321, 987654.321),
|
||||
(Units.mm, Units.m, 98765432.1, 98765.4321),
|
||||
(Units.cm, Units.m, 9876543.21, 98765.4321),
|
||||
|
||||
#To negative meters
|
||||
(Units.km, Units.m, -987654.321, -987654321),
|
||||
(Units.m, Units.m,- 987654.321, -987654.321),
|
||||
(Units.mm, Units.m, -98765432.1, -98765.4321),
|
||||
(Units.cm, Units.m, -9876543.21, -98765.4321),
|
||||
|
||||
(Units.m, Units.km, 987654.321, 987.654321),
|
||||
(Units.m, Units.cm, 987654.321, 98765432.1),
|
||||
(Units.m, Units.mm, 987654.321, 987654321),
|
||||
|
||||
#Imperial
|
||||
(Units.miles, Units.m, 123.45, 198673.517),
|
||||
(Units.miles, Units.inches, 123.45, 7821792),
|
||||
(Units.yards, Units.m, 123.45, 112.88268),
|
||||
(Units.yards, Units.inches, 123.45, 4444.2),
|
||||
(Units.feet, Units.m, 123.45, 37.62756),
|
||||
(Units.feet, Units.inches, 123.45, 1481.4),
|
||||
(Units.inches, Units.m, 123.45, 3.13563),
|
||||
],
|
||||
)
|
||||
def test_get_scale_factor_between_units(fromUnits: Units, toUnits: Units, inValue: float, expectedOutValue: float):
|
||||
Tolerance = 1e-10
|
||||
actual = inValue * get_scale_factor(fromUnits, toUnits)
|
||||
assert(actual - expectedOutValue < Tolerance)
|
||||
Reference in New Issue
Block a user