Merge pull request #374 from specklesystems/gergo/uvSetup

gergo/uvSetup
This commit is contained in:
Dogukan Karatas
2025-01-22 10:49:17 +01:00
committed by GitHub
63 changed files with 1885 additions and 2470 deletions
+7 -100
View File
@@ -1,108 +1,15 @@
version: 2.1
orbs:
codecov: codecov/codecov@3.3.0
# Define the jobs we want to run for this project
jobs:
pre-commit:
parameters:
config_file:
default: ./.pre-commit-config.yaml
description: Optional, path to pre-commit config file.
type: string
cache_prefix:
default: ''
description: |
Optional cache prefix to be used on CircleCI. Can be used for cache busting or to ensure multiple jobs use different caches.
type: string
build:
docker:
- image: speckle/pre-commit-runner:latest
resource_class: medium
- image: cimg/base:2023.03
steps:
- checkout
- restore_cache:
keys:
- cache-pre-commit-<<parameters.cache_prefix>>-{{ checksum "<<parameters.config_file>>" }}
- run:
name: Install pre-commit hooks
command: pre-commit install-hooks --config <<parameters.config_file>>
- save_cache:
key: cache-pre-commit-<<parameters.cache_prefix>>-{{ checksum "<<parameters.config_file>>" }}
paths:
- ~/.cache/pre-commit
- run:
name: Run pre-commit
command: pre-commit run --all-files
- run:
command: git --no-pager diff
name: git diff
when: on_fail
test:
machine:
image: ubuntu-2204:2023.02.1
docker_layer_caching: false
resource_class: medium
parameters:
tag:
default: "3.11"
type: string
steps:
- checkout
- run:
name: Install python
command: |
pyenv install -s << parameters.tag >>
pyenv global << parameters.tag >>
- run:
name: Startup the Speckle Server
command: docker compose -f docker-compose.yml up -d
- run:
name: Install Poetry
command: |
pip install poetry
- run:
name: Install packages
command: poetry install
- run:
name: Run tests
command: poetry run pytest --cov --cov-report xml:reports/coverage.xml --junitxml=reports/test-results.xml
- store_test_results:
path: reports
- store_artifacts:
path: reports
- codecov/upload
deploy:
docker:
- image: "cimg/python:3.8"
steps:
- checkout
- run: python patch_version.py $CIRCLE_TAG
- run: poetry build
- run: poetry publish -u __token__ -p $PYPI_TOKEN
- run: echo "so long and thanks for all the fish"
# Orchestrate our job run sequence
workflows:
main:
build_and_test:
jobs:
- pre-commit:
filters:
tags:
only: /.*/
- test:
matrix:
parameters:
tag: ["3.11"]
filters:
tags:
only: /.*/
- deploy:
context: pypi
requires:
- pre-commit
- test
filters:
tags:
only: /[0-9]+(\.[0-9]+)*/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
- build
+56
View File
@@ -0,0 +1,56 @@
name: "Specklepy test and build"
on:
# pull_request:
# branches:
# - 'v3-dev'
push:
branches:
- "gergo/uvSetup"
jobs:
ci:
name: continuous-integration
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
steps:
- uses: actions/checkout@v4
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install the project
run: uv sync --all-extras --dev
- uses: actions/cache@v3
with:
path: ~/.cache/pre-commit/
key: ${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
run: uv run pre-commit run --all-files
# - name: Run Speckle Server
# run: docker compose up -d
# - name: Run tests
# run: uv run pytest --cov --cov-report xml:reports/coverage.xml --junitxml=reports/test-results.xml
# - uses: codecov/codecov-action@v5
# if: matrix.python-version == 3.13
# with:
# fail_ci_if_error: true # optional (default = false)
# files: ./reports/test-results.xml # optional
# token: ${{ secrets.CODECOV_TOKEN }}
- name: Minimize uv cache
run: uv cache prune --ci
+36
View File
@@ -0,0 +1,36 @@
# Publish a release to PyPI.
# name: 'Publish to PyPI'
# on:
# push:
# branches:
# - 'gergo/uvSetup'
# jobs:
# pypi-publish:
# name: Upload to PyPI
# runs-on: ubuntu-latest
# environment:
# name: release
# permissions:
# # For PyPI's trusted publishing.
# id-token: write
# steps:
# - name: 'Install uv'
# uses: astral-sh/setup-uv@v5
# - uses: actions/checkout@v4
# with:
# # This is necessary so that we have the tags.
# fetch-depth: 0
# - uses: mtkennerly/dunamai-action@v1
# with:
# env-var: MY_VERSION
# args: --style semver
# - run: echo $MY_VERSION
# - name: 'Build artifacts'
# run: uv build
# - name: Publish to PyPi
# run: uv publish --publish-url https://test.pypi.org/simple/
# - name: Test package install
# run: uv run --with specklepy --no-project -- python -c "import specklepy"
+15 -17
View File
@@ -1,33 +1,31 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
- repo: local
hooks:
# Run the linter.
- id: ruff
rev: v0.8.2
name: ruff lint
entry: uv run ruff check --force-exclude
language: system
types_or: [python, pyi]
# Run the formatter.
- id: ruff-format
name: ruff format
entry: uv run ruff format --force-exclude
language: system
types_or: [python, pyi]
- repo: https://github.com/commitizen-tools/commitizen
hooks:
- id: commitizen
- id: commitizen-branch
stages:
- push
- pre-push
rev: v3.13.0
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
# It is recommended to specify the latest version of Python
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
# language_version: python3.11
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v5.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
+7 -7
View File
@@ -25,25 +25,25 @@ Head to the [**📚 specklepy docs**](https://speckle.guide/dev/python.html) for
### Installation
This project uses python-poetry for dependency management, make sure you follow the official [docs](https://python-poetry.org/docs/#installation) to get poetry.
This project uses uv for dependency management, make sure you follow the official [docs](https://docs.astral.sh/uv/) to get it.
To bootstrap the project environment run `$ poetry install`. This will create a new virtual-env for the project and install both the package and dev dependencies.
To create a new virtual environment with uv run `$ uv venv` and follow the instructions on the screen to activate the virtual environment.
To bootstrap the project environment run `$ uv sync`. This will install both the package and dev dependencies.
If this is your first time using poetry and you're used to creating your venvs within the project directory, run `poetry config virtualenvs.in-project true` to configure poetry to do the same.
To execute any python script run `$ uv run python my_script.py`
To execute any python script run `$ poetry run python my_script.py`
> Alternatively you may roll your own virtual-env with either venv, virtualenv, pyenv-virtualenv etc. Poetry will play along an recognize if it is invoked from inside a virtual environment.
> Alternatively you may roll your own virtual-env with either venv, virtualenv, pyenv-virtualenv etc. Uv will play along an recognize if it is invoked from inside a virtual environment.
### Style guide
All our repo wide styling linting and other rules are checked and enforced by `pre-commit`, which is included in the dev dependencies.
All our repo wide styling linting and other rules are checked and enforced by `pre-commit`, which is included in the dev dependencies.
It is recommended to set up `pre-commit` after installing the dependencies by running `$ pre-commit install`.
Commiting code that doesn't adhere to the given rules, will fail the checks in our CI system.
### Local Data Paths
It may be helpful to know where the local accounts and object cache dbs are stored. Depending on on your OS, you can find the dbs at:
- Windows: `APPDATA` or `<USER>\AppData\Roaming\Speckle`
- Linux: `$XDG_DATA_HOME` or by default `~/.local/share/Speckle`
- Mac: `~/.config/Speckle`
+1 -7
View File
@@ -6,7 +6,7 @@ services:
# Speckle Server dependencies
#######
postgres:
image: "postgres:14.5-alpine"
image: "postgres:16-alpine"
restart: always
environment:
POSTGRES_DB: speckle
@@ -53,12 +53,6 @@ services:
# Speckle Server
#######
speckle-frontend:
image: speckle/speckle-frontend-2:latest
restart: always
ports:
- "0.0.0.0:8080:8080"
speckle-server:
image: speckle/speckle-server:latest
restart: always
+2 -2
View File
@@ -1,8 +1,8 @@
from devtools import debug
from specklepy.api import operations
from specklepy.objects.geometry import Base
from specklepy.objects.units import Units
from specklepy.objects_v2.geometry import Base
from specklepy.objects_v2.units import Units
dct = {
"id": "1234abcd",
-31
View File
@@ -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("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()
Generated
-2075
View File
File diff suppressed because it is too large Load Diff
+56 -61
View File
@@ -1,75 +1,70 @@
[tool.poetry]
[project]
dynamic = ["version"]
name = "specklepy"
version = "2.17.14"
description = "The Python SDK for Speckle 2.0"
readme = "README.md"
authors = ["Speckle Systems <devops@speckle.systems>"]
license = "Apache-2.0"
repository = "https://github.com/specklesystems/speckle-py"
documentation = "https://speckle.guide/dev/py-examples.html"
homepage = "https://speckle.systems/"
packages = [
{ include = "specklepy", from = "src" },
{ include = "speckle_automate", from = "src" },
authors = [{ name = "Speckle Systems", email = "devops@speckle.systems" }]
license = { text = "Apache-2.0" }
requires-python = ">=3.10.0, <4.0"
dependencies = [
"appdirs>=1.4.4",
"attrs>=24.3.0",
"deprecated>=1.2.15",
"gql[requests,websockets]>=3.5.0",
"httpx>=0.28.1",
"pydantic>=2.10.5",
"pydantic-settings>=2.7.1",
"stringcase>=1.2.0",
"ujson>=5.10.0",
]
[dependency-groups]
dev = [
"commitizen>=4.1.0",
"devtools>=0.12.2",
"pre-commit>=4.0.1",
"pytest>=8.3.4",
"pytest-asyncio>=0.25.2",
"pytest-cov>=6.0.0",
"pytest-ordering>=0.6",
"ruff>=0.9.2",
"types-deprecated>=1.2.15.20241117",
"types-requests>=2.32.0.20241016",
"types-ujson>=5.10.0.20240515",
]
[tool.poetry.dependencies]
python = ">=3.10.0, <4.0"
pydantic = "^2.5"
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"
httpx = "^0.25.0"
pydantic-settings = "^2.6.1"
[project.urls]
repository = "https://github.com/specklesystems/specklepy"
documentation = "https://speckle.guide/dev/py-examples.html"
homepage = "https://speckle.systems/"
[tool.poetry.group.dev.dependencies]
black = "24.10.0"
isort = "^5.13.2"
pytest = "^7.1.3"
pytest-asyncio = "^0.23.0"
pytest-ordering = "^0.6"
pytest-cov = "^3.0.0"
devtools = "^0.8.0"
pylint = "^3.3.2"
pydantic-settings = "^2.3.0"
mypy = "^0.982"
pre-commit = "^2.20.0"
commitizen = "^3.13.0"
ruff = "^0.8.2"
types-deprecated = "^1.2.9"
types-ujson = "^5.6.0.0"
types-requests = "^2.28.11.5"
[build-system]
requires = ["setuptools>=64", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"
[tool.black]
exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
include = '\.pyi?$'
line-length = 88
target-version = ["py39", "py310", "py311", "py312", "py313"]
[tool.setuptools_scm]
[tool.commitizen]
name = "cz_conventional_commits"
version = "2.9.2"
tag_format = "$version"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.isort]
profile = "black"
[tool.ruff]
exclude = [".venv", "**/*.yml"]
[tool.ruff.lint]
select = [
# pycodestyle
"E",
# Pyflakes
"F",
# pyupgrade
"UP",
# flake8-bugbear
"B",
# flake8-simplify
"SIM",
# isort
"I",
]
ignore = ["UP006", "UP007", "UP035"]
+4 -2
View File
@@ -96,7 +96,8 @@ class AutomationContext:
def receive_version(self) -> Base:
"""Receive the Speckle project version that triggered this automation run."""
# TODO: this is a quick hack to keep implementation consistency. Move to proper receive many versions
# TODO: this is a quick hack to keep implementation consistency.
# Move to proper receive many versions
version_id = self.automation_run_data.triggers[0].payload.version_id
commit = self.speckle_client.commit.get(
self.automation_run_data.project_id, version_id
@@ -264,7 +265,8 @@ class AutomationContext:
if not path_obj.exists():
raise ValueError("The given file path doesn't exist")
files = {path_obj.name: open(str(path_obj), "rb")}
files = {path_obj.name: path_obj.open("rb")}
url = (
f"{self.automation_run_data.speckle_server_url}api/stream/"
+3 -2
View File
@@ -128,7 +128,8 @@ def execute_automate_function(
automate_function, # type: ignore
)
# if we've gotten this far, the execution should technically be completed as expected
# if we've gotten this far,
# the execution should technically be completed as expected
# thus exiting with 0 is the schemantically correct thing to do
exit_code = (
1 if automation_context.run_status == AutomationStatus.EXCEPTION else 0
@@ -190,4 +191,4 @@ def run_function(
if not automation_context.context_view:
automation_context.set_context_view()
automation_context.report_run_status()
return automation_context
return automation_context
+6 -4
View File
@@ -1,3 +1,5 @@
import contextlib
from deprecated import deprecated
from specklepy.api.credentials import Account
@@ -40,7 +42,8 @@ class SpeckleClient(CoreSpeckleClient):
client = SpeckleClient(host="app.speckle.systems") # or whatever your host is
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
# authenticate the client with an account (account has been added in Speckle Manager)
# authenticate the client with an account
# (account has been added in Speckle Manager)
account = get_default_account()
client.authenticate_with_account(account)
@@ -74,10 +77,9 @@ class SpeckleClient(CoreSpeckleClient):
)
server_version = None
try:
with contextlib.suppress(Exception):
server_version = self.server.version()
except Exception:
pass
self.other_user = OtherUserResource(
account=self.account,
+5 -2
View File
@@ -2,8 +2,11 @@ from typing import List, Optional
# following imports seem to be unnecessary, but they need to stay
# to not break the scripts using these functions as non-core
from specklepy.core.api.credentials import StreamWrapper # noqa: F401
from specklepy.core.api.credentials import Account, UserInfo # noqa: F401
from specklepy.core.api.credentials import ( # noqa: F401
Account,
StreamWrapper, # noqa: F401
UserInfo,
)
from specklepy.core.api.credentials import (
get_account_from_token as core_get_account_from_token,
)
+5 -1
View File
@@ -53,7 +53,9 @@ def receive(
return _untracked_receive(obj_id, remote_transport, local_transport)
def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str:
def serialize(
base: Base, write_transports: List[AbstractTransport] | None = None
) -> str:
"""
Serialize a base object. If no write transports are provided,
the object will be serialized
@@ -67,6 +69,8 @@ def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str
Returns:
str -- the serialized object
"""
if not write_transports:
write_transports = []
metrics.track(metrics.SDK, custom_props={"name": "Serialize"})
return core_serialize(base, write_transports)
@@ -138,7 +138,8 @@ class ActiveUserResource(CoreResource):
token (Optional[str]): The token of the invite to look for (optional).
Returns:
Optional[PendingStreamCollaborator]: The invite for the given stream, or None if not found.
Optional[PendingStreamCollaborator]: The invite for the given stream,
or None if not found.
"""
metrics.track(metrics.SDK, self.account, {"name": "User Active Invite Get"})
return super().get_pending_invite(stream_id, token)
@@ -20,8 +20,10 @@ from specklepy.logging.exceptions import SpeckleException
class OtherUserResource(CoreResource):
"""
Provides API access to other users' profiles and activities on the platform.
This class enables fetching limited information about users, searching for users by name or email,
and accessing user activity logs with appropriate privacy and access control measures in place.
This class enables fetching limited information about users,
searching for users by name or email,
and accessing user activity logs with appropriate privacy
and access control measures in place.
"""
def __init__(self, account, basepath, client, server_version) -> None:
@@ -64,7 +66,8 @@ class OtherUserResource(CoreResource):
limit (int): Maximum number of search results to return.
Returns:
Union[List[LimitedUser], SpeckleException]: A list of users matching the search
Union[List[LimitedUser], SpeckleException]:
A list of users matching the search
query or an exception if the query is too short.
"""
if len(search_query) < 3:
@@ -86,8 +89,8 @@ class OtherUserResource(CoreResource):
cursor: Optional[datetime] = None,
) -> ActivityCollection:
"""
Retrieves a collection of activities for a specified user, with optional filters for activity type,
time frame, and pagination.
Retrieves a collection of activities for a specified user,
with optional filters for activity type, time frame, and pagination.
Args:
user_id (str): The ID of the user whose activities are being requested.
@@ -98,7 +101,8 @@ class OtherUserResource(CoreResource):
cursor (Optional[datetime]): Timestamp for pagination cursor.
Returns:
ActivityCollection: A collection of user activities filtered according to specified criteria.
ActivityCollection: A collection of user activities filtered
according to specified criteria.
"""
metrics.track(metrics.SDK, self.account, {"name": "Other User Activity"})
return super().activity(user_id, limit, action_type, before, after, cursor)
@@ -31,7 +31,8 @@ class ServerResource(CoreResource):
the server version in the format (major, minor, patch, (tag, build))
eg (2, 6, 3) for a stable build and (2, 6, 4, 'alpha', 4711) for alpha
"""
# not tracking as it will be called along with other mutations / queries as a check
# not tracking as it will be called along with other
# mutations / queries as a check
return super().version()
def apps(self) -> Dict:
+12 -10
View File
@@ -1,3 +1,4 @@
import contextlib
import re
from typing import Dict
from warnings import warn
@@ -49,7 +50,8 @@ class SpeckleClient:
client = SpeckleClient(host="app.speckle.systems") # or whatever your host is
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
# authenticate the client with an account (account has been added in Speckle Manager)
# authenticate the client with an account
# (account has been added in Speckle Manager)
account = get_default_account()
client.authenticate_with_account(account)
@@ -102,7 +104,8 @@ class SpeckleClient:
self._init_resources()
# ? Check compatibility with the server - i think we can skip this at this point? save a request
# ? Check compatibility with the server
# - i think we can skip this at this point? save a request
# try:
# server_info = self.server.get()
# if isinstance(server_info, Exception):
@@ -187,9 +190,10 @@ class SpeckleClient:
if ex.exception.code == 403:
warn(
SpeckleWarning(
"Possibly invalid token - could not authenticate Speckle Client"
f" for server {self.url}"
)
"Possibly invalid token - could not authenticate "
f"Speckle Client for server {self.url}"
),
stacklevel=2,
)
else:
raise ex
@@ -203,10 +207,8 @@ class SpeckleClient:
)
server_version = None
try:
with contextlib.suppress(Exception):
server_version = self.server.version()
except Exception:
pass
self.other_user = OtherUserResource(
account=self.account,
@@ -283,7 +285,7 @@ class SpeckleClient:
return attr.Resource(
account=self.account, basepath=self.url, client=self.httpclient
)
except AttributeError:
except AttributeError as ex:
raise SpeckleException(
f"Method {name} is not supported by the SpeckleClient class"
)
) from ex
+2 -1
View File
@@ -55,7 +55,8 @@ class ServerConfiguration(BaseModel):
objectSizeLimitBytes: int
# Keeping this one all Optionals at the minute, because its used both as a deserialization model for GQL and Account Management
# Keeping this one all Optionals at the minute,
# because its used both as a deserialization model for GQL and Account Management
class ServerInfo(BaseModel):
name: Optional[str] = None
company: Optional[str] = None
+4 -1
View File
@@ -4,7 +4,10 @@ from typing import List, Optional
from deprecated import deprecated
from pydantic import BaseModel, Field
FE1_DEPRECATION_REASON = "Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in Client}"
FE1_DEPRECATION_REASON = (
"Stream/Branch/Commit API is now deprecated, "
"Use the new Project/Model/Version API functions in Client"
)
FE1_DEPRECATION_VERSION = "2.20"
+7 -2
View File
@@ -70,7 +70,8 @@ def receive(
serializer = BaseObjectSerializer(read_transport=local_transport)
# try local transport first. if the parent is there, we assume all the children are there and continue with deserialization using the local transport
# try local transport first. if the parent is there, we assume all the children
# are there and continue with deserialization using the local transport
obj_string = local_transport.get_object(obj_id)
if obj_string:
return serializer.read_json(obj_string=obj_string)
@@ -90,7 +91,9 @@ def receive(
return serializer.read_json(obj_string=obj_string)
def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str:
def serialize(
base: Base, write_transports: List[AbstractTransport] | None = None
) -> str:
"""
Serialize a base object. If no write transports are provided,
the object will be serialized
@@ -104,6 +107,8 @@ def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str
Returns:
str -- the serialized object
"""
if not write_transports:
write_transports = []
serializer = BaseObjectSerializer(write_transports=write_transports)
return serializer.write_json(base)[1]
+3 -2
View File
@@ -18,7 +18,7 @@ from specklepy.transports.sqlite import SQLiteTransport
T = TypeVar("T", bound=BaseModel)
class ResourceBase(object):
class ResourceBase:
def __init__(
self,
account: Account,
@@ -101,7 +101,8 @@ class ResourceBase(object):
parse_response: bool = True,
) -> Any:
"""Executes the GraphQL query"""
# This method has quite complex and ambiguous typing, and counter-intuitive error handling
# This method has quite complex and ambiguous typing,
# and counter-intuitive error handling
# We are going to phase it out in favour of `make_request_and_parse_response`
try:
with self.__lock:
@@ -37,10 +37,12 @@ class ActiveUserResource(ResourceBase):
self.schema = User
def get(self) -> Optional[User]:
"""Gets the currently active user profile (as extracted from the authorization header)
"""Gets the currently active user profile
(as extracted from the authorization header)
Returns:
User -- the requested user, or none if no authentication token is provided to the Client
User -- the requested user, or none if no authentication token
is provided to the Client
"""
QUERY = gql(
"""
@@ -76,14 +76,24 @@ class ModelResource(ResourceBase):
) -> ModelWithVersions:
QUERY = gql(
"""
query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) {
query ModelGetWithVersions(
$modelId: String!,
$projectId: String!,
$versionsLimit: Int!,
$versionsCursor: String,
$versionsFilter: ModelVersionsFilter
) {
data:project(id: $projectId) {
data:model(id: $modelId) {
id
name
previewUrl
updatedAt
versions(limit: $versionsLimit, cursor: $versionsCursor, filter: $versionsFilter) {
versions(
limit: $versionsLimit,
cursor: $versionsCursor,
filter: $versionsFilter
) {
items {
id
referencedObject
@@ -148,9 +158,18 @@ class ModelResource(ResourceBase):
) -> ResourceCollection[Model]:
QUERY = gql(
"""
query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) {
query ProjectGetWithModels(
$projectId: String!,
$modelsLimit: Int!,
$modelsCursor: String,
$modelsFilter: ProjectModelsFilter
) {
data:project(id: $projectId) {
data:models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) {
data:models(
limit: $modelsLimit,
cursor: $modelsCursor,
filter: $modelsFilter
) {
items {
id
name
@@ -74,7 +74,9 @@ class OtherUserResource(ResourceBase):
archived: bool = False,
emailOnly: bool = False,
) -> UserSearchResultCollection:
"""Searches for a user on the server, by name or email. The search query must be at least
"""
Searches for a user on the server, by name or email.
The search query must be at least
3 characters long
Arguments:
@@ -89,8 +91,20 @@ class OtherUserResource(ResourceBase):
QUERY = gql(
"""
query UserSearch($query: String!, $limit: Int!, $cursor: String, $archived: Boolean, $emailOnly: Boolean) {
data:userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) {
query UserSearch(
$query: String!,
$limit: Int!,
$cursor: String,
$archived: Boolean,
$emailOnly: Boolean
) {
data:userSearch(
query: $query,
limit: $limit,
cursor: $cursor,
archived: $archived,
emailOnly: $emailOnly
) {
cursor
items {
id
@@ -37,7 +37,10 @@ class ProjectInviteResource(ResourceBase):
) -> ProjectWithTeam:
QUERY = gql(
"""
mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) {
mutation ProjectInviteCreate(
$projectId: ID!,
$input: ProjectInviteCreateInput!
) {
data:projectMutations {
data:invites {
data:create(projectId: $projectId, input: $input) {
@@ -65,7 +65,12 @@ class ProjectResource(ResourceBase):
) -> ProjectWithModels:
QUERY = gql(
"""
query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) {
query ProjectGetWithModels(
$projectId: String!,
$modelsLimit: Int!,
$modelsCursor: String,
$modelsFilter: ProjectModelsFilter
) {
data:project(id: $projectId) {
id
name
@@ -77,7 +82,11 @@ class ProjectResource(ResourceBase):
updatedAt
sourceApps
workspaceId
models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) {
models(
limit: $modelsLimit,
cursor: $modelsCursor,
filter: $modelsFilter
) {
items {
id
name
@@ -80,7 +80,8 @@ class ServerResource(ResourceBase):
the server version in the format (major, minor, patch, (tag, build))
eg (2, 6, 3) for a stable build and (2, 6, 4, 'alpha', 4711) for alpha
"""
# not tracking as it will be called along with other mutations / queries as a check
# not tracking as it will be called along with other mutations / queries
# as a check
query = gql(
"""
query Server {
@@ -76,7 +76,13 @@ class VersionResource(ResourceBase):
) -> ResourceCollection[Version]:
QUERY = gql(
"""
query VersionGetVersions($projectId: String!, $modelId: String!, $limit: Int!, $cursor: String, $filter: ModelVersionsFilter) {
query VersionGetVersions(
$projectId: String!,
$modelId: String!,
$limit: Int!,
$cursor: String,
$filter: ModelVersionsFilter
) {
data:project(id: $projectId) {
data:model(id: $modelId) {
data:versions(limit: $limit, cursor: $cursor, filter: $filter) {
+16 -5
View File
@@ -159,11 +159,12 @@ class StreamWrapper:
try:
self.branch_name = project["project"]["model"]["name"]
except KeyError as ke:
raise SpeckleException("Project model name is not found", ke)
raise SpeckleException("Project model name is not found", ke) from ke
if not self.stream_id:
raise SpeckleException(
f"Cannot parse {url} into a stream wrapper class - no {key_stream} id found."
f"Cannot parse {url} into a stream wrapper class - no {key_stream} ",
"id found.",
)
@property
@@ -213,7 +214,11 @@ class StreamWrapper:
self._client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
if self._account.token is None and token is None:
warn(f"No local account found for server {self.host}", SpeckleWarning)
warn(
f"No local account found for server {self.host}",
SpeckleWarning,
stacklevel=2,
)
return self._client
if self._account.token:
@@ -266,14 +271,20 @@ class StreamWrapper:
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}"
base_url = (
f"{self.server_url}{key_streams}"
f"{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}"
return (
f"{self.server_url}{key_streams}{self.stream_id}"
f"{key_branches}{value_branch}"
)
elif wrapper_type == "stream":
return f"{self.server_url}{key_streams}{self.stream_id}"
else:
@@ -99,7 +99,7 @@ def user_application_data_path() -> Path:
except Exception as ex:
raise SpeckleException(
message="Failed to initialize user application data path.", exception=ex
)
) from ex
def user_speckle_folder_path() -> Path:
+3 -2
View File
@@ -86,7 +86,8 @@ def track(
METRICS_TRACKER.queue.put_nowait(event_params)
except Exception as ex:
# wrapping this whole thing in a try except as we never want a failure here to annoy users!
# wrapping this whole thing in a try except as we never want a failure here
# to annoy users!
LOG.debug(f"Error queueing metrics request: {str(ex)}")
@@ -106,7 +107,7 @@ class Singleton(type):
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
+10 -5
View File
@@ -224,7 +224,7 @@ def _validate_type(t: Optional[type], value: Any) -> Tuple[bool, Any]:
if isinstance(t, ForwardRef):
return True, value
origin = getattr(t, "__origin__")
origin = t.__origin__
# below is what in nicer for >= py38
# origin = get_origin(t)
@@ -289,7 +289,7 @@ def _validate_type(t: Optional[type], value: Any) -> Tuple[bool, Any]:
if len(args) != len(value):
return False, value
values = []
for t_item, v_item in zip(args, value):
for t_item, v_item in zip(args, value, strict=True):
item_valid, item_value = _validate_type(t_item, v_item)
if not item_valid:
return False, value
@@ -372,7 +372,8 @@ class Base(_RegisteringBase, speckle_type="Base"):
if name == "speckle_type":
# not sure if we should raise an exception here??
# raise SpeckleException(
# "Cannot override the `speckle_type`. This is set manually by the class or on deserialisation"
# "Cannot override the `speckle_type`."
# "This is set manually by the class or on deserialisation"
# )
return
# if value is not None:
@@ -400,7 +401,10 @@ class Base(_RegisteringBase, speckle_type="Base"):
try:
cls._attr_types = get_type_hints(cls)
except Exception as e:
warn(f"Could not update forward refs for class {cls.__name__}: {e}")
warn(
f"Could not update forward refs for class {cls.__name__}: {e}",
stacklevel=2,
)
@classmethod
def validate_prop_name(cls, name: str) -> None:
@@ -465,7 +469,8 @@ class Base(_RegisteringBase, speckle_type="Base"):
# @units.setter
# def units(self, value: Union[str, Units, None]):
# """While this property accepts any string value, geometry expects units to be specific strings (see Units enum)"""
# """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):
+7 -11
View File
@@ -23,7 +23,10 @@ class Point(Base, IHasUnits, speckle_type="Objects.Geometry.Point"):
z: float
def __repr__(self) -> str:
return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, units: {self.units})"
return (
f"{self.__class__.__name__}"
f"(x: {self.x}, y: {self.y}, z: {self.z}, units: {self.units})"
)
def to_list(self) -> List[float]:
return [self.x, self.y, self.z]
@@ -52,8 +55,7 @@ class Point(Base, IHasUnits, speckle_type="Objects.Geometry.Point"):
# convert other point's coordinates to this point's units
scale_factor = get_scale_factor(
get_units_from_string(
other.units), get_units_from_string(self.units)
get_units_from_string(other.units), get_units_from_string(self.units)
)
dx = (other.x * scale_factor) - self.x
@@ -94,8 +96,7 @@ class Line(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Line"):
@classmethod
def from_list(cls, coords: List[float], units: str | Units) -> "Line":
if len(coords) < 6:
raise ValueError(
"Line from coordinate array requires 6 coordinates.")
raise ValueError("Line from coordinate array requires 6 coordinates.")
start = Point(x=coords[0], y=coords[1], z=coords[2], units=units)
end = Point(x=coords[3], y=coords[4], z=coords[5], units=units)
@@ -191,7 +192,7 @@ class Polyline(Base, IHasUnits, ICurve, speckle_type="Objects.Geometry.Polyline"
return cls(
closed=(int(coords[2]) == 1),
domain=Interval(start=coords[3], end=coords[4]),
value=coords[6: 6 + point_count],
value=coords[6 : 6 + point_count],
units=units,
)
@@ -211,7 +212,6 @@ class Mesh(
"textureCoordinates": 31250,
},
):
vertices: List[float]
faces: List[int]
colors: List[int] = field(default_factory=list)
@@ -226,7 +226,6 @@ class Mesh(
return len(self.textureCoordinates) // 2
def get_point(self, index: int) -> Point:
index *= 3
return Point(
x=self.vertices[index],
@@ -236,7 +235,6 @@ class Mesh(
)
def get_points(self) -> List[Point]:
if len(self.vertices) % 3 != 0:
raise ValueError(
"Mesh vertices list is malformed: expected length to be multiple of 3"
@@ -255,12 +253,10 @@ class Mesh(
return points
def get_texture_coordinate(self, index: int) -> Tuple[float, float]:
index *= 2
return (self.textureCoordinates[index], self.textureCoordinates[index + 1])
def align_vertices_with_texcoords_by_index(self) -> None:
if not self.textureCoordinates:
return
+2 -5
View File
@@ -37,7 +37,6 @@ class IDisplayValue(Generic[T], metaclass=ABCMeta):
@dataclass(kw_only=True)
class IHasUnits(metaclass=ABCMeta):
units: str | Units
_units: str = field(repr=False, init=False)
@@ -59,7 +58,6 @@ class IHasUnits(metaclass=ABCMeta):
@dataclass(kw_only=True)
class IHasArea(metaclass=ABCMeta):
area: float
_area: float = field(init=False, repr=False)
@@ -69,14 +67,13 @@ class IHasArea(metaclass=ABCMeta):
@area.setter
def area(self, value: float):
if not isinstance(value, (int, float)):
if not isinstance(value, int | float):
raise ValueError(f"Area must be a number, got {type(value)}")
self._area = float(value)
@dataclass(kw_only=True)
class IHasVolume(metaclass=ABCMeta):
volume: float
_volume: float = field(init=False, repr=False)
@@ -86,7 +83,7 @@ class IHasVolume(metaclass=ABCMeta):
@volume.setter
def volume(self, value: float):
if not isinstance(value, (int, float)):
if not isinstance(value, int | float):
raise ValueError(f"Volume must be a number, got {type(value)}")
self._volume = float(value)
@@ -12,18 +12,25 @@ class Collection(
detachable={"elements"},
):
"""
A simple container for organising objects within a model and preserving object hierarchy.
A simple container for organising objects within a model
and preserving object hierarchy.
A container is defined by a human-readable name a unique applicationId and its list of contained elements.
The elements can include an unrestricted number of Base objects including additional nested Collections.
A container is defined by a human-readable name a unique applicationId and
its list of contained elements.
The elements can include an unrestricted number of Base objects including
additional nested Collections.
Note:
A Collection can be for example a Layer in Rhino/AutoCad, a collection in Blender, or a Category in Revit.
The location of each collection in the hierarchy of collections in a commit will be retrieved through commit traversal.
A Collection can be for example a Layer in Rhino/AutoCad,
a collection in Blender, or a Category in Revit.
The location of each collection in the hierarchy of collections in a commit
will be retrieved through commit traversal.
Attributes:
name: The human-readable name of the Collection. This name is not necessarily unique within a commit. Set the applicationId for a unique identifier.
elements: The elements contained in this Collection. This may include additional nested Collections
name: The human-readable name of the Collection. This name is not necessarily
unique within a commit. Set the applicationId for a unique identifier.
elements: The elements contained in this Collection.
This may include additional nested Collections
"""
name: str
+1 -4
View File
@@ -1,4 +1,4 @@
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import List, Optional
from specklepy.objects.base import Base
@@ -22,7 +22,6 @@ class GroupProxy(
speckle_type="Models.Proxies.GroupProxy",
detachable={"objects"},
):
objects: List[str]
name: str
@@ -33,7 +32,6 @@ class InstanceProxy(
IHasUnits,
speckle_type="Models.Proxies.InstanceProxy",
):
definition_id: str
transform: List[float]
max_depth: int
@@ -45,7 +43,6 @@ class InstanceDefinitionProxy(
speckle_type="Models.Proxies.InstanceDefinitionProxy",
detachable={"objects"},
):
objects: List[str]
max_depth: int
name: str
+2 -3
View File
@@ -149,7 +149,7 @@ cube_mesh = Mesh(
volume=0.0,
)
print(f"\nMesh Details:")
print("\nMesh Details:")
print(f"Number of vertices: {cube_mesh.vertices_count}")
print(f"Number of texture coordinates: {cube_mesh.texture_coordinates_count}")
@@ -178,6 +178,5 @@ print("Alignment complete.")
print(f"Vertices count after alignment: {cube_mesh.vertices_count}")
print(
f"Texture coordinates count after alignment: {
cube_mesh.texture_coordinates_count}"
f"Texture coordinates count after alignment: {cube_mesh.texture_coordinates_count}"
)
+13 -6
View File
@@ -19,7 +19,7 @@ from warnings import warn
from stringcase import pascalcase
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
from specklepy.objects.units import Units
from specklepy.objects_v2.units import Units
from specklepy.transports.memory import MemoryTransport
PRIMITIVES = (int, float, str, bool)
@@ -225,7 +225,7 @@ def _validate_type(t: Optional[type], value: Any) -> Tuple[bool, Any]:
if isinstance(t, ForwardRef):
return True, value
origin = getattr(t, "__origin__")
origin = t.__origin__
# below is what in nicer for >= py38
# origin = get_origin(t)
@@ -290,7 +290,7 @@ def _validate_type(t: Optional[type], value: Any) -> Tuple[bool, Any]:
if len(args) != len(value):
return False, value
values = []
for t_item, v_item in zip(args, value):
for t_item, v_item in zip(args, value, strict=True):
item_valid, item_value = _validate_type(t_item, v_item)
if not item_valid:
return False, value
@@ -387,7 +387,8 @@ class Base(_RegisteringBase, speckle_type="Base"):
if name == "speckle_type":
# not sure if we should raise an exception here??
# raise SpeckleException(
# "Cannot override the `speckle_type`. This is set manually by the class or on deserialisation"
# "Cannot override the `speckle_type`."
# "This is set manually by the class or on deserialisation"
# )
return
# if value is not None:
@@ -415,7 +416,10 @@ class Base(_RegisteringBase, speckle_type="Base"):
try:
cls._attr_types = get_type_hints(cls)
except Exception as e:
warn(f"Could not update forward refs for class {cls.__name__}: {e}")
warn(
f"Could not update forward refs for class {cls.__name__}: {e}",
stacklevel=2,
)
@classmethod
def validate_prop_name(cls, name: str) -> None:
@@ -480,7 +484,10 @@ class Base(_RegisteringBase, speckle_type="Base"):
@units.setter
def units(self, value: Union[str, Units, None]):
"""While this property accepts any string value, geometry expects units to be specific strings (see Units enum)"""
"""
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):
+8 -6
View File
@@ -1,10 +1,10 @@
from enum import Enum
from typing import Any, List, Optional
from specklepy.objects.base import Base
from specklepy.objects.encoding import CurveArray, CurveTypeEncoding, ObjectArray
from specklepy.objects.primitive import Interval
from specklepy.objects.units import get_encoding_from_units, get_units_from_encoding
from specklepy.objects_v2.base import Base
from specklepy.objects_v2.encoding import CurveArray, CurveTypeEncoding, ObjectArray
from specklepy.objects_v2.primitive import Interval
from specklepy.objects_v2.units import get_encoding_from_units, get_units_from_encoding
GEOMETRY = "Objects.Geometry."
@@ -918,10 +918,12 @@ class Brep(
self.Vertices = vertices
# TODO: can this be consistent with loops, edges, faces, curves, etc and prepend with the chunk list? needs to happen in sharp first
# TODO: can this be consistent with loops, edges, faces, curves,
# etc and prepend with the chunk list? needs to happen in sharp first
@property
def TrimsValue(self) -> List[float]:
# return None if self.Trims is None else ObjectArray.from_objects(self.Trims).data
# return None if self.Trims is None else
# ObjectArray.from_objects(self.Trims).data
if not self.Trims:
return
value = []
@@ -58,9 +58,7 @@ class CommitObjectBuilder(ABC, Generic[T]):
if parent_id == ROOT:
parent = root_commit_object
else:
parent = (
self.converted[parent_id] if parent_id in self.converted else None
)
parent = self.converted.get(parent_id, None)
if not parent:
continue
@@ -74,13 +72,15 @@ class CommitObjectBuilder(ABC, Generic[T]):
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)
# 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!"
f"Could not find a valid parent for object of type {type(current)}."
f"Checked {len(parents)} potential parent, and non were converted!"
)
@@ -58,12 +58,16 @@ class GraphTraversal:
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
# debug: to avoid noisy exceptions,
# explicitly avoid checking ones we know will fail,
# this is not exhaustive
continue
if getattr(current, child_prop, None):
value = current[child_prop]
self._traverse_member_to_stack(stack, value, child_prop, head)
except KeyError:
# Unset application ids, and class variables like SpeckleType will throw when __getitem__ is called
# Unset application ids, and class variables like SpeckleType will
# throw when __getitem__ is called
pass
@staticmethod
@@ -119,7 +123,4 @@ class TraversalRule:
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
return any(condition(o) for condition in self._conditions)
@@ -30,6 +30,7 @@ def safe_json_loads(obj: str, obj_id=None) -> Any:
f"Failed to deserialise object (id: {obj_id}). This is likely a ujson big"
f" int error - falling back to json. \nError: {err}",
SpeckleWarning,
stacklevel=2,
)
return json.loads(obj)
@@ -140,7 +141,8 @@ class BaseObjectSerializer:
object_builder[prop] = value
continue
# NOTE: for dynamic props, this won't be re-serialised as an enum but as an int
# NOTE: for dynamic props, this won't be re-serialised
# as an enum but as an int
if isinstance(value, Enum):
object_builder[prop] = value.value
continue
@@ -222,7 +224,7 @@ class BaseObjectSerializer:
if isinstance(obj, Enum):
return obj.value
elif isinstance(obj, (list, tuple, set)):
elif isinstance(obj, list | tuple | set):
if not detach:
return [self.traverse_value(o) for o in obj]
@@ -257,6 +259,7 @@ class BaseObjectSerializer:
f"Failed to handle {type(obj)} in"
" `BaseObjectSerializer.traverse_value`",
SpeckleWarning,
stacklevel=2,
)
return str(obj)
@@ -374,6 +377,7 @@ class BaseObjectSerializer:
f"Could not find the referenced child object of id `{ref_id}`"
f" in the given read transport: {self.read_transport.name}",
SpeckleWarning,
stacklevel=2,
)
base.__setattr__(prop, self.handle_value(value))
@@ -437,6 +441,7 @@ class BaseObjectSerializer:
f"Could not find the referenced child object of id `{ref_id}` in the"
f" given read transport: {self.read_transport.name}",
SpeckleWarning,
stacklevel=2,
)
return obj
+2 -2
View File
@@ -27,8 +27,8 @@ class MemoryTransport(AbstractTransport):
) -> None:
raise NotImplementedError
def get_object(self, id: str) -> str or None:
return self.objects[id] if id in self.objects else None
def get_object(self, id: str) -> str | None:
return self.objects.get(id, None)
def has_objects(self, id_list: List[str]) -> Dict[str, bool]:
return {id: (id in self.objects) for id in id_list}
@@ -11,7 +11,7 @@ from specklepy.logging.exceptions import SpeckleException
LOG = logging.getLogger(__name__)
class BatchSender(object):
class BatchSender:
def __init__(
self,
server_url,
@@ -123,8 +123,14 @@ class BatchSender(object):
upload_data = "[" + ",".join(new_objects) + "]"
upload_data_gzip = gzip.compress(upload_data.encode())
LOG.info(
"Uploading batch of %s objects (%s new): (size: %s, compressed size: %s)"
% (len(batch), len(new_objects), len(upload_data), len(upload_data_gzip))
"Uploading batch of {batch_size} objects {new_object_count}: ",
"(size: {upload_size}, compressed size: {upload_data_size})",
{
"batch_size": len(batch),
"new_object_count": len(new_objects),
"upload_size": len(upload_data),
"upload_data_size": len(upload_data_gzip),
},
)
try:
+2 -1
View File
@@ -74,7 +74,8 @@ class ServerTransport(AbstractTransport):
SpeckleWarning(
"Unauthenticated Speckle Client provided to Server Transport"
f" for {url}. Receiving from private streams will fail."
)
),
stacklevel=2,
)
else:
self.account = client.account
+3 -5
View File
@@ -39,8 +39,7 @@ class SQLiteTransport(AbstractTransport):
f"SQLiteTransport could not initialise {self.scope}.db at"
f" {self._base_path}. Either provide a different `base_path` or use an"
" alternative transport.",
ex,
)
) from ex
def __repr__(self) -> str:
return f"SQLiteTransport(app: '{self.app_name}', scope: '{self.scope}')"
@@ -105,10 +104,9 @@ class SQLiteTransport(AbstractTransport):
raise SpeckleException(
"Could not save the batch of objects to the local db. Inner exception:"
f" {ex}",
ex,
)
) from ex
def get_object(self, id: str) -> str or None:
def get_object(self, id: str) -> str | None:
self.__check_connection()
with closing(self.__connection.cursor()) as c:
row = c.execute(
@@ -45,7 +45,8 @@ class TestActiveUserResource:
def test_active_user_get_projects_with_filter(self, client: SpeckleClient):
# Since the client may be reused for other tests,
# this test does rely on no other test creating a project with "Search for me" in its name
# this test does rely on no other test creating a project
# with "Search for me" in its name
p1 = client.project.create(
ProjectCreateInput(name="Search for me!", description=None, visibility=None)
)
@@ -33,8 +33,8 @@ class TestOtherUser:
assert isinstance(fetched_user, LimitedUser)
assert fetched_user.name == second_user_dict["name"]
# changed in the server, now you cannot get emails of other users
# not checking this, since the first user could or could not be an admin on the server
# admins can get emails of others, regular users can't
# not checking this, since the first user could or could not be an admin
# on the server, admins can get emails of others, regular users can't
# assert fetched_user.email == None
second_user_dict["id"] = fetched_user.id
@@ -36,8 +36,8 @@ class TestUser:
assert isinstance(fetched_user, User)
assert fetched_user.name == second_user_dict["name"]
# changed in the server, now you cannot get emails of other users
# not checking this, since the first user could or could not be an admin on the server
# admins can get emails of others, regular users can't
# not checking this, since the first user could or could not be an admin
# on the server, admins can get emails of others, regular users can't
# assert fetched_user.email == None
second_user_dict["id"] = fetched_user.id
+8 -10
View File
@@ -1,3 +1,4 @@
import contextlib
import json
import tempfile
from pathlib import Path
@@ -16,14 +17,11 @@ def user_path() -> Iterable[Path]:
speckle_path_provider.override_application_data_path(tempfile.gettempdir())
path = speckle_path_provider.accounts_folder_path().joinpath("test_acc.json")
# hey, py37 doesn't support the missing_ok argument
try:
with contextlib.suppress(Exception):
path.unlink()
except Exception:
pass
try:
with contextlib.suppress(Exception):
path.unlink(missing_ok=True)
except Exception:
pass
path.parent.absolute().mkdir(exist_ok=True)
yield path
if path.exists():
@@ -34,7 +32,7 @@ def user_path() -> Iterable[Path]:
def test_parse_empty():
try:
StreamWrapper("https://testing.speckle.dev/streams")
assert False
raise AssertionError()
except SpeckleException:
assert True
@@ -42,7 +40,7 @@ def test_parse_empty():
def test_parse_empty_fe2():
try:
StreamWrapper("https://latest.speckle.systems/projects")
assert False
raise AssertionError()
except SpeckleException:
assert True
@@ -169,7 +167,7 @@ def test_parse_model():
def test_parse_federated_model():
try:
StreamWrapper("https://latest.speckle.systems/projects/843d07eb10/models/$main")
assert False
raise AssertionError()
except SpeckleException:
assert True
@@ -179,7 +177,7 @@ def test_parse_multi_model():
StreamWrapper(
"https://latest.speckle.systems/projects/2099ac4b5f/models/1870f279e3,a9cfdddc79"
)
assert False
raise AssertionError()
except SpeckleException:
assert True
+2 -2
View File
@@ -6,8 +6,8 @@ import pytest
from specklepy.api import operations
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
from specklepy.objects.base import Base
from specklepy.objects.units import Units
from specklepy.objects_v2.base import Base
from specklepy.objects_v2.units import Units
@pytest.mark.parametrize(
+4 -4
View File
@@ -6,8 +6,8 @@ import pytest
from specklepy.api import operations
from specklepy.logging.exceptions import SpeckleException
from specklepy.objects.base import Base
from specklepy.objects.encoding import CurveArray, ObjectArray
from specklepy.objects.geometry import (
from specklepy.objects_v2.encoding import CurveArray, ObjectArray
from specklepy.objects_v2.geometry import (
Arc,
Box,
Brep,
@@ -30,7 +30,7 @@ from specklepy.objects.geometry import (
Surface,
Vector,
)
from specklepy.objects.units import Units
from specklepy.objects_v2.units import Units
from specklepy.transports.memory import MemoryTransport
@@ -508,7 +508,7 @@ def test_serialized_brep_attributes(brep: Brep):
]
for k in removed_keys:
assert k not in serialized_dict.keys()
assert k not in serialized_dict
def test_mesh_create():
+2 -2
View File
@@ -2,8 +2,8 @@ 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
from specklepy.objects_v2 import Base
from specklepy.objects_v2.graph_traversal.traversal import GraphTraversal, TraversalRule
@dataclass()
+2 -2
View File
@@ -2,8 +2,8 @@ from typing import Type
import pytest
from specklepy.objects.base import Base
from specklepy.objects.structural import Concrete
from specklepy.objects_v2.base import Base
from specklepy.objects_v2.structural import Concrete
class Foo(Base):
+6 -6
View File
@@ -1,8 +1,8 @@
import pytest
from specklepy.objects.geometry import Line, Mesh, Point, Vector
from specklepy.objects.structural.analysis import Model
from specklepy.objects.structural.geometry import (
from specklepy.objects_v2.geometry import Line, Mesh, Point, Vector
from specklepy.objects_v2.structural.analysis import Model
from specklepy.objects_v2.structural.geometry import (
Element1D,
Element2D,
ElementType1D,
@@ -10,9 +10,9 @@ from specklepy.objects.structural.geometry import (
Node,
Restraint,
)
from specklepy.objects.structural.loading import LoadGravity
from specklepy.objects.structural.materials import StructuralMaterial
from specklepy.objects.structural.properties import (
from specklepy.objects_v2.structural.loading import LoadGravity
from specklepy.objects_v2.structural.materials import StructuralMaterial
from specklepy.objects_v2.structural.properties import (
MemberType,
Property1D,
Property2D,
+2 -2
View File
@@ -3,8 +3,8 @@ from typing import List
import pytest
from specklepy.api import operations
from specklepy.objects.geometry import Point, Vector
from specklepy.objects.other import Transform
from specklepy.objects_v2.geometry import Point, Vector
from specklepy.objects_v2.other import Transform
@pytest.fixture()
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import List
from specklepy.objects.base import Base
from specklepy.objects_v2.base import Base
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
+2 -2
View File
@@ -3,8 +3,8 @@ from typing import Any, Dict, List, Optional, Set, Tuple, Union
import pytest
from specklepy.objects.base import Base, _validate_type
from specklepy.objects.primitive import Interval
from specklepy.objects_v2.base import Base, _validate_type
from specklepy.objects_v2.primitive import Interval
test_base = Base()
+1 -1
View File
@@ -1,6 +1,6 @@
import pytest
from specklepy.objects.units import Units, get_scale_factor
from specklepy.objects_v2.units import Units, get_scale_factor
@pytest.mark.parametrize(
+9 -5
View File
@@ -67,7 +67,7 @@ def user_application_data_path() -> Path:
else:
return _ensure_folder_exists(Path.home(), ".config")
except Exception as ex:
raise Exception("Failed to initialize user application data path.", ex)
raise Exception("Failed to initialize user application data path.") from ex
def user_speckle_folder_path() -> Path:
@@ -162,7 +162,10 @@ def install_requirements(host_application: str) -> None:
)
if completed_process.returncode != 0:
m = f"Failed to install dependenices through pip, got {completed_process.returncode} return code"
m = (
"Failed to install dependenices through pip, got ",
f"{completed_process.returncode} return code",
)
print(m)
raise Exception(m)
@@ -197,7 +200,8 @@ def ensure_dependencies(host_application: str) -> None:
invalidate_caches()
_import_dependencies()
print("Successfully found dependencies")
except ImportError:
except ImportError as e:
raise Exception(
f"Cannot automatically ensure Speckle dependencies. Please try restarting the host application {host_application}!"
)
"Cannot automatically ensure Speckle dependencies.",
f"Please try restarting the host application {host_application}!",
) from e
Generated
+1415
View File
File diff suppressed because it is too large Load Diff