Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9303af6827 | |||
| 973dc07d5b | |||
| 7dd5b7a2a1 | |||
| f259f256c7 | |||
| 08986056a3 | |||
| f89b07eacb | |||
| c973d916b3 | |||
| 4ff6288317 | |||
| 8566674f2e | |||
| 1f3b6da9c7 | |||
| 5d99d5fcad | |||
| 4fc07f33d0 | |||
| 4e23a69b89 | |||
| 04a0ddc8c4 | |||
| 1b4d43e0aa | |||
| f78c8c407f | |||
| 892c11f38f | |||
| 72639bf4bb | |||
| b2c210abc1 | |||
| 2250e8a897 | |||
| cb07f55551 | |||
| d1b3d5e25e | |||
| 79cca557f5 | |||
| 1e6e66a90a | |||
| 09d84cf64a | |||
| 3ccb0ae2a8 | |||
| 6028a38355 | |||
| 07418cfc9c | |||
| 1ada797d81 | |||
| 73703f6237 | |||
| 7644af22df | |||
| 564e1d4432 | |||
| fc4511ad02 | |||
| ad710b72da | |||
| 041d9f56ce | |||
| e1c0b705ad | |||
| 7b011b1122 | |||
| 3f09cd9d77 | |||
| 29a361892b | |||
| 2672b40aff | |||
| 35b6911b27 | |||
| a4f0a2cc2b | |||
| 1970890ecc | |||
| 13df5135b8 | |||
| 4e206b5c60 | |||
| e696091555 | |||
| a512dbb4e4 | |||
| 9a1f28516d | |||
| 92892b83d8 | |||
| 8904e9eeb4 | |||
| 68dc1794ee | |||
| 7e7940f25b | |||
| 29c97cde45 | |||
| 02f4f4fe41 | |||
| b2dd5bfedd | |||
| 09b3edcc23 | |||
| a44036863d | |||
| 5806c032dd | |||
| cff20aec54 | |||
| 144d51b147 | |||
| 09f61a6efd | |||
| e61bf0f78f | |||
| 6ac72ce8ee | |||
| 0b9ef942f5 | |||
| 6988eae46f | |||
| 31fa619f82 | |||
| 409ac68df0 | |||
| 81051a87c1 | |||
| fca386706b |
+11
-8
@@ -4,7 +4,7 @@ orbs:
|
||||
python: circleci/python@1.3.2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
test:
|
||||
docker:
|
||||
- image: "cimg/python:<<parameters.tag>>"
|
||||
- image: "circleci/node:12"
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
POSTGRES_DB: speckle2_test
|
||||
POSTGRES_PASSWORD: speckle
|
||||
POSTGRES_USER: speckle
|
||||
- image: "speckle/speckle-server:5f8cf11cba07ea6a54000243f9cb343b61cbba13"
|
||||
- image: "speckle/speckle-server"
|
||||
command: ["bash", "-c", "/wait && node bin/www"]
|
||||
environment:
|
||||
POSTGRES_URL: "localhost"
|
||||
@@ -38,27 +38,30 @@ jobs:
|
||||
name: upgrade pip
|
||||
- python/install-packages:
|
||||
pkg-manager: poetry
|
||||
- run: poetry run pytest --version
|
||||
- run: poetry run pytest
|
||||
|
||||
deploy:
|
||||
docker:
|
||||
- image: "circleci/python:3.8"
|
||||
steps:
|
||||
- checkout
|
||||
- run: python patch_version.py $CIRCLE_TAG
|
||||
- run: poetry build
|
||||
- run: poetry publish -u specklesystems -p $PYPI_PASSWORD
|
||||
|
||||
workflows:
|
||||
main:
|
||||
jobs:
|
||||
- build:
|
||||
jobs:
|
||||
- test:
|
||||
matrix:
|
||||
parameters:
|
||||
tag: ["3.6", "3.7", "3.8", "3.9"]
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- deploy:
|
||||
requires:
|
||||
- test
|
||||
filters:
|
||||
tags:
|
||||
only: /[0-9]+(\.[0-9]+)*/
|
||||
|
||||
@@ -45,7 +45,7 @@ from specklepy.api.credentials import get_default_account, get_local_accounts
|
||||
all_accounts = get_local_accounts() # get back a list
|
||||
account = get_default_account()
|
||||
|
||||
client = SpeckleClient(host="localhost:3000", use_ssl=False)
|
||||
client = SpeckleClient(host="speckle.xyz")
|
||||
# client = SpeckleClient(host="yourserver.com") or whatever your host is
|
||||
|
||||
client.authenticate(account.token)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def patch(tag):
|
||||
print(f"Patching version: {tag}")
|
||||
|
||||
with open("pyproject.toml", "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
if "version" not in lines[2]:
|
||||
raise Exception(f"Invalid pyproject.toml. Could not patch version.")
|
||||
|
||||
lines[2] = f'version = "{tag}"\n'
|
||||
with open("pyproject.toml", "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
return
|
||||
|
||||
tag = sys.argv[1]
|
||||
if not re.match(r"[0-9]+(\.[0-9]+)*$", tag):
|
||||
raise ValueError(f"Invalid tag provided: {tag}")
|
||||
|
||||
patch(tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Generated
+129
-84
@@ -1,6 +1,6 @@
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.7.3"
|
||||
version = "3.7.4.post0"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -9,7 +9,7 @@ python-versions = ">=3.6"
|
||||
[package.dependencies]
|
||||
async-timeout = ">=3.0,<4.0"
|
||||
attrs = ">=17.3.0"
|
||||
chardet = ">=2.0,<4.0"
|
||||
chardet = ">=2.0,<5.0"
|
||||
idna-ssl = {version = ">=1.0", markers = "python_version < \"3.7\""}
|
||||
multidict = ">=4.5,<7.0"
|
||||
typing-extensions = ">=3.6.5"
|
||||
@@ -81,7 +81,7 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2020.12.5"
|
||||
version = "2021.5.30"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -89,11 +89,11 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "3.0.4"
|
||||
version = "4.0.0"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
@@ -121,28 +121,31 @@ python-versions = ">=3.6, <3.7"
|
||||
|
||||
[[package]]
|
||||
name = "gql"
|
||||
version = "3.0.0a5"
|
||||
version = "3.0.0a6"
|
||||
description = "GraphQL client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
graphql-core = ">=3.1,<3.2"
|
||||
aiohttp = {version = ">=3.7.1,<3.8.0", optional = true, markers = "extra == \"all\""}
|
||||
graphql-core = ">=3.1.5,<3.2"
|
||||
requests = {version = ">=2.23,<3", optional = true, markers = "extra == \"all\""}
|
||||
websockets = {version = ">=9,<10", optional = true, markers = "extra == \"all\""}
|
||||
yarl = ">=1.6,<2.0"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.7.1,<3.8.0)"]
|
||||
all = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=8.1,<9)"]
|
||||
dev = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=8.1,<9)", "black (==19.10b0)", "check-manifest (>=0.42,<1)", "flake8 (==3.8.1)", "isort (==4.3.21)", "mypy (==0.770)", "sphinx (>=3.0.0,<4)", "sphinx_rtd_theme (>=0.4,<1)", "sphinx-argparse (==0.2.5)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)"]
|
||||
all = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)"]
|
||||
dev = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)", "black (==19.10b0)", "check-manifest (>=0.42,<1)", "flake8 (==3.8.1)", "isort (==4.3.21)", "mypy (==0.770)", "sphinx (>=3.0.0,<4)", "sphinx_rtd_theme (>=0.4,<1)", "sphinx-argparse (==0.2.5)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
requests = ["requests (>=2.23,<3)"]
|
||||
test = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=8.1,<9)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)"]
|
||||
test_no_transport = ["parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)"]
|
||||
websockets = ["websockets (>=8.1,<9)"]
|
||||
test = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
test_no_transport = ["parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
websockets = ["websockets (>=9,<10)"]
|
||||
|
||||
[[package]]
|
||||
name = "graphql-core"
|
||||
version = "3.1.2"
|
||||
version = "3.1.5"
|
||||
description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -368,9 +371,17 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "ujson"
|
||||
version = "4.1.0"
|
||||
description = "Ultra fast JSON encoder and decoder for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.3"
|
||||
version = "1.26.5"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -383,7 +394,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "8.1"
|
||||
version = "9.1"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -417,47 +428,47 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.5"
|
||||
content-hash = "5f77d3e90d0b7a555d5e53d35caedc4d539d21f9c67805219fb94dbb99b186e1"
|
||||
content-hash = "728db0014dfb8a83c50fe5ce6e86d068c4c87d319d50fb1e8135e63507713f30"
|
||||
|
||||
[metadata.files]
|
||||
aiohttp = [
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:328b552513d4f95b0a2eea4c8573e112866107227661834652a8984766aa7656"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c733ef3bdcfe52a1a75564389bad4064352274036e7e234730526d155f04d914"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2858b2504c8697beb9357be01dc47ef86438cc1cb36ecb6991796d19475faa3e"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d2cfac21e31e841d60dc28c0ec7d4ec47a35c608cb8906435d47ef83ffb22150"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:3228b7a51e3ed533f5472f54f70fd0b0a64c48dc1649a0f0e809bec312934d7a"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:dcc119db14757b0c7bce64042158307b9b1c76471e655751a61b57f5a0e4d78e"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:7d9b42127a6c0bdcc25c3dcf252bb3ddc70454fac593b1b6933ae091396deb13"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-win32.whl", hash = "sha256:df48a623c58180874d7407b4d9ec06a19b84ed47f60a3884345b1a5099c1818b"},
|
||||
{file = "aiohttp-3.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:0b795072bb1bf87b8620120a6373a3c61bfcb8da7e5c2377f4bb23ff4f0b62c9"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:0d438c8ca703b1b714e82ed5b7a4412c82577040dadff479c08405e2a715564f"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8389d6044ee4e2037dca83e3f6994738550f6ee8cfb746762283fad9b932868f"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3ea8c252d8df5e9166bcf3d9edced2af132f4ead8ac422eac723c5781063709a"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:78e2f18a82b88cbc37d22365cf8d2b879a492faedb3f2975adb4ed8dfe994d3a"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:df3a7b258cc230a65245167a202dd07320a5af05f3d41da1488ba0fa05bc9347"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:f326b3c1bbfda5b9308252ee0dcb30b612ee92b0e105d4abec70335fab5b1245"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5e479df4b2d0f8f02133b7e4430098699450e1b2a826438af6bec9a400530957"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-win32.whl", hash = "sha256:6d42debaf55450643146fabe4b6817bb2a55b23698b0434107e892a43117285e"},
|
||||
{file = "aiohttp-3.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9c58b0b84055d8bc27b7df5a9d141df4ee6ff59821f922dd73155861282f6a3"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f411cb22115cb15452d099fec0ee636b06cf81bfb40ed9c02d30c8dc2bc2e3d1"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1e0920909d916d3375c7a1fdb0b1c78e46170e8bb42792312b6eb6676b2f87f"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:59d11674964b74a81b149d4ceaff2b674b3b0e4d0f10f0be1533e49c4a28408b"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:41608c0acbe0899c852281978492f9ce2c6fbfaf60aff0cefc54a7c4516b822c"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:16a3cb5df5c56f696234ea9e65e227d1ebe9c18aa774d36ff42f532139066a5f"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:6ccc43d68b81c424e46192a778f97da94ee0630337c9bbe5b2ecc9b0c1c59001"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:d03abec50df423b026a5aa09656bd9d37f1e6a49271f123f31f9b8aed5dc3ea3"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-win32.whl", hash = "sha256:39f4b0a6ae22a1c567cb0630c30dd082481f95c13ca528dc501a7766b9c718c0"},
|
||||
{file = "aiohttp-3.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:c68fdf21c6f3573ae19c7ee65f9ff185649a060c9a06535e9c3a0ee0bbac9235"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:710376bf67d8ff4500a31d0c207b8941ff4fba5de6890a701d71680474fe2a60"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2406dc1dda01c7f6060ab586e4601f18affb7a6b965c50a8c90ff07569cf782a"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2a7b7640167ab536c3cb90cfc3977c7094f1c5890d7eeede8b273c175c3910fd"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:684850fb1e3e55c9220aad007f8386d8e3e477c4ec9211ae54d968ecdca8c6f9"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:1edfd82a98c5161497bbb111b2b70c0813102ad7e0aa81cbeb34e64c93863005"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:77149002d9386fae303a4a162e6bce75cc2161347ad2ba06c2f0182561875d45"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:756ae7efddd68d4ea7d89c636b703e14a0c686688d42f588b90778a3c2fc0564"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-win32.whl", hash = "sha256:3b0036c978cbcc4a4512278e98e3e6d9e6b834dc973206162eddf98b586ef1c6"},
|
||||
{file = "aiohttp-3.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:e1b95972a0ae3f248a899cdbac92ba2e01d731225f566569311043ce2226f5e7"},
|
||||
{file = "aiohttp-3.7.3.tar.gz", hash = "sha256:9c1a81af067e72261c9cbe33ea792893e83bc6aa987bfbd6fdc1e5e7b22777c4"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"},
|
||||
{file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"},
|
||||
]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
@@ -479,12 +490,12 @@ black = [
|
||||
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
||||
{file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
|
||||
{file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
|
||||
]
|
||||
chardet = [
|
||||
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
|
||||
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
|
||||
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
|
||||
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||
@@ -499,11 +510,11 @@ dataclasses = [
|
||||
{file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"},
|
||||
]
|
||||
gql = [
|
||||
{file = "gql-3.0.0a5.tar.gz", hash = "sha256:a87bc42934a70902c005a317567cc57e460b160a9c606db8be62e618e29c336c"},
|
||||
{file = "gql-3.0.0a6.tar.gz", hash = "sha256:bdcbf60bc37b11d6d2f2ed271f69292c4e96d56df7000ba1dad52e487330bdce"},
|
||||
]
|
||||
graphql-core = [
|
||||
{file = "graphql-core-3.1.2.tar.gz", hash = "sha256:c056424cbdaa0ff67446e4379772f43746bad50a44ec23d643b9bdcd052f5b3a"},
|
||||
{file = "graphql_core-3.1.2-py3-none-any.whl", hash = "sha256:b1826fbd1c6c290f7180d758ecf9c3859a46574cff324bf35a10167533c0e463"},
|
||||
{file = "graphql-core-3.1.5.tar.gz", hash = "sha256:a755635d1d364a17e8d270347000722351aaa03f1ab7d280878aae82fc68b1f3"},
|
||||
{file = "graphql_core-3.1.5-py3-none-any.whl", hash = "sha256:91d96ef0e86665777bb7115d3bbb6b0326f43dc7dbcdd60da5486a27a50cfb11"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
@@ -708,33 +719,67 @@ typing-extensions = [
|
||||
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
|
||||
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
|
||||
]
|
||||
ujson = [
|
||||
{file = "ujson-4.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:148680f2bc6e52f71c56908b65f59b36a13611ac2f75a86f2cb2bce2b2c2588c"},
|
||||
{file = "ujson-4.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c2fb32976982e4e75ca0843a1e7b2254b8c5d8c45d979ebf2db29305b4fa31"},
|
||||
{file = "ujson-4.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:971d4b450e689bfec8ad6b22060fb9b9bec1e0860dbdf0fa7cfe4068adbc5f58"},
|
||||
{file = "ujson-4.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f453480b275192ae40ef350a4e8288977f00b02e504ed34245ebd12d633620cb"},
|
||||
{file = "ujson-4.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f135db442e5470d9065536745968efc42a60233311c8509b9327bcd59a8821c7"},
|
||||
{file = "ujson-4.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:2251fc9395ba4498cbdc48136a179b8f20914fa8b815aa9453b20b48ad120f43"},
|
||||
{file = "ujson-4.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9005d0d952d0c1b3dff5cdb79df2bde35a3499e2de3f708a22c45bbb4089a1f6"},
|
||||
{file = "ujson-4.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:117855246a9ea3f61f3b69e5ca1b1d11d622b3126f50a0ec08b577cb5c87e56e"},
|
||||
{file = "ujson-4.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:989bed422e7e20c7ba740a4e1bbeb28b3b6324e04f023ea238a2e5449fc53668"},
|
||||
{file = "ujson-4.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:44993136fd2ecade747b6db95917e4f015a3279e09a08113f70cbbd0d241e66a"},
|
||||
{file = "ujson-4.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9e962df227fd1d851ff095382a9f8432c2470c3ee640f02ae14231dc5728e6f3"},
|
||||
{file = "ujson-4.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6013cda610c5149fb80a84ee815b210aa2e7fe4edf1d2bce42c02336715208"},
|
||||
{file = "ujson-4.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41b7e5422184249b5b94d1571206f76e5d91e8d721ce51abe341a88f41dd6692"},
|
||||
{file = "ujson-4.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:807bb0585f30a650ec981669827721ed3ee1ee24f2c6f333a64982a40eb66b82"},
|
||||
{file = "ujson-4.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d2955dd5cce0e76ba56786d647aaedca2cebb75eda9f0ec1787110c3646751a8"},
|
||||
{file = "ujson-4.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a873c93d43f9bd14d9e9a6d2c6eb7aae4aad9717fe40c748d0cd4b6ed7767c62"},
|
||||
{file = "ujson-4.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8fe9bbeca130debb10eea7910433a0714c8efc057fad36353feccb87c1d07f"},
|
||||
{file = "ujson-4.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81a49dbf176ae041fc86d2da564f5b9b46faf657306035632da56ecfd7203193"},
|
||||
{file = "ujson-4.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1fb2455e62f20ab4a6d49f78b5dc4ff99c72fdab9466e761120e9757fa35f4d7"},
|
||||
{file = "ujson-4.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:44db30b8fc52e70a6f67def11804f74818addafef0a65cd7f0abb98b7830920f"},
|
||||
{file = "ujson-4.1.0.tar.gz", hash = "sha256:22b63ec4409f0d2f2c4c9d5aa331997e02470b7a15a3233f3cc32f2f9b92d58c"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
||||
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
||||
{file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"},
|
||||
{file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"},
|
||||
]
|
||||
websockets = [
|
||||
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
|
||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
|
||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"},
|
||||
{file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"},
|
||||
{file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"},
|
||||
{file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"},
|
||||
{file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"},
|
||||
{file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"},
|
||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"},
|
||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"},
|
||||
{file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"},
|
||||
{file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"},
|
||||
{file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"},
|
||||
{file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"},
|
||||
{file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"},
|
||||
{file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"},
|
||||
{file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"},
|
||||
{file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"},
|
||||
{file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"},
|
||||
{file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"},
|
||||
{file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"},
|
||||
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
||||
{file = "websockets-9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d144b350045c53c8ff09aa1cfa955012dd32f00c7e0862c199edcabb1a8b32da"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b4ad84b156cf50529b8ac5cc1638c2cf8680490e3fccb6121316c8c02620a2e4"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2cf04601633a4ec176b9cc3d3e73789c037641001dbfaf7c411f89cd3e04fcaf"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c8f0d82ea2468282e08b0cf5307f3ad022290ed50c45d5cb7767957ca782880"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:caa68c95bc1776d3521f81eeb4d5b9438be92514ec2a79fececda814099c8314"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d2c2d9b24d3c65b5a02cac12cbb4e4194e590314519ed49db2f67ef561c3cf58"},
|
||||
{file = "websockets-9.1-cp36-cp36m-win32.whl", hash = "sha256:f31722f1c033c198aa4a39a01905951c00bd1c74f922e8afc1b1c62adbcdd56a"},
|
||||
{file = "websockets-9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3ddff38894c7857c476feb3538dd847514379d6dc844961dc99f04b0384b1b1b"},
|
||||
{file = "websockets-9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:51d04df04ed9d08077d10ccbe21e6805791b78eac49d16d30a1f1fe2e44ba0af"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f68c352a68e5fdf1e97288d5cec9296664c590c25932a8476224124aaf90dbcd"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b43b13e5622c5a53ab12f3272e6f42f1ce37cd5b6684b2676cb365403295cd40"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9147868bb0cc01e6846606cd65cbf9c58598f187b96d14dd1ca17338b08793bb"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:836d14eb53b500fd92bd5db2fc5894f7c72b634f9c2a28f546f75967503d8e25"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:48c222feb3ced18f3dc61168ca18952a22fb88e5eb8902d2bf1b50faefdc34a2"},
|
||||
{file = "websockets-9.1-cp37-cp37m-win32.whl", hash = "sha256:900589e19200be76dd7cbaa95e9771605b5ce3f62512d039fb3bc5da9014912a"},
|
||||
{file = "websockets-9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ab5ee15d3462198c794c49ccd31773d8a2b8c17d622aa184f669d2b98c2f0857"},
|
||||
{file = "websockets-9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85e701a6c316b7067f1e8675c638036a796fe5116783a4c932e7eb8e305a3ffe"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b2e71c4670ebe1067fa8632f0d081e47254ee2d3d409de54168b43b0ba9147e0"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:230a3506df6b5f446fed2398e58dcaafdff12d67fe1397dff196411a9e820d02"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:7df3596838b2a0c07c6f6d67752c53859a54993d4f062689fdf547cb56d0f84f"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:826ccf85d4514609219725ba4a7abd569228c2c9f1968e8be05be366f68291ec"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0dd4eb8e0bbf365d6f652711ce21b8fd2b596f873d32aabb0fbb53ec604418cc"},
|
||||
{file = "websockets-9.1-cp38-cp38-win32.whl", hash = "sha256:1d0971cc7251aeff955aa742ec541ee8aaea4bb2ebf0245748fbec62f744a37e"},
|
||||
{file = "websockets-9.1-cp38-cp38-win_amd64.whl", hash = "sha256:7189e51955f9268b2bdd6cc537e0faa06f8fffda7fb386e5922c6391de51b077"},
|
||||
{file = "websockets-9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9e5fd6dbdf95d99bc03732ded1fc8ef22ebbc05999ac7e0c7bf57fe6e4e5ae2"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9e7fdc775fe7403dbd8bc883ba59576a6232eac96dacb56512daacf7af5d618d"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:597c28f3aa7a09e8c070a86b03107094ee5cdafcc0d55f2f2eac92faac8dc67d"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:ad893d889bc700a5835e0a95a3e4f2c39e91577ab232a3dc03c262a0f8fc4b5c"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:1d6b4fddb12ab9adf87b843cd4316c4bd602db8d5efd2fb83147f0458fe85135"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:ebf459a1c069f9866d8569439c06193c586e72c9330db1390af7c6a0a32c4afd"},
|
||||
{file = "websockets-9.1-cp39-cp39-win32.whl", hash = "sha256:be5fd35e99970518547edc906efab29afd392319f020c3c58b0e1a158e16ed20"},
|
||||
{file = "websockets-9.1-cp39-cp39-win_amd64.whl", hash = "sha256:85db8090ba94e22d964498a47fdd933b8875a1add6ebc514c7ac8703eb97bbf0"},
|
||||
{file = "websockets-9.1.tar.gz", hash = "sha256:276d2339ebf0df4f45df453923ebd2270b87900eda5dfd4a6b0cfa15f82111c3"},
|
||||
]
|
||||
yarl = [
|
||||
{file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"},
|
||||
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "specklepy"
|
||||
version = "2.2.1"
|
||||
version = "2.1.0"
|
||||
description = "The Python SDK for Speckle 2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Speckle Systems <devops@speckle.systems>"]
|
||||
@@ -13,11 +13,9 @@ homepage = "https://speckle.systems/"
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.6.5"
|
||||
pydantic = "^1.7.3"
|
||||
gql = "^3.0.0a5"
|
||||
aiohttp = "^3.7.3"
|
||||
appdirs = "^1.4.4"
|
||||
requests = "^2.25.1"
|
||||
websockets = "^8.1"
|
||||
gql = {version = ">=3.0.0a6", extras = ["all"], allow-prereleases = true}
|
||||
ujson = "^4.1.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
|
||||
@@ -21,6 +21,33 @@ from gql.transport.websockets import WebsocketsTransport
|
||||
|
||||
|
||||
class SpeckleClient:
|
||||
"""
|
||||
The `SpeckleClient` is your entry point for interacting with your Speckle Server's GraphQL API.
|
||||
You'll need to have access to a server to use it, or you can use our public server `speckle.xyz`.
|
||||
|
||||
To authenticate the client, you'll need to have downloaded the [Speckle Manager](https://speckle.guide/#speckle-manager)
|
||||
and added your account.
|
||||
|
||||
```py
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.credentials import get_default_account
|
||||
|
||||
# initialise the client
|
||||
client = SpeckleClient(host="speckle.xyz") # or whatever your host is
|
||||
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
|
||||
|
||||
# authenticate the client with a token (account has been added in Speckle Manager)
|
||||
account = get_default_account()
|
||||
client.authenticate(token=account.token)
|
||||
|
||||
# create a new stream. this returns the stream id
|
||||
new_stream_id = client.stream.create(name="a shiny new stream")
|
||||
|
||||
# use that stream id to get the stream from the server
|
||||
new_stream = client.stream.get(id=new_stream_id)
|
||||
```
|
||||
"""
|
||||
|
||||
DEFAULT_HOST = "speckle.xyz"
|
||||
USE_SSL = True
|
||||
|
||||
@@ -55,6 +82,11 @@ class SpeckleClient:
|
||||
except Exception as ex:
|
||||
raise SpeckleException(f"{self.url} is not a compatible Speckle Server", ex)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"SpeckleClient( server: {self.url}, authenticated: {self.me is not None} )"
|
||||
)
|
||||
|
||||
def authenticate(self, token: str) -> None:
|
||||
"""Authenticate the client using a personal access token
|
||||
The token is saved in the client object and a synchronous GraphQL entrypoint is created
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import os
|
||||
from typing import List, Optional
|
||||
from specklepy.transports.server.server import ServerTransport
|
||||
from warnings import warn
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from urllib.parse import urlparse, unquote
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
@@ -38,11 +42,9 @@ def get_local_accounts(base_path: str = None) -> List[Account]:
|
||||
List[Account] -- list of all local accounts or an empty list if no accounts were found
|
||||
"""
|
||||
account_storage = SQLiteTransport(scope="Accounts", base_path=base_path)
|
||||
json_acct_files = [
|
||||
file
|
||||
for file in os.listdir(account_storage._base_path)
|
||||
if file.endswith(".json")
|
||||
]
|
||||
json_path = os.path.join(account_storage._base_path, "Accounts")
|
||||
os.makedirs(json_path, exist_ok=True)
|
||||
json_acct_files = [file for file in os.listdir(json_path) if file.endswith(".json")]
|
||||
|
||||
accounts = []
|
||||
res = account_storage.get_all_objects()
|
||||
@@ -51,7 +53,7 @@ def get_local_accounts(base_path: str = None) -> List[Account]:
|
||||
if json_acct_files:
|
||||
try:
|
||||
accounts.extend(
|
||||
Account.parse_file(os.path.join(account_storage._base_path, json_file))
|
||||
Account.parse_file(os.path.join(json_path, json_file))
|
||||
for json_file in json_acct_files
|
||||
)
|
||||
except Exception as ex:
|
||||
@@ -81,3 +83,100 @@ def get_default_account(base_path: str = None) -> Account:
|
||||
default.isDefault = True
|
||||
|
||||
return default
|
||||
|
||||
|
||||
class StreamWrapper:
|
||||
stream_url: str = None
|
||||
use_ssl: bool = True
|
||||
host: str = None
|
||||
stream_id: str = None
|
||||
commit_id: str = None
|
||||
object_id: str = None
|
||||
branch_name: str = None
|
||||
client: SpeckleClient = None
|
||||
account: Account = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type: {self.type} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
if self.object_id:
|
||||
return "object"
|
||||
elif self.commit_id:
|
||||
return "commit"
|
||||
elif self.branch_name:
|
||||
return "branch"
|
||||
else:
|
||||
return "stream" if self.stream_id else "invalid"
|
||||
|
||||
def __init__(self, url: str) -> None:
|
||||
self.stream_url = url
|
||||
parsed = urlparse(url)
|
||||
self.host = parsed.netloc
|
||||
self.use_ssl = parsed.scheme == "https"
|
||||
segments = parsed.path.strip("/").split("/")
|
||||
|
||||
if not segments or len(segments) > 4 or len(segments) < 2:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
while segments:
|
||||
segment = segments.pop(0)
|
||||
if segments and segment.lower() == "streams":
|
||||
self.stream_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "commits":
|
||||
self.commit_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "branches":
|
||||
self.branch_name = unquote(segments.pop(0))
|
||||
elif segments and segment.lower() == "objects":
|
||||
self.object_id = segments.pop(0)
|
||||
elif segment.lower() == "globals":
|
||||
self.branch_name = "globals"
|
||||
if segments:
|
||||
self.commit_id = segments.pop(0)
|
||||
else:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
if not self.stream_id:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - no stream id found."
|
||||
)
|
||||
|
||||
def get_account(self) -> Account:
|
||||
if self.account:
|
||||
return self.account
|
||||
|
||||
self.account = next(
|
||||
(a for a in get_local_accounts() if self.host in a.serverInfo.url),
|
||||
None,
|
||||
)
|
||||
|
||||
return self.account
|
||||
|
||||
def get_client(self) -> SpeckleClient:
|
||||
if self.client:
|
||||
return self.client
|
||||
|
||||
if not self.account:
|
||||
self.get_account()
|
||||
|
||||
self.client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
|
||||
|
||||
if self.account is None:
|
||||
warn(f"No local account found for server {self.host}", SpeckleWarning)
|
||||
return self.client
|
||||
|
||||
self.client.authenticate(self.account.token)
|
||||
return self.client
|
||||
|
||||
def get_transport(self) -> ServerTransport:
|
||||
if not self.client:
|
||||
self.get_client()
|
||||
return ServerTransport(self.client, self.stream_id)
|
||||
@@ -22,6 +22,7 @@ class Commit(BaseModel):
|
||||
authorName: Optional[str]
|
||||
authorId: Optional[str]
|
||||
authorAvatar: Optional[str]
|
||||
branchName: Optional[str]
|
||||
createdAt: Optional[str]
|
||||
sourceApplication: Optional[str]
|
||||
referencedObject: Optional[str]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import json
|
||||
from typing import List
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
@@ -52,7 +51,8 @@ def receive(
|
||||
Arguments:
|
||||
obj_id {str} -- the id of the object to receive
|
||||
remote_transport {Transport} -- the transport to receive from
|
||||
local_transport {Transport} -- the transport to send from
|
||||
local_transport {Transport} -- the local cache to check for existing objects
|
||||
(defaults to `SQLiteTransport`)
|
||||
|
||||
Returns:
|
||||
Base -- the base object
|
||||
@@ -97,9 +97,7 @@ def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str
|
||||
return serializer.write_json(base)[1]
|
||||
|
||||
|
||||
def deserialize(
|
||||
obj_string: str, read_transport: AbstractTransport = SQLiteTransport()
|
||||
) -> Base:
|
||||
def deserialize(obj_string: str, read_transport: AbstractTransport = None) -> Base:
|
||||
"""
|
||||
Deserialize a string object into a Base object. If the object contains referenced child objects that are not stored in the local db, a read transport needs to be provided in order to recompose the base with the children objects.
|
||||
|
||||
@@ -111,6 +109,9 @@ def deserialize(
|
||||
Returns:
|
||||
Base -- the deserialized object
|
||||
"""
|
||||
if not read_transport:
|
||||
read_transport = SQLiteTransport()
|
||||
|
||||
serializer = BaseObjectSerializer(read_transport=read_transport)
|
||||
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from logging import error
|
||||
from specklepy.logging.exceptions import GraphQLException, SpeckleException
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from typing import Dict, List
|
||||
from gql.client import Client
|
||||
from gql.gql import gql
|
||||
from gql.transport.exceptions import TransportQueryError
|
||||
from specklepy.logging.exceptions import GraphQLException, SpeckleException
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
|
||||
|
||||
class ResourceBase(object):
|
||||
@@ -40,7 +41,11 @@ class ResourceBase(object):
|
||||
if schema:
|
||||
return schema.parse_obj(response)
|
||||
elif self.schema:
|
||||
return self.schema.parse_obj(response)
|
||||
try:
|
||||
return self.schema.parse_obj(response)
|
||||
except:
|
||||
s = BaseObjectSerializer(read_transport=SQLiteTransport())
|
||||
return s.recompose_base(response)
|
||||
else:
|
||||
return response
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from typing import Optional, List
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import Commit
|
||||
|
||||
@@ -40,6 +39,7 @@ class Resource(ResourceBase):
|
||||
authorId
|
||||
authorName
|
||||
authorAvatar
|
||||
branchName
|
||||
createdAt
|
||||
sourceApplication
|
||||
totalChildrenCount
|
||||
|
||||
@@ -28,3 +28,8 @@ class GraphQLException(SpeckleException):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"GraphQLException: {self.message}"
|
||||
|
||||
|
||||
class SpeckleWarning(Warning):
|
||||
def __init__(self, *args: object) -> None:
|
||||
super().__init__(*args)
|
||||
+210
-62
@@ -1,15 +1,71 @@
|
||||
from inspect import getattr_static
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.main import Extra
|
||||
import typing
|
||||
from warnings import warn
|
||||
from typing import get_type_hints
|
||||
from typing import ClassVar, Dict, List, Optional, Any, Set, Type
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects.units import get_units_from_string
|
||||
|
||||
|
||||
PRIMITIVES = (int, float, str, bool)
|
||||
|
||||
# to remove from dir() when calling get_member_names()
|
||||
REMOVE_FROM_DIR = {
|
||||
"Config",
|
||||
"_Base__dict_helper",
|
||||
"__annotations__",
|
||||
"__class__",
|
||||
"__delattr__",
|
||||
"__dict__",
|
||||
"__dir__",
|
||||
"__doc__",
|
||||
"__eq__",
|
||||
"__format__",
|
||||
"__ge__",
|
||||
"__getattribute__",
|
||||
"__getitem__",
|
||||
"__gt__",
|
||||
"__hash__",
|
||||
"__init__",
|
||||
"__init_subclass__",
|
||||
"__le__",
|
||||
"__lt__",
|
||||
"__module__",
|
||||
"__ne__",
|
||||
"__new__",
|
||||
"__reduce__",
|
||||
"__reduce_ex__",
|
||||
"__repr__",
|
||||
"__setattr__",
|
||||
"__setitem__",
|
||||
"__sizeof__",
|
||||
"__str__",
|
||||
"__subclasshook__",
|
||||
"__weakref__",
|
||||
"_chunk_size_default",
|
||||
"_chunkable",
|
||||
"_count_descendants",
|
||||
"_attr_types",
|
||||
"_detachable",
|
||||
"_handle_object_count",
|
||||
"_type_check",
|
||||
"_type_registry",
|
||||
"_units",
|
||||
"add_chunkable_attrs",
|
||||
"add_detachable_attrs",
|
||||
"get_children_count",
|
||||
"get_dynamic_member_names",
|
||||
"get_id",
|
||||
"get_member_names",
|
||||
"get_registered_type",
|
||||
"get_typed_member_names",
|
||||
"to_dict",
|
||||
"update_forward_refs",
|
||||
"validate_prop_name",
|
||||
}
|
||||
|
||||
class _RegisteringBase(BaseModel):
|
||||
|
||||
class _RegisteringBase:
|
||||
"""
|
||||
Private Base model for Speckle types.
|
||||
|
||||
@@ -21,7 +77,8 @@ class _RegisteringBase(BaseModel):
|
||||
"""
|
||||
|
||||
speckle_type: ClassVar[str]
|
||||
_type_registry: ClassVar[Dict[str, Type["Base"]]] = {}
|
||||
_type_registry: ClassVar[Dict[str, "Base"]] = {}
|
||||
_attr_types: ClassVar[Dict[str, Type]] = {}
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
@@ -33,7 +90,9 @@ class _RegisteringBase(BaseModel):
|
||||
|
||||
def __init_subclass__(
|
||||
cls,
|
||||
speckle_type: Optional[str] = None,
|
||||
speckle_type: str = None,
|
||||
chunkable: Dict[str, int] = None,
|
||||
detachable: Set[str] = None,
|
||||
**kwargs: Dict[str, Any],
|
||||
):
|
||||
"""
|
||||
@@ -51,6 +110,15 @@ class _RegisteringBase(BaseModel):
|
||||
)
|
||||
cls.speckle_type = speckle_type or cls.__name__
|
||||
cls._type_registry[cls.speckle_type] = cls # type: ignore
|
||||
try:
|
||||
cls._attr_types = get_type_hints(cls)
|
||||
except Exception:
|
||||
cls._attr_types = getattr(cls, "__annotations__", {})
|
||||
if chunkable:
|
||||
chunkable = {k: v for k, v in chunkable.items() if isinstance(v, int)}
|
||||
cls._chunkable = dict(cls._chunkable, **chunkable)
|
||||
if detachable:
|
||||
cls._detachable = cls._detachable.union(detachable)
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
||||
|
||||
@@ -63,6 +131,11 @@ class Base(_RegisteringBase):
|
||||
_chunk_size_default: int = 1000
|
||||
_detachable: Set[str] = set() # list of defined detachable props
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__()
|
||||
for k, v in kwargs.items():
|
||||
self.__setattr__(k, v)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"{self.__class__.__name__}(id: {self.id}, "
|
||||
@@ -73,6 +146,23 @@ class Base(_RegisteringBase):
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@classmethod
|
||||
def of_type(cls, speckle_type: str, **kwargs) -> "Base":
|
||||
"""
|
||||
Get a plain Base object with a specified speckle_type.
|
||||
|
||||
The speckle_type is protected and cannot be overwritten on a class instance.
|
||||
This is to prevent problems with receiving in other platforms or connectors.
|
||||
However, if you really need a base with a different type, here is a helper
|
||||
to do that for you.
|
||||
|
||||
This is used in the deserialisation of unknown types so their speckle_type
|
||||
can be preserved.
|
||||
"""
|
||||
b = cls(**kwargs)
|
||||
b.__dict__.update(speckle_type=speckle_type)
|
||||
return b
|
||||
|
||||
def __setitem__(self, name: str, value: Any) -> None:
|
||||
self.validate_prop_name(name)
|
||||
self.__dict__[name] = value
|
||||
@@ -82,23 +172,46 @@ class Base(_RegisteringBase):
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
"""
|
||||
Guard attribute and property set mechanism.
|
||||
Type checking, guard attribute, and property set mechanism.
|
||||
|
||||
The `speckle_type` is a protected class attribute it must not be overridden.
|
||||
|
||||
This also performs a type check if the attribute is type hinted.
|
||||
"""
|
||||
if name != "speckle_type":
|
||||
attr = getattr(self.__class__, name, None)
|
||||
if isinstance(attr, property):
|
||||
try:
|
||||
attr.__set__(self, value)
|
||||
except AttributeError:
|
||||
pass # the prop probably doesn't have a setter
|
||||
super().__setattr__(name, value)
|
||||
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"
|
||||
# )
|
||||
return
|
||||
# if value is not None:
|
||||
value = self._type_check(name, value)
|
||||
attr = getattr(self.__class__, name, None)
|
||||
if isinstance(attr, property):
|
||||
try:
|
||||
attr.__set__(self, value)
|
||||
except AttributeError:
|
||||
pass # the prop probably doesn't have a setter
|
||||
super().__setattr__(name, value)
|
||||
|
||||
@classmethod
|
||||
def update_forward_refs(cls) -> None:
|
||||
"""
|
||||
Attempts to populate the internal defined types dict for type checking sometime after defining the class.
|
||||
This is already done when defining the class, but can be called again if references to undefined types were
|
||||
included.
|
||||
|
||||
See `objects.geometry` for an example of how this is used with the Brep class definitions
|
||||
"""
|
||||
try:
|
||||
cls._attr_types = get_type_hints(cls)
|
||||
except Exception as e:
|
||||
warn(f"Could not update forward refs for class {cls.__name__}: {e}")
|
||||
|
||||
@classmethod
|
||||
def validate_prop_name(cls, name: str) -> None:
|
||||
"""Validator for dynamic attribute names."""
|
||||
if name in ("", "@"):
|
||||
if name in {"", "@"}:
|
||||
raise ValueError("Invalid Name: Base member names cannot be empty strings")
|
||||
if name.startswith("@@"):
|
||||
raise ValueError(
|
||||
@@ -109,6 +222,45 @@ class Base(_RegisteringBase):
|
||||
"Invalid Name: Base member names cannot contain characters '.' or '/'",
|
||||
)
|
||||
|
||||
def _type_check(self, name: str, value: Any):
|
||||
"""
|
||||
Lightweight type checking of values before setting them
|
||||
|
||||
NOTE: Does not check subscripted types within generics as the performance hit of checking
|
||||
each item within a given collection isn't worth it. Eg if you have a type Dict[str, float],
|
||||
we will only check if the value you're trying to set is a dict.
|
||||
"""
|
||||
types = getattr(self, "_attr_types", {})
|
||||
t = types.get(name, None)
|
||||
|
||||
if t is None:
|
||||
return value
|
||||
|
||||
if t.__module__ == "typing":
|
||||
origin = getattr(t, "__origin__")
|
||||
t = t.__args__ if origin is typing.Union else origin
|
||||
if not isinstance(t, (type, tuple)):
|
||||
warn(
|
||||
f"Unrecognised type '{t}' provided for attribute '{name}'. Type will not been validated."
|
||||
)
|
||||
return value
|
||||
if isinstance(value, t):
|
||||
return value
|
||||
|
||||
# to be friendly, we'll parse ints and strs into floats, but not the other way around
|
||||
# (to avoid unexpected rounding)
|
||||
if t is float and isinstance(value, (int, str, float)):
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if t is str and value is not None:
|
||||
return str(value)
|
||||
|
||||
raise SpeckleException(
|
||||
f"Cannot set '{self.__class__.__name__}.{name}': it expects type '{t.__name__}', but received type '{type(value).__name__}'"
|
||||
)
|
||||
|
||||
def add_chunkable_attrs(self, **kwargs: int) -> None:
|
||||
"""
|
||||
Mark defined attributes as chunkable for serialisation
|
||||
@@ -136,53 +288,50 @@ class Base(_RegisteringBase):
|
||||
def units(self, value: str):
|
||||
self._units = get_units_from_string(value)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convenience method to view the whole base object as a dict"""
|
||||
base_dict = self.__dict__
|
||||
for key, value in base_dict.items():
|
||||
if not value or isinstance(value, PRIMITIVES):
|
||||
continue
|
||||
else:
|
||||
base_dict[key] = self.__dict_helper(value)
|
||||
return base_dict
|
||||
# def to_dict(self) -> Dict[str, Any]:
|
||||
# """Convenience method to view the whole base object as a dict"""
|
||||
# base_dict = self.__dict__
|
||||
# for key, value in base_dict.items():
|
||||
# if not value or isinstance(value, PRIMITIVES):
|
||||
# continue
|
||||
# else:
|
||||
# base_dict[key] = self.__dict_helper(value)
|
||||
# return base_dict
|
||||
|
||||
def __dict_helper(self, obj: Any) -> Any:
|
||||
if not obj or isinstance(obj, PRIMITIVES):
|
||||
return obj
|
||||
if isinstance(obj, Base):
|
||||
return self.__dict_helper(obj.__dict__)
|
||||
if isinstance(obj, (list, set)):
|
||||
return [self.__dict_helper(v) for v in obj]
|
||||
if not isinstance(obj, dict):
|
||||
raise SpeckleException(
|
||||
message=f"Could not convert to dict due to unrecognized type: {type(obj)}"
|
||||
)
|
||||
# def __dict_helper(self, obj: Any) -> Any:
|
||||
# if not obj or isinstance(obj, PRIMITIVES):
|
||||
# return obj
|
||||
# if isinstance(obj, Base):
|
||||
# return self.__dict_helper(obj.__dict__)
|
||||
# if isinstance(obj, (list, set)):
|
||||
# return [self.__dict_helper(v) for v in obj]
|
||||
# if not isinstance(obj, dict):
|
||||
# raise SpeckleException(
|
||||
# message=f"Could not convert to dict due to unrecognized type: {type(obj)}"
|
||||
# )
|
||||
|
||||
for k, v in obj.items():
|
||||
if v and not isinstance(obj, PRIMITIVES):
|
||||
obj[k] = self.__dict_helper(v)
|
||||
return obj
|
||||
# for k, v in obj.items():
|
||||
# if v and not isinstance(obj, PRIMITIVES):
|
||||
# obj[k] = self.__dict_helper(v)
|
||||
# return obj
|
||||
|
||||
def get_member_names(self) -> List[str]:
|
||||
"""Get all of the property names on this object, dynamic or not"""
|
||||
attrs = list(self.__dict__.keys())
|
||||
properties = [
|
||||
# attrs = set(self.__dict__.keys())
|
||||
attr_dir = list(set(dir(self)) - REMOVE_FROM_DIR)
|
||||
return [
|
||||
name
|
||||
for name in dir(self)
|
||||
if not name.startswith("_")
|
||||
and name
|
||||
!= "fields" # soon to be removed as this pydantic prop is depreciated
|
||||
and isinstance(getattr(self, name, None), property)
|
||||
for name in attr_dir
|
||||
if not name.startswith("_") and not callable(getattr(self, name))
|
||||
]
|
||||
return attrs + properties
|
||||
|
||||
def get_typed_member_names(self) -> List[str]:
|
||||
"""Get all of the names of the defined (typed) properties of this object"""
|
||||
return list(self.__fields__.keys())
|
||||
return list(self._attr_types.keys())
|
||||
|
||||
def get_dynamic_member_names(self) -> List[str]:
|
||||
"""Get all of the names of the dynamic properties of this object"""
|
||||
return list(set(self.__dict__.keys()) - set(self.__fields__.keys()))
|
||||
return list(set(self.__dict__.keys()) - set(self._attr_types.keys()))
|
||||
|
||||
def get_children_count(self) -> int:
|
||||
"""Get the total count of children Base objects"""
|
||||
@@ -215,15 +364,11 @@ class Base(_RegisteringBase):
|
||||
return 0
|
||||
parsed.append(base)
|
||||
|
||||
count = 0
|
||||
|
||||
for name, value in base.__dict__.items():
|
||||
if name.startswith("@"):
|
||||
continue
|
||||
else:
|
||||
count += self._handle_object_count(value, parsed)
|
||||
|
||||
return count
|
||||
return sum(
|
||||
self._handle_object_count(value, parsed)
|
||||
for name, value in base.get_member_names()
|
||||
if not name.startswith("@")
|
||||
)
|
||||
|
||||
def _handle_object_count(self, obj: Any, parsed: List) -> int:
|
||||
count = 0
|
||||
@@ -249,9 +394,12 @@ class Base(_RegisteringBase):
|
||||
count += self._handle_object_count(value, parsed)
|
||||
return count
|
||||
|
||||
class Config:
|
||||
extra = Extra.allow
|
||||
|
||||
Base.update_forward_refs()
|
||||
|
||||
|
||||
class DataChunk(Base, speckle_type="Speckle.Core.Models.DataChunk"):
|
||||
data: List[Any] = []
|
||||
data: List[Any] = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.data = []
|
||||
|
||||
@@ -14,7 +14,12 @@ CHUNKABLE_PROPS = {
|
||||
DETACHABLE = {"detach_this", "origin", "detached_list"}
|
||||
|
||||
|
||||
class FakeMesh(Base):
|
||||
class FakeGeo(Base, chunkable={"dots": 50}, detachable={"pointslist"}):
|
||||
pointslist: List[Base] = None
|
||||
dots: List[int] = None
|
||||
|
||||
|
||||
class FakeMesh(FakeGeo, chunkable=CHUNKABLE_PROPS, detachable=DETACHABLE):
|
||||
vertices: List[float] = None
|
||||
faces: List[int] = None
|
||||
colors: List[int] = None
|
||||
@@ -24,10 +29,10 @@ class FakeMesh(Base):
|
||||
detached_list: List[Base] = None
|
||||
_origin: Point = None
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super(FakeMesh, self).__init__(**kwargs)
|
||||
self.add_chunkable_attrs(**CHUNKABLE_PROPS)
|
||||
self.add_detachable_attrs(DETACHABLE)
|
||||
# def __init__(self, **kwargs) -> None:
|
||||
# super(FakeMesh, self).__init__(**kwargs)
|
||||
# self.add_chunkable_attrs(**CHUNKABLE_PROPS)
|
||||
# self.add_detachable_attrs(DETACHABLE)
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
|
||||
@@ -5,25 +5,27 @@ GEOMETRY = "Objects.Geometry."
|
||||
|
||||
|
||||
class Interval(Base, speckle_type="Objects.Primitive.Interval"):
|
||||
start: float = 0
|
||||
end: float = 0
|
||||
start: float = 0.0
|
||||
end: float = 0.0
|
||||
|
||||
def length(self):
|
||||
return abs(self.start - self.end)
|
||||
|
||||
|
||||
class Point(Base, speckle_type=GEOMETRY + "Point"):
|
||||
x: float = 0
|
||||
y: float = 0
|
||||
z: float = 0
|
||||
|
||||
def __init__(self, x: float = 0, y: float = 0, z: float = 0, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.x, self.y, self.z = x, y, z
|
||||
x: float = 0.0
|
||||
y: float = 0.0
|
||||
z: float = 0.0
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, id: {self.id}, speckle_type: {self.speckle_type})"
|
||||
|
||||
@classmethod
|
||||
def from_coords(x: float = 0.0, y: float = 0.0, z: float = 0.0):
|
||||
pt = Point()
|
||||
pt.x, pt.y, pt.z = x, y, z
|
||||
return pt
|
||||
|
||||
|
||||
class Vector(Point, speckle_type=GEOMETRY + "Vector"):
|
||||
pass
|
||||
@@ -92,7 +94,7 @@ class Ellipse(Base, speckle_type=GEOMETRY + "Ellipse"):
|
||||
length: float = None
|
||||
|
||||
|
||||
class Polyline(Base, speckle_type=GEOMETRY + "Polyline"):
|
||||
class Polyline(Base, speckle_type=GEOMETRY + "Polyline", chunkable={"value": 20000}):
|
||||
value: List[float] = None
|
||||
closed: bool = None
|
||||
domain: Interval = None
|
||||
@@ -100,14 +102,11 @@ class Polyline(Base, speckle_type=GEOMETRY + "Polyline"):
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(value=20000)
|
||||
|
||||
@classmethod
|
||||
def from_points(cls, points: List[Point]):
|
||||
polyline = cls()
|
||||
polyline.units = points[0].units
|
||||
polyline.value = []
|
||||
for point in points:
|
||||
polyline.value.extend([point.x, point.y, point.z])
|
||||
return polyline
|
||||
@@ -131,10 +130,16 @@ class Polyline(Base, speckle_type=GEOMETRY + "Polyline"):
|
||||
raise ValueError("Points array malformed: length%3 != 0.")
|
||||
|
||||
values = iter(self.value)
|
||||
return [Point(v, next(values), next(values), units=self.units) for v in values]
|
||||
return [
|
||||
Point(x=v, y=next(values), z=next(values), units=self.units) for v in values
|
||||
]
|
||||
|
||||
|
||||
class Curve(Base, speckle_type=GEOMETRY + "Curve"):
|
||||
class Curve(
|
||||
Base,
|
||||
speckle_type=GEOMETRY + "Curve",
|
||||
chunkable={"points": 20000, "weights": 20000, "knots": 20000},
|
||||
):
|
||||
degree: int = None
|
||||
periodic: bool = None
|
||||
rational: bool = None
|
||||
@@ -148,10 +153,6 @@ class Curve(Base, speckle_type=GEOMETRY + "Curve"):
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(points=20000, weights=20000, knots=20000)
|
||||
|
||||
def as_points(self) -> List[Point]:
|
||||
"""Converts the `value` attribute to a list of Points"""
|
||||
if not self.points:
|
||||
@@ -161,11 +162,13 @@ class Curve(Base, speckle_type=GEOMETRY + "Curve"):
|
||||
raise ValueError("Points array malformed: length%3 != 0.")
|
||||
|
||||
values = iter(self.points)
|
||||
return [Point(v, next(values), next(values), units=self.units) for v in values]
|
||||
return [
|
||||
Point(x=v, y=next(values), z=next(values), units=self.units) for v in values
|
||||
]
|
||||
|
||||
|
||||
class Polycurve(Base, speckle_type=GEOMETRY + "Polycurve"):
|
||||
segments: List[Base] = []
|
||||
segments: List[Base] = None
|
||||
domain: Interval = None
|
||||
closed: bool = None
|
||||
bbox: Box = None
|
||||
@@ -187,7 +190,16 @@ class Extrusion(Base, speckle_type=GEOMETRY + "Extrusion"):
|
||||
bbox: Box = None
|
||||
|
||||
|
||||
class Mesh(Base, speckle_type=GEOMETRY + "Mesh"):
|
||||
class Mesh(
|
||||
Base,
|
||||
speckle_type=GEOMETRY + "Mesh",
|
||||
chunkable={
|
||||
"vertices": 2000,
|
||||
"faces": 2000,
|
||||
"colors": 2000,
|
||||
"textureCoordinates": 2000,
|
||||
},
|
||||
):
|
||||
vertices: List[float] = None
|
||||
faces: List[int] = None
|
||||
colors: List[int] = None
|
||||
@@ -196,12 +208,6 @@ class Mesh(Base, speckle_type=GEOMETRY + "Mesh"):
|
||||
area: float = None
|
||||
volume: float = None
|
||||
|
||||
def __init__(self, **data) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(
|
||||
vertices=2000, faces=2000, colors=2000, textureCoordinates=2000
|
||||
)
|
||||
|
||||
|
||||
class Surface(Base, speckle_type=GEOMETRY + "Surface"):
|
||||
degreeU: int = None
|
||||
@@ -231,7 +237,8 @@ class BrepFace(Base, speckle_type=GEOMETRY + "BrepFace"):
|
||||
|
||||
@property
|
||||
def _loops(self):
|
||||
return [self._Brep.Loops[index] for index in self.LoopIndices]
|
||||
if self.LoopIndices:
|
||||
return [self._Brep.Loops[i] for i in self.LoopIndices]
|
||||
|
||||
|
||||
class BrepEdge(Base, speckle_type=GEOMETRY + "BrepEdge"):
|
||||
@@ -253,7 +260,8 @@ class BrepEdge(Base, speckle_type=GEOMETRY + "BrepEdge"):
|
||||
|
||||
@property
|
||||
def _trims(self):
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
if self.TrimIndices:
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
@property
|
||||
def _curve(self):
|
||||
@@ -272,7 +280,8 @@ class BrepLoop(Base, speckle_type=GEOMETRY + "BrepLoop"):
|
||||
|
||||
@property
|
||||
def _trims(self):
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
if self.TrimIndices:
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
|
||||
class BrepTrim(Base, speckle_type=GEOMETRY + "BrepTrim"):
|
||||
@@ -305,41 +314,41 @@ class BrepTrim(Base, speckle_type=GEOMETRY + "BrepTrim"):
|
||||
return self._Brep.Curve2D[self.CurveIndex]
|
||||
|
||||
|
||||
class Brep(Base, speckle_type=GEOMETRY + "Brep"):
|
||||
class Brep(
|
||||
Base,
|
||||
speckle_type=GEOMETRY + "Brep",
|
||||
chunkable={
|
||||
"Surfaces": 200,
|
||||
"Curve3D": 200,
|
||||
"Curve2D": 200,
|
||||
"Vertices": 5000,
|
||||
"Edges": 5000,
|
||||
"Loops": 5000,
|
||||
"Trims": 5000,
|
||||
"Faces": 5000,
|
||||
},
|
||||
detachable={"displayValue"},
|
||||
):
|
||||
provenance: str = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
volume: float = None
|
||||
displayValue: Mesh = None
|
||||
Surfaces: List[Surface] = []
|
||||
Curve3D: List[Base] = []
|
||||
Curve2D: List[Base] = []
|
||||
Vertices: List[Point] = []
|
||||
Edges: List[BrepEdge] = []
|
||||
Loops: List[BrepLoop] = []
|
||||
Trims: List[BrepTrim] = []
|
||||
Faces: List[BrepFace] = []
|
||||
Surfaces: List[Surface] = None
|
||||
Curve3D: List[Base] = None
|
||||
Curve2D: List[Base] = None
|
||||
Vertices: List[Point] = None
|
||||
Edges: List[BrepEdge] = None
|
||||
Loops: List[BrepLoop] = None
|
||||
Trims: List[BrepTrim] = None
|
||||
Faces: List[BrepFace] = None
|
||||
IsClosed: bool = None
|
||||
Orientation: int = 0
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_detachable_attrs({"displayValue"})
|
||||
self.add_chunkable_attrs(
|
||||
Surfaces=200,
|
||||
Curve3D=200,
|
||||
Curve2D=200,
|
||||
Vertices=5000,
|
||||
Edges=5000,
|
||||
Loops=5000,
|
||||
Trims=5000,
|
||||
Faces=5000,
|
||||
)
|
||||
Orientation: int = None
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if not value:
|
||||
return
|
||||
if name in ["Edges", "Loops", "Trims", "Faces"]:
|
||||
if name in {"Edges", "Loops", "Trims", "Faces"}:
|
||||
for val in value:
|
||||
val._Brep = self
|
||||
super().__setattr__(name, value)
|
||||
|
||||
@@ -6,6 +6,7 @@ UNITS_STRINGS = {
|
||||
"mm": ["mm", "mil", "millimeters", "millimetres"],
|
||||
"cm": ["cm", "centimetre", "centimeter", "centimetres", "centimeters"],
|
||||
"m": ["m", "meter", "meters", "metre", "metres"],
|
||||
"km": ["km", "kilometer", "kilometre", "kilometers", "kilometres"],
|
||||
"in": ["in", "inch", "inches"],
|
||||
"ft": ["ft", "foot", "feet"],
|
||||
"yd": ["yd", "yard", "yards"],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import json
|
||||
import ujson
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
@@ -14,7 +14,7 @@ PRIMITIVES = (int, float, str, bool)
|
||||
|
||||
|
||||
def hash_obj(obj: Any) -> str:
|
||||
return hashlib.sha256(json.dumps(obj).encode()).hexdigest()[:32]
|
||||
return hashlib.sha256(ujson.dumps(obj).encode()).hexdigest()[:32]
|
||||
|
||||
|
||||
class BaseObjectSerializer:
|
||||
@@ -35,7 +35,7 @@ class BaseObjectSerializer:
|
||||
self.__reset_writer()
|
||||
self.detach_lineage = [True]
|
||||
hash, obj = self.traverse_base(base)
|
||||
return hash, json.dumps(obj)
|
||||
return hash, ujson.dumps(obj)
|
||||
|
||||
def traverse_base(self, base: Base) -> Tuple[str, Dict]:
|
||||
"""Decomposes the given base object and builds a serializable dictionary
|
||||
@@ -70,7 +70,9 @@ class BaseObjectSerializer:
|
||||
|
||||
# only bother with chunking and detaching if there is a write transport
|
||||
if self.write_transports:
|
||||
dynamic_chunk_match = re.match(r"^@\((\d*)\)", prop)
|
||||
dynamic_chunk_match = prop.startswith("@") and re.match(
|
||||
r"^@\((\d*)\)", prop
|
||||
)
|
||||
if dynamic_chunk_match:
|
||||
chunk_size = dynamic_chunk_match.groups()[0]
|
||||
base._chunkable[prop] = (
|
||||
@@ -140,7 +142,7 @@ class BaseObjectSerializer:
|
||||
# write detached or root objects to transports
|
||||
if detached and self.write_transports:
|
||||
for t in self.write_transports:
|
||||
t.save_object(id=hash, serialized_object=json.dumps(object_builder))
|
||||
t.save_object(id=hash, serialized_object=ujson.dumps(object_builder))
|
||||
|
||||
del self.lineage[-1]
|
||||
|
||||
@@ -236,7 +238,7 @@ class BaseObjectSerializer:
|
||||
"""
|
||||
if not obj_string:
|
||||
return None
|
||||
obj = json.loads(obj_string)
|
||||
obj = ujson.loads(obj_string)
|
||||
return self.recompose_base(obj=obj)
|
||||
|
||||
def recompose_base(self, obj: dict) -> Base:
|
||||
@@ -252,7 +254,7 @@ class BaseObjectSerializer:
|
||||
if not obj:
|
||||
return
|
||||
if isinstance(obj, str):
|
||||
obj = json.loads(obj)
|
||||
obj = ujson.loads(obj)
|
||||
|
||||
if "speckle_type" in obj and obj["speckle_type"] == "reference":
|
||||
obj = self.get_child(obj=obj)
|
||||
@@ -266,7 +268,7 @@ class BaseObjectSerializer:
|
||||
object_type = Base.get_registered_type(speckle_type)
|
||||
|
||||
# initialise the base object using `speckle_type` fall back to base if needed
|
||||
base = object_type() if object_type else Base(speckle_type=speckle_type)
|
||||
base = object_type() if object_type else Base.of_type(speckle_type=speckle_type)
|
||||
# get total children count
|
||||
if "__closure" in obj:
|
||||
if not self.read_transport:
|
||||
@@ -290,7 +292,7 @@ class BaseObjectSerializer:
|
||||
raise SpeckleException(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
|
||||
)
|
||||
ref_obj = json.loads(ref_obj_str)
|
||||
ref_obj = ujson.loads(ref_obj_str)
|
||||
base.__setattr__(prop, self.recompose_base(obj=ref_obj))
|
||||
|
||||
# 3. handle all other cases (base objects, lists, and dicts)
|
||||
@@ -324,7 +326,7 @@ class BaseObjectSerializer:
|
||||
# handle chunked lists
|
||||
data = []
|
||||
for o in obj_list:
|
||||
data.extend(o["data"])
|
||||
data.extend(o.data)
|
||||
return data
|
||||
return obj_list
|
||||
|
||||
@@ -348,4 +350,4 @@ class BaseObjectSerializer:
|
||||
raise SpeckleException(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
|
||||
)
|
||||
return json.loads(ref_obj_str)
|
||||
return ujson.loads(ref_obj_str)
|
||||
|
||||
@@ -22,11 +22,15 @@ class ServerTransport(AbstractTransport):
|
||||
def __init__(self, client: SpeckleClient, stream_id: str, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
# TODO: replace client with account or some other auth avenue
|
||||
if not client.me:
|
||||
raise SpeckleException("The provided SpeckleClient was not authenticated.")
|
||||
self.url = client.url
|
||||
self.stream_id = stream_id
|
||||
|
||||
token = client.me["token"]
|
||||
self._batch_sender = BatchSender(self.url, self.stream_id, token, max_batch_size_mb=1)
|
||||
self._batch_sender = BatchSender(
|
||||
self.url, self.stream_id, token, max_batch_size_mb=1
|
||||
)
|
||||
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update(
|
||||
@@ -73,19 +77,25 @@ class ServerTransport(AbstractTransport):
|
||||
r.encoding = "utf-8"
|
||||
|
||||
if r.status_code != 200:
|
||||
raise SpeckleException(f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})")
|
||||
raise SpeckleException(
|
||||
f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})"
|
||||
)
|
||||
root_obj_serialized = r.text
|
||||
root_obj = json.loads(root_obj_serialized)
|
||||
closures = root_obj.get('__closure', {})
|
||||
closures = root_obj.get("__closure", {})
|
||||
|
||||
# Check which children are not already in the target transport
|
||||
children_ids = list(closures.keys())
|
||||
children_found_map = target_transport.has_objects(children_ids)
|
||||
new_children_ids = [id for id in children_found_map if not children_found_map[id]]
|
||||
new_children_ids = [
|
||||
id for id in children_found_map if not children_found_map[id]
|
||||
]
|
||||
|
||||
# Get the new children
|
||||
endpoint = f"{self.url}/api/getobjects/{self.stream_id}"
|
||||
r = self.session.post(endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True)
|
||||
r = self.session.post(
|
||||
endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True
|
||||
)
|
||||
if r.encoding is None:
|
||||
r.encoding = "utf-8"
|
||||
lines = r.iter_lines(decode_unicode=True)
|
||||
|
||||
@@ -6,7 +6,6 @@ import sqlite3
|
||||
from typing import Any, List, Dict
|
||||
from appdirs import user_data_dir
|
||||
from contextlib import closing
|
||||
from multiprocessing import Process, Queue
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
@@ -19,7 +18,6 @@ class SQLiteTransport(AbstractTransport):
|
||||
_scheduler = sched.scheduler(time.time, time.sleep)
|
||||
_polling_interval = 0.5 # seconds
|
||||
__connection: sqlite3.Connection = None
|
||||
__queue: Queue = Queue()
|
||||
app_name: str = ""
|
||||
scope: str = ""
|
||||
saved_obj_count: int = 0
|
||||
@@ -36,12 +34,18 @@ class SQLiteTransport(AbstractTransport):
|
||||
self.scope = scope or "Objects"
|
||||
self._base_path = base_path or self.__get_base_path()
|
||||
|
||||
os.makedirs(self._base_path, exist_ok=True)
|
||||
try:
|
||||
os.makedirs(self._base_path, exist_ok=True)
|
||||
|
||||
self._root_path = os.path.join(
|
||||
os.path.join(self._base_path, f"{self.scope}.db")
|
||||
)
|
||||
self.__initialise()
|
||||
self._root_path = os.path.join(
|
||||
os.path.join(self._base_path, f"{self.scope}.db")
|
||||
)
|
||||
self.__initialise()
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
f"SQLiteTransport could not initialise {self.scope}.db at {self._base_path}. Either provide a different `base_path` or use an alternative transport.",
|
||||
ex,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"SQLiteTransport(app: '{self.app_name}', scope: '{self.scope}')"
|
||||
@@ -63,26 +67,26 @@ class SQLiteTransport(AbstractTransport):
|
||||
if os_name.startswith("Mac"):
|
||||
system = "darwin"
|
||||
|
||||
if system == "darwin":
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, self.app_name)
|
||||
else:
|
||||
if system != "darwin":
|
||||
return user_data_dir(appname=self.app_name, appauthor=False, roaming=True)
|
||||
|
||||
def __consume_queue(self):
|
||||
if self._is_writing or self.__queue.empty():
|
||||
return
|
||||
print("CONSUME QUEUE")
|
||||
self._is_writing = True
|
||||
while not self.__queue.empty():
|
||||
data = self.__queue.get()
|
||||
self.save_object(data[0], data[1])
|
||||
self._is_writing = False
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, self.app_name)
|
||||
|
||||
self._scheduler.enter(
|
||||
delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
)
|
||||
self._scheduler.run(blocking=True)
|
||||
# def __consume_queue(self):
|
||||
# if self._is_writing or self.__queue.empty():
|
||||
# return
|
||||
# print("CONSUME QUEUE")
|
||||
# self._is_writing = True
|
||||
# while not self.__queue.empty():
|
||||
# data = self.__queue.get()
|
||||
# self.save_object(data[0], data[1])
|
||||
# self._is_writing = False
|
||||
|
||||
# self._scheduler.enter(
|
||||
# delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
# )
|
||||
# self._scheduler.run(blocking=True)
|
||||
|
||||
# def save_object(self, id: str, serialized_object: str) -> None:
|
||||
# """Adds an object to the queue and schedules it to be saved.
|
||||
@@ -102,15 +106,14 @@ class SQLiteTransport(AbstractTransport):
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
) -> None:
|
||||
"""Adds an object from the given transport to the queue and schedules it to be saved.
|
||||
"""Adds an object from the given transport to the the local db
|
||||
|
||||
Arguments:
|
||||
id {str} -- the object id
|
||||
source_transport {AbstractTransport) -- the transport through which the object can be found
|
||||
"""
|
||||
serialized_object = source_transport.get_object(id)
|
||||
self.__queue.put((id, serialized_object))
|
||||
raise NotImplementedError
|
||||
self.save_object(id, serialized_object)
|
||||
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
"""Directly saves an object into the database.
|
||||
|
||||
+2
-1
@@ -90,7 +90,7 @@ def mesh():
|
||||
[1, 2, 3],
|
||||
Base(name="detached within a list"),
|
||||
]
|
||||
mesh.origin = Point(value=[4, 2, 0])
|
||||
mesh.origin = Point(x=4, y=2)
|
||||
return mesh
|
||||
|
||||
|
||||
@@ -102,4 +102,5 @@ def base():
|
||||
base.vertices = [random.uniform(0, 10) for _ in range(1, 120)]
|
||||
base.test_bases = [Base(name=i) for i in range(1, 22)]
|
||||
base["@detach"] = Base(name="detached base")
|
||||
base["@revit_thing"] = Base.of_type("SpecialRevitFamily", name="secret tho")
|
||||
return base
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import pytest
|
||||
from typing import Dict, List
|
||||
from specklepy.objects import Base
|
||||
from specklepy.api import operations
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -72,3 +74,39 @@ def test_speckle_type_cannot_be_set(base: Base) -> None:
|
||||
assert base.speckle_type == "Base"
|
||||
base.speckle_type = "unset"
|
||||
assert base.speckle_type == "Base"
|
||||
|
||||
|
||||
def test_base_of_custom_speckle_type() -> None:
|
||||
b1 = Base.of_type("BirdHouse", name="Tweety's Crib")
|
||||
assert b1.speckle_type == "BirdHouse"
|
||||
assert b1.name == "Tweety's Crib"
|
||||
|
||||
|
||||
class FrozenYoghurt(Base):
|
||||
"""Testing type checking"""
|
||||
|
||||
servings: int
|
||||
flavours: List[str] = None # list item types won't be checked
|
||||
customer: str
|
||||
add_ons: Dict[str, float] # dict item types won't be checked
|
||||
price: float = 0.0
|
||||
|
||||
|
||||
def test_type_checking() -> None:
|
||||
order = FrozenYoghurt()
|
||||
|
||||
order.servings = 2
|
||||
order.price = "7" # will get converted
|
||||
order.customer = "izzy"
|
||||
|
||||
with pytest.raises(SpeckleException):
|
||||
order.flavours = "not a list"
|
||||
with pytest.raises(SpeckleException):
|
||||
order.servings = "five"
|
||||
with pytest.raises(SpeckleException):
|
||||
order.add_ons = ["sprinkles"]
|
||||
|
||||
order.add_ons = {"sprinkles": 0.2, "chocolate": 1.0}
|
||||
order.flavours = ["strawberry", "lychee", "peach", "pineapple"]
|
||||
|
||||
assert order.price == 7.0
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.objects import Base
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.api.models import Stream
|
||||
@@ -19,12 +20,13 @@ class TestObject:
|
||||
return stream
|
||||
|
||||
def test_object_create(self, client, stream, base):
|
||||
transport = MemoryTransport()
|
||||
transport = SQLiteTransport()
|
||||
s = BaseObjectSerializer(write_transports=[transport], read_transport=transport)
|
||||
_, base_dict = s.traverse_base(base)
|
||||
obj_id = client.object.create(stream_id=stream.id, objects=[base_dict])[0]
|
||||
|
||||
assert isinstance(obj_id, str)
|
||||
assert base_dict["@detach"]["speckle_type"] == "reference"
|
||||
assert obj_id == base.get_id(True)
|
||||
|
||||
def test_object_get(self, client, stream, base):
|
||||
@@ -35,4 +37,4 @@ class TestObject:
|
||||
assert isinstance(fetched_base, Base)
|
||||
assert fetched_base.name == base.name
|
||||
assert isinstance(fetched_base.vertices, list)
|
||||
assert fetched_base["@detach"]["speckle_type"] == "reference"
|
||||
# assert fetched_base["@detach"]["speckle_type"] == "reference"
|
||||
|
||||
@@ -19,6 +19,7 @@ class TestSerialization:
|
||||
assert base.get_id() == deserialized.get_id()
|
||||
assert base.units == "mm"
|
||||
assert isinstance(base.test_bases[0], Base)
|
||||
assert base["@revit_thing"].speckle_type == "SpecialRevitFamily"
|
||||
assert base["@detach"].name == deserialized["@detach"].name
|
||||
|
||||
def test_detaching(self, mesh):
|
||||
@@ -60,7 +61,7 @@ class TestSerialization:
|
||||
assert isinstance(received, FakeMesh)
|
||||
assert received.vertices == mesh.vertices
|
||||
assert isinstance(received.origin, Point)
|
||||
assert received.origin.value == mesh.origin.value
|
||||
assert received.origin.x == mesh.origin.x
|
||||
# not comparing hashes as order is not guaranteed back from server
|
||||
|
||||
mesh.id = hash # populate with decomposed id for use in proceeding tests
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
from specklepy.api.credentials import StreamWrapper
|
||||
|
||||
|
||||
class TestWrapper:
|
||||
def test_parse_stream(self):
|
||||
wrap = StreamWrapper("https://testing.speckle.dev/streams/a75ab4f10f")
|
||||
assert wrap.type == "stream"
|
||||
|
||||
def test_parse_branch(self):
|
||||
wacky_wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/branches/%F0%9F%8D%95%E2%AC%85%F0%9F%8C%9F%20you%20wat%3F"
|
||||
)
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/branches/next%20level"
|
||||
)
|
||||
assert wacky_wrap.type == "branch"
|
||||
assert wacky_wrap.branch_name == "🍕⬅🌟 you wat?"
|
||||
assert wrap.type == "branch"
|
||||
|
||||
def test_parse_commit(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/4c3ce1459c/commits/8b9b831792"
|
||||
)
|
||||
assert wrap.type == "commit"
|
||||
|
||||
def test_parse_object(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/a75ab4f10f/objects/5530363e6d51c904903dafc3ea1d2ec6"
|
||||
)
|
||||
assert wrap.type == "object"
|
||||
|
||||
def test_parse_globals_as_branch(self):
|
||||
wrap = StreamWrapper("https://testing.speckle.dev/streams/0c6ad366c4/globals/")
|
||||
assert wrap.type == "branch"
|
||||
|
||||
def test_parse_globals_as_commit(self):
|
||||
wrap = StreamWrapper(
|
||||
"https://testing.speckle.dev/streams/0c6ad366c4/globals/abd3787893"
|
||||
)
|
||||
assert wrap.type == "commit"
|
||||
Reference in New Issue
Block a user