Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd09e97a53 | |||
| 459bd0901f | |||
| ae7c4bc14d | |||
| 41f1823aae | |||
| 625bd5cd84 | |||
| 8812985c67 | |||
| c838835a65 | |||
| 361ba6bfcd | |||
| 8078a4b596 | |||
| 08b106464f | |||
| 0cfe5db674 | |||
| d9dbca2c68 | |||
| ab5b55871b | |||
| 2f87956154 | |||
| 6fc4ab1539 | |||
| 7f432e768d | |||
| d0eb364b3e | |||
| 936b2d8b5a | |||
| 5a9aec80bd | |||
| 6616526279 | |||
| 6e00de58b9 | |||
| 0ec404bbec | |||
| 506aaf68ca | |||
| 9fa6b661eb | |||
| d1adccba00 | |||
| 9e30250446 | |||
| 3e7d657e2e | |||
| 7484d8441b |
@@ -1,11 +1,11 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.191.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
|
||||
ARG VARIANT="3.9"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
|
||||
ARG VARIANT="3.10"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
|
||||
|
||||
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
|
||||
ARG NODE_VERSION="none"
|
||||
ARG NODE_VERSION="16"
|
||||
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
||||
|
||||
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
"python.languageServer": "Pylance",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.pylintArgs": [
|
||||
"--max-line-length=120"
|
||||
],
|
||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||
|
||||
@@ -6,16 +6,12 @@ services:
|
||||
POSTGRES_DB: speckle2_test
|
||||
POSTGRES_PASSWORD: speckle
|
||||
POSTGRES_USER: speckle
|
||||
# ports:
|
||||
# - "5432:5432"
|
||||
network_mode: host
|
||||
redis:
|
||||
image: cimg/redis:6.2
|
||||
# ports:
|
||||
# - "6379:6379"
|
||||
network_mode: host
|
||||
speckle-server:
|
||||
image: speckle/speckle-server
|
||||
image: speckle/speckle-server:latest
|
||||
command: ["bash", "-c", "/wait && node bin/www"]
|
||||
environment:
|
||||
POSTGRES_URL: "localhost"
|
||||
@@ -28,8 +24,6 @@ services:
|
||||
CANONICAL_URL: "http://localhost:3000"
|
||||
WAIT_HOSTS: localhost:5432, localhost:6379
|
||||
DISABLE_FILE_UPLOADS: "true"
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
network_mode: host
|
||||
|
||||
specklepy:
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: New issue
|
||||
about: Create a report to help us improve
|
||||
title:
|
||||
labels:
|
||||
assignees:
|
||||
---
|
||||
|
||||
If it's your first time here - or you forgot about them - make sure you read the [contribution guidelines](CONTRIBUTING.md), and then feel free to delete this line!
|
||||
|
||||
### Expected vs. Actual Behavior
|
||||
|
||||
Describe the problem here.
|
||||
|
||||
### Reproduction Steps & System Config (win, osx, web, etc.)
|
||||
|
||||
Let us know how we can reproduce this, and attach relevant files (if any).
|
||||
|
||||
### Proposed Solution (if any)
|
||||
|
||||
Let us know what how you would solve this.
|
||||
|
||||
#### Optional: Affected Projects
|
||||
|
||||
Does this issue propagate to other dependencies or dependents? If so, list them here!
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Help improve Speckle!
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!---
|
||||
|
||||
Provide a short summary in the Title above. Examples of good Issue titles:
|
||||
|
||||
* "Bug: Error from server when reticulating splines"
|
||||
* "Bug: Revit crashes when installing connector"
|
||||
|
||||
-->
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<!---
|
||||
|
||||
Please answer the following questions before submitting an issue.
|
||||
|
||||
-->
|
||||
|
||||
- [ ] I read the [contribution guidelines](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I checked the [documentation](https://speckle.guide/) and found no answer.
|
||||
- [ ] I checked [existing issues](../issues?q=is%3Aissue) and found no similar issue. <!-- If you do find an existing issue, please show your support by liking it :+1: instead of creating a new issue -->
|
||||
- [ ] I checked the [community forum](https://speckle.community/) for related discussions and found no answer.
|
||||
- [ ] I'm reporting the issue to the correct repository (see also [speckle-server](https://github.com/specklesystems/speckle-server), [speckle-sharp](https://github.com/specklesystems/speckle-sharp), [specklepy](https://github.com/specklesystems/specklepy), [speckle-docs](https://github.com/specklesystems/speckle-docs), and [others](https://github.com/orgs/specklesystems/repositories))
|
||||
|
||||
## What package are you referring to?
|
||||
|
||||
<!---
|
||||
Is it related to the server (backend) only, or does this bug relate to the frontend, viewer, objectloader or any other package?
|
||||
-->
|
||||
|
||||
## Describe the bug
|
||||
|
||||
<!---
|
||||
A clear and concise description of what the bug is.
|
||||
-->
|
||||
|
||||
## To Reproduce
|
||||
|
||||
<!---
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
## Expected behavior
|
||||
|
||||
<!---
|
||||
A clear and concise description of what you expected to happen.
|
||||
-->
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!---
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
-->
|
||||
|
||||
## System Info
|
||||
|
||||
If applicable, please fill in the below details - they help a lot!
|
||||
|
||||
### Desktop (please complete the following information):
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
### Smartphone (please complete the following information):
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
## Failure Logs
|
||||
|
||||
<!---
|
||||
Please include any relevant log snippets or files here, or upload as a file.
|
||||
|
||||
If including inline, please use markdown code block syntax. https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks
|
||||
For example:
|
||||
|
||||
```
|
||||
your log output here
|
||||
```
|
||||
-->
|
||||
|
||||
## Additional context
|
||||
|
||||
<!---
|
||||
Add any other context about the problem here.
|
||||
-->
|
||||
|
||||
## Proposed Solution (if any)
|
||||
|
||||
<!---
|
||||
Let us know what how you would solve this.
|
||||
-->
|
||||
|
||||
#### Optional: Affected Projects
|
||||
|
||||
<!---
|
||||
Does this issue propagate to other dependencies or dependents? If so, list them here with links!
|
||||
-->
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for Speckle!
|
||||
title: ''
|
||||
labels: enhancement, question
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!---
|
||||
|
||||
Provide a short summary in the Title above. Examples of good Issue titles:
|
||||
|
||||
* "Enhancement: Connector for Minecraft"
|
||||
* "Enhancement: Web viewer should support tesseracts"
|
||||
|
||||
-->
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<!---
|
||||
|
||||
Please answer the following questions before submitting an issue.
|
||||
|
||||
-->
|
||||
|
||||
- [ ] I read the [contribution guidelines](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I checked the [documentation](https://speckle.guide/) and found no answer.
|
||||
- [ ] I checked [existing issues](../issues?q=is%3Aissue) and found no similar issue. <!-- If you do find an existing issue, please show your support by liking it :+1: instead of creating a new issue -->
|
||||
- [ ] I checked the [community forum](https://speckle.community/) for related discussions and found no answer.
|
||||
- [ ] I'm requesting the feature to the correct repository (see also [speckle-server](https://github.com/specklesystems/speckle-server), [speckle-sharp](https://github.com/specklesystems/speckle-sharp), [specklepy](https://github.com/specklesystems/specklepy), [speckle-docs](https://github.com/specklesystems/speckle-docs), and [others](https://github.com/orgs/specklesystems/repositories))
|
||||
|
||||
## What package are you referring to?
|
||||
|
||||
<!---
|
||||
Is it related to the server (backend) only, or does this feature request relate to the frontend, viewer, objectloader or any other package?
|
||||
-->
|
||||
|
||||
## Is your feature request related to a problem? Please describe.
|
||||
|
||||
<!---
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
-->
|
||||
|
||||
## Describe the solution you'd like
|
||||
|
||||
<!---
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
|
||||
## Describe alternatives you've considered
|
||||
|
||||
<!---
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
-->
|
||||
|
||||
## Additional context
|
||||
|
||||
<!---
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
Have you seen this feature implemented in any other software? Can you provide screenshots or links to video or documentation?
|
||||
What works well about these existing features in other software? What doesn't work well?
|
||||
-->
|
||||
|
||||
## Related issues or community discussions
|
||||
|
||||
<!---
|
||||
Is this feature request related to (but sufficiently distinct from) any existing issues?
|
||||
Does this feature request require other features to be available beforehand?
|
||||
Has this feature been discussed in the community forum, please link here? https://speckle.community/
|
||||
-->
|
||||
@@ -1,25 +0,0 @@
|
||||
Description of PR...
|
||||
|
||||
## Changes
|
||||
|
||||
- Item 1
|
||||
- Item 2
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Unit tests
|
||||
- [ ] Documentation
|
||||
|
||||
## References
|
||||
|
||||
(optional)
|
||||
|
||||
Include **important** links regarding the implementation of this PR.
|
||||
This usually includes and RFC or an aggregation of issues and/or individual conversations
|
||||
that helped put this solution together. This helps ensure there is a good aggregation
|
||||
of resources regarding the implementation.
|
||||
|
||||
```text
|
||||
Fixes #85, Fixes #22, Fixes username/repo#123
|
||||
Connects #123
|
||||
```
|
||||
@@ -0,0 +1,102 @@
|
||||
<!---
|
||||
|
||||
Provide a short summary in the Title above. Examples of good PR titles:
|
||||
|
||||
* "Feature: adds metrics to component"
|
||||
|
||||
* "Fix: resolves duplication in comment thread"
|
||||
|
||||
* "Update: apollo v2.34.0"
|
||||
|
||||
-->
|
||||
|
||||
## Description & motivation
|
||||
|
||||
<!---
|
||||
|
||||
Describe your changes, and why you're making them. What benefit will this have to others?
|
||||
|
||||
Is this linked to an open Github issue, a thread in Speckle community,
|
||||
or another pull request? Link it here.
|
||||
|
||||
If it is related to a Github issue, and resolves it, please link to the issue number, e.g.:
|
||||
Fixes #85, Fixes #22, Fixes username/repo#123
|
||||
Connects #123
|
||||
|
||||
-->
|
||||
|
||||
## Changes:
|
||||
|
||||
<!---
|
||||
|
||||
- Item 1
|
||||
- Item 2
|
||||
|
||||
-->
|
||||
|
||||
## To-do before merge:
|
||||
|
||||
<!---
|
||||
|
||||
(Optional -- remove this section if not needed)
|
||||
|
||||
Include any notes about things that need to happen before this PR is merged, e.g.:
|
||||
|
||||
- [ ] Change the base branch
|
||||
|
||||
- [ ] Ensure PR #56 is merged
|
||||
|
||||
-->
|
||||
|
||||
## Screenshots:
|
||||
|
||||
<!---
|
||||
|
||||
Include a screenshot the before and after. This can be a screenshot of a plugin, web frontend, or output in a terminal.
|
||||
|
||||
-->
|
||||
|
||||
## Validation of changes:
|
||||
|
||||
<!---
|
||||
|
||||
Describe what tests have been added or amended, and why these demonstrate it works and will prevent this feature being accidentally broken by future changes.
|
||||
|
||||
-->
|
||||
|
||||
## Checklist:
|
||||
|
||||
<!---
|
||||
|
||||
This checklist is mostly useful as a reminder of small things that can easily be
|
||||
|
||||
forgotten – it is meant as a helpful tool rather than hoops to jump through.
|
||||
|
||||
Put an `x` between the square brackets, e.g. [x], for all the items that apply,
|
||||
|
||||
make notes next to any that haven't been addressed, and remove any items that are not relevant to this PR.
|
||||
|
||||
-->
|
||||
|
||||
- [ ] My pull request follows the guidelines in the [Contributing guide](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md)?
|
||||
- [ ] My pull request does not duplicate any other open [Pull Requests](../../pulls) for the same update/change?
|
||||
- [ ] My commits are related to the pull request and do not amend unrelated code or documentation.
|
||||
- [ ] My code follows a similar style to existing code.
|
||||
- [ ] I have added appropriate tests.
|
||||
- [ ] I have updated or added relevant documentation.
|
||||
|
||||
## References
|
||||
|
||||
<!---
|
||||
|
||||
(Optional -- remove this section if not needed )
|
||||
|
||||
Include **important** links regarding the implementation of this PR.
|
||||
|
||||
This usually includes a RFC or an aggregation of issues and/or individual conversations
|
||||
|
||||
that helped put this solution together. This helps ensure we retain and share knowledge
|
||||
|
||||
regarding the implementation, and may help others understand motivation and design decisions etc..
|
||||
|
||||
-->
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
|
||||
echo "$PROJECT_ID"
|
||||
echo "$STATUS_FIELD_ID"
|
||||
|
||||
|
||||
echo 'DONE_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .settings | fromjson | .options[] | select(.name== "Done") | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo "$DONE_ID"
|
||||
|
||||
@@ -52,9 +52,9 @@ jobs:
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Update Status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
@@ -75,4 +75,3 @@ jobs:
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f status=$STATUS_FIELD_ID -f id=$ITEM_ID -f value=${{ env.DONE_ID }}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Add Issue to project
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
@@ -46,5 +46,5 @@ jobs:
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
|
||||
@@ -74,7 +74,7 @@ It may be helpful to know where the local accounts and object cache dbs are stor
|
||||
|
||||
## Contributing
|
||||
|
||||
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
|
||||
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) and [code of conduct](.github/CODE_OF_CONDUCT.md) for an overview of the practices we try to follow.
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.2.+ | :white_check_mark: |
|
||||
| < 2.2 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Hi! If you've found something off, we'd be more than happy if you would report it via security@speckle.systems. We will work together with you to correctly identify the cause and implement a fix. Thanks for helping make Speckle safer!
|
||||
@@ -0,0 +1,39 @@
|
||||
from typing import List
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.objects import Base
|
||||
from specklepy.api import operations
|
||||
import string
|
||||
import random
|
||||
|
||||
|
||||
class Sub(Base):
|
||||
bar: List[str]
|
||||
|
||||
|
||||
def random_string():
|
||||
letters = string.ascii_lowercase
|
||||
return "".join(random.choice(letters) for _ in range(10))
|
||||
|
||||
|
||||
def create_object(child_count: int) -> Base:
|
||||
foo = Base()
|
||||
for i in range(child_count):
|
||||
stuff = random_string()
|
||||
foo[f"@child_{i}"] = Sub(bar=["asdf", "bar", i, stuff])
|
||||
return foo
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
stream_url = "http://hyperion:3000/streams/2372b54c35"
|
||||
|
||||
child_count = 10
|
||||
foo = create_object(child_count)
|
||||
|
||||
wrapper = StreamWrapper(stream_url)
|
||||
transport = wrapper.get_transport()
|
||||
|
||||
hash = operations.send(base=foo, transports=[transport], use_default_cache=False)
|
||||
|
||||
rec = operations.receive(hash, transport)
|
||||
print(rec)
|
||||
@@ -0,0 +1,35 @@
|
||||
from specklepy.api import operations
|
||||
from specklepy.objects.geometry import Base
|
||||
from specklepy.objects.units import Units
|
||||
|
||||
dct = {
|
||||
"id": "1234abcd",
|
||||
"units": None,
|
||||
"speckle_type": "Base",
|
||||
"applicationId": None,
|
||||
"totalChildrenCount": 0,
|
||||
}
|
||||
base = Base()
|
||||
for prop, value in dct.items():
|
||||
base.__setattr__(prop, value)
|
||||
|
||||
from devtools import debug
|
||||
|
||||
debug(base)
|
||||
debug(base.units)
|
||||
|
||||
base.units = "m"
|
||||
debug(base.units)
|
||||
base.units = None
|
||||
|
||||
debug(base.units)
|
||||
|
||||
foo = operations.serialize(base)
|
||||
|
||||
base.units = 10
|
||||
|
||||
debug(base.units)
|
||||
debug(foo)
|
||||
|
||||
base.units = Units.mm
|
||||
debug(base.units)
|
||||
Generated
+187
-279
@@ -6,6 +6,20 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.11.7"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
lazy-object-proxy = ">=1.4.0"
|
||||
typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
|
||||
typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
|
||||
wrapt = ">=1.11,<2"
|
||||
|
||||
[[package]]
|
||||
name = "asttokens"
|
||||
version = "2.0.5"
|
||||
@@ -42,27 +56,36 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
||||
|
||||
[[package]]
|
||||
name = "backoff"
|
||||
version = "2.1.2"
|
||||
description = "Function decoration for backoff and retry"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "20.8b1"
|
||||
version = "22.8.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = "*"
|
||||
click = ">=7.1.2"
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.6,<1"
|
||||
regex = ">=2020.1.8"
|
||||
toml = ">=0.10.1"
|
||||
typed-ast = ">=1.4.0"
|
||||
typing-extensions = ">=3.7.4"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
||||
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
|
||||
d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
@@ -105,7 +128,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "6.4.1"
|
||||
version = "6.4.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -146,6 +169,17 @@ executing = ">=0.8.0,<1.0.0"
|
||||
[package.extras]
|
||||
pygments = ["Pygments (>=2.2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "dill"
|
||||
version = "0.3.5.1"
|
||||
description = "serialize all of python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
|
||||
|
||||
[package.extras]
|
||||
graph = ["objgraph (>=1.7.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "0.8.3"
|
||||
@@ -156,13 +190,14 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "gql"
|
||||
version = "3.3.0"
|
||||
version = "3.4.0"
|
||||
description = "GraphQL client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
backoff = ">=1.11.1,<3.0"
|
||||
graphql-core = ">=3.2,<3.3"
|
||||
requests = {version = ">=2.26,<3", optional = true, markers = "extra == \"requests\""}
|
||||
requests-toolbelt = {version = ">=0.9.1,<1", optional = true, markers = "extra == \"requests\""}
|
||||
@@ -235,6 +270,22 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"]
|
||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||
plugins = ["setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "lazy-object-proxy"
|
||||
version = "1.7.1"
|
||||
description = "A fast and thorough lazy object proxy."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.0.2"
|
||||
@@ -243,6 +294,25 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "0.971"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=0.4.3"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""}
|
||||
typing-extensions = ">=3.10"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
python2 = ["typed-ast (>=1.4.0,<2)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
@@ -270,6 +340,18 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.5.2"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
|
||||
test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
@@ -308,6 +390,29 @@ typing-extensions = ">=3.7.4.3"
|
||||
dotenv = ["python-dotenv (>=0.10.4)"]
|
||||
email = ["email-validator (>=1.0.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.14.4"
|
||||
description = "python code static checker"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.11.6,<=2.12.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = ">=0.2"
|
||||
isort = ">=4.2.5,<6"
|
||||
mccabe = ">=0.6,<0.8"
|
||||
platformdirs = ">=2.2.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
tomlkit = ">=0.10.1"
|
||||
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
spelling = ["pyenchant (>=3.2,<4.0)"]
|
||||
testutils = ["gitpython (>3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
@@ -367,14 +472,6 @@ python-versions = "*"
|
||||
[package.dependencies]
|
||||
pytest = "*"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2022.6.2"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.28.0"
|
||||
@@ -428,6 +525,14 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.11.1"
|
||||
description = "Style preserving TOML library"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
|
||||
[[package]]
|
||||
name = "typed-ast"
|
||||
version = "1.5.4"
|
||||
@@ -508,14 +613,15 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = ">=3.7.0, <4.0"
|
||||
content-hash = "027e0ce91c7e3dfbee0a5c1eea4204267ba3d5ec7df98df81ae3828691c36d7d"
|
||||
python-versions = ">=3.7.2, <4.0"
|
||||
content-hash = "46dae0dc34c24a27efb649d4837360551f355d0a531f3f998fd53e1980be8c9e"
|
||||
|
||||
[metadata.files]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||
]
|
||||
astroid = []
|
||||
asttokens = [
|
||||
{file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"},
|
||||
{file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"},
|
||||
@@ -528,13 +634,9 @@ attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
|
||||
{file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
|
||||
]
|
||||
backoff = []
|
||||
black = []
|
||||
certifi = []
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
@@ -547,49 +649,7 @@ colorama = [
|
||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"},
|
||||
{file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"},
|
||||
{file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
|
||||
]
|
||||
coverage = []
|
||||
deprecated = [
|
||||
{file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
|
||||
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
|
||||
@@ -598,14 +658,12 @@ devtools = [
|
||||
{file = "devtools-0.8.0-py3-none-any.whl", hash = "sha256:00717ef184223cf36c65bbd17c6eb412f8a7564f47957f9e8b2b7610661b17fb"},
|
||||
{file = "devtools-0.8.0.tar.gz", hash = "sha256:6162a2f61c70242479dff3163e7837e6a9bf32451661af1347bfa3115602af16"},
|
||||
]
|
||||
dill = []
|
||||
executing = [
|
||||
{file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"},
|
||||
{file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"},
|
||||
]
|
||||
gql = [
|
||||
{file = "gql-3.3.0-py2.py3-none-any.whl", hash = "sha256:77fbc0a47c2df4edb8a98afa8c9c23aead5e0b4ab26d424e2dcfeb3fa2a2319d"},
|
||||
{file = "gql-3.3.0.tar.gz", hash = "sha256:efa405aebe0e7b2fe342b43cfc70fd57e2e385fcae04f79c509366ad48a64381"},
|
||||
]
|
||||
gql = []
|
||||
graphql-core = [
|
||||
{file = "graphql-core-3.2.1.tar.gz", hash = "sha256:9d1bf141427b7d54be944587c8349df791ce60ade2e3cccaf9c56368c133c201"},
|
||||
{file = "graphql_core-3.2.1-py3-none-any.whl", hash = "sha256:f83c658e4968998eed1923a2e3e3eddd347e005ac0315fbb7ca4d70ea9156323"},
|
||||
@@ -614,10 +672,7 @@ idna = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.11.4-py3-none-any.whl", hash = "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec"},
|
||||
{file = "importlib_metadata-4.11.4.tar.gz", hash = "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700"},
|
||||
]
|
||||
importlib-metadata = []
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
@@ -626,6 +681,49 @@ isort = [
|
||||
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
|
||||
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
|
||||
]
|
||||
lazy-object-proxy = [
|
||||
{file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"},
|
||||
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
|
||||
{file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
|
||||
]
|
||||
mccabe = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
multidict = [
|
||||
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
|
||||
{file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
|
||||
@@ -687,6 +785,7 @@ multidict = [
|
||||
{file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
|
||||
{file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
|
||||
]
|
||||
mypy = []
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
@@ -699,6 +798,7 @@ pathspec = [
|
||||
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
|
||||
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
|
||||
]
|
||||
platformdirs = []
|
||||
pluggy = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
@@ -707,43 +807,8 @@ py = [
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pydantic = [
|
||||
{file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"},
|
||||
{file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"},
|
||||
{file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"},
|
||||
{file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"},
|
||||
{file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"},
|
||||
{file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"},
|
||||
{file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"},
|
||||
{file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"},
|
||||
]
|
||||
pydantic = []
|
||||
pylint = []
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
@@ -761,86 +826,7 @@ pytest-ordering = [
|
||||
{file = "pytest_ordering-0.6-py2-none-any.whl", hash = "sha256:27fba3fc265f5d0f8597e7557885662c1bdc1969497cd58aff6ed21c3b617de2"},
|
||||
{file = "pytest_ordering-0.6-py3-none-any.whl", hash = "sha256:3f314a178dbeb6777509548727dc69edf22d6d9a2867bf2d310ab85c403380b6"},
|
||||
]
|
||||
regex = [
|
||||
{file = "regex-2022.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:042d122f9fee3ceb6d7e3067d56557df697d1aad4ff5f64ecce4dc13a90a7c01"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffef4b30785dc2d1604dfb7cf9fca5dc27cd86d65f7c2a9ec34d6d3ae4565ec2"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0afa6a601acf3c0dc6de4e8d7d8bbce4e82f8542df746226cd35d4a6c15e9456"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a11cbe8eb5fb332ae474895b5ead99392a4ea568bd2a258ab8df883e9c2bf92"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c1f62ee2ba880e221bc950651a1a4b0176083d70a066c83a50ef0cb9b178e12"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aba3d13c77173e9bfed2c2cea7fc319f11c89a36fcec08755e8fb169cf3b0df"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249437f7f5b233792234aeeecb14b0aab1566280de42dfc97c26e6f718297d68"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:179410c79fa86ef318d58ace233f95b87b05a1db6dc493fa29404a43f4b215e2"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5e201b1232d81ca1a7a22ab2f08e1eccad4e111579fd7f3bbf60b21ef4a16cea"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fdecb225d0f1d50d4b26ac423e0032e76d46a788b83b4e299a520717a47d968c"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:be57f9c7b0b423c66c266a26ad143b2c5514997c05dd32ce7ca95c8b209c2288"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ed657a07d8a47ef447224ea00478f1c7095065dfe70a89e7280e5f50a5725131"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:24908aefed23dd065b4a668c0b4ca04d56b7f09d8c8e89636cf6c24e64e67a1e"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-win32.whl", hash = "sha256:775694cd0bb2c4accf2f1cdd007381b33ec8b59842736fe61bdbad45f2ac7427"},
|
||||
{file = "regex-2022.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:809bbbbbcf8258049b031d80932ba71627d2274029386f0452e9950bcfa2c6e8"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2b5d983eb0adf2049d41f95205bdc3de4e6cc2350e9c80d4409d3a75229de"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4c101746a8dac0401abefa716b357c546e61ea2e3d4a564a9db9eac57ccbce"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:166ae7674d0a0e0f8044e7335ba86d0716c9d49465cff1b153f908e0470b8300"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5eac5d8a8ac9ccf00805d02a968a36f5c967db6c7d2b747ab9ed782b3b3a28b"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f57823f35b18d82b201c1b27ce4e55f88e79e81d9ca07b50ce625d33823e1439"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d42e3b7b23473729adbf76103e7df75f9167a5a80b1257ca30688352b4bb2dc"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2932e728bee0a634fe55ee54d598054a5a9ffe4cd2be21ba2b4b8e5f8064c2c"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:17764683ea01c2b8f103d99ae9de2473a74340df13ce306c49a721f0b1f0eb9e"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:2ac29b834100d2c171085ceba0d4a1e7046c434ddffc1434dbc7f9d59af1e945"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:f43522fb5d676c99282ca4e2d41e8e2388427c0cf703db6b4a66e49b10b699a8"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:9faa01818dad9111dbf2af26c6e3c45140ccbd1192c3a0981f196255bf7ec5e6"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:17443f99b8f255273731f915fdbfea4d78d809bb9c3aaf67b889039825d06515"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-win32.whl", hash = "sha256:4a5449adef907919d4ce7a1eab2e27d0211d1b255bf0b8f5dd330ad8707e0fc3"},
|
||||
{file = "regex-2022.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4d206703a96a39763b5b45cf42645776f5553768ea7f3c2c1a39a4f59cafd4ba"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fcd7c432202bcb8b642c3f43d5bcafc5930d82fe5b2bf2c008162df258445c1d"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:186c5a4a4c40621f64d771038ede20fca6c61a9faa8178f9e305aaa0c2442a97"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:047b2d1323a51190c01b6604f49fe09682a5c85d3c1b2c8b67c1cd68419ce3c4"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30637e7fa4acfed444525b1ab9683f714be617862820578c9fd4e944d4d9ad1f"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adafe6f2c6d86dbf3313866b61180530ca4dcd0c264932dc8fa1ffb10871d58"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67ae3601edf86e15ebe40885e5bfdd6002d34879070be15cf18fc0d80ea24fed"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:48dddddce0ea7e7c3e92c1e0c5a28c13ca4dc9cf7e996c706d00479652bff76c"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:68e5c641645351eb9eb12c465876e76b53717f99e9b92aea7a2dd645a87aa7aa"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8fd5f8ae42f789538bb634bdfd69b9aa357e76fdfd7ad720f32f8994c0d84f1e"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:71988a76fcb68cc091e901fddbcac0f9ad9a475da222c47d3cf8db0876cb5344"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:4b8838f70be3ce9e706df9d72f88a0aa7d4c1fea61488e06fdf292ccb70ad2be"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:663dca677bd3d2e2b5b7d0329e9f24247e6f38f3b740dd9a778a8ef41a76af41"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-win32.whl", hash = "sha256:24963f0b13cc63db336d8da2a533986419890d128c551baacd934c249d51a779"},
|
||||
{file = "regex-2022.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ceff75127f828dfe7ceb17b94113ec2df4df274c4cd5533bb299cb099a18a8ca"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a6f2698cfa8340dfe4c0597782776b393ba2274fe4c079900c7c74f68752705"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8a08ace913c4101f0dc0be605c108a3761842efd5f41a3005565ee5d169fb2b"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26dbe90b724efef7820c3cf4a0e5be7f130149f3d2762782e4e8ac2aea284a0b"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5f759a1726b995dc896e86f17f9c0582b54eb4ead00ed5ef0b5b22260eaf2d0"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1fc26bb3415e7aa7495c000a2c13bf08ce037775db98c1a3fac9ff04478b6930"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52684da32d9003367dc1a1c07e059b9bbaf135ad0764cd47d8ac3dba2df109bc"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c1264eb40a71cf2bff43d6694ab7254438ca19ef330175060262b3c8dd3931a"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bc635ab319c9b515236bdf327530acda99be995f9d3b9f148ab1f60b2431e970"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:27624b490b5d8880f25dac67e1e2ea93dfef5300b98c6755f585799230d6c746"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:555f7596fd1f123f8c3a67974c01d6ef80b9769e04d660d6c1a7cc3e6cff7069"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:933e72fbe1829cbd59da2bc51ccd73d73162f087f88521a87a8ec9cb0cf10fa8"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cff5c87e941292c97d11dc81bd20679f56a2830f0f0e32f75b8ed6e0eb40f704"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c757f3a27b6345de13ef3ca956aa805d7734ce68023e84d0fc74e1f09ce66f7a"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-win32.whl", hash = "sha256:a58d21dd1a2d6b50ed091554ff85e448fce3fe33a4db8b55d0eba2ca957ed626"},
|
||||
{file = "regex-2022.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:495a4165172848503303ed05c9d0409428f789acc27050fe2cf0a4549188a7d5"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ab5cf7d09515548044e69d3a0ec77c63d7b9dfff4afc19653f638b992573126"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1ea28f0ee6cbe4c0367c939b015d915aa9875f6e061ba1cf0796ca9a3010570"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de1ecf26ce85521bf73897828b6d0687cc6cf271fb6ff32ac63d26b21f5e764"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7c7044aabdad2329974be2246babcc21d3ede852b3971a90fd8c2056c20360"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53d69d77e9cfe468b000314dd656be85bb9e96de088a64f75fe128dfe1bf30dd"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c8d61883a38b1289fba9944a19a361875b5c0170b83cdcc95ea180247c1b7d3"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5429202bef174a3760690d912e3a80060b323199a61cef6c6c29b30ce09fd17"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e85b10280cf1e334a7c95629f6cbbfe30b815a4ea5f1e28d31f79eb92c2c3d93"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c400dfed4137f32127ea4063447006d7153c974c680bf0fb1b724cce9f8567fc"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f648037c503985aed39f85088acab6f1eb6a0482d7c6c665a5712c9ad9eaefc"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e7b2ff451f6c305b516281ec45425dd423223c8063218c5310d6f72a0a7a517c"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:be456b4313a86be41706319c397c09d9fdd2e5cdfde208292a277b867e99e3d1"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c3db393b21b53d7e1d3f881b64c29d886cbfdd3df007e31de68b329edbab7d02"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-win32.whl", hash = "sha256:d70596f20a03cb5f935d6e4aad9170a490d88fc4633679bf00c652e9def4619e"},
|
||||
{file = "regex-2022.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:3b9b6289e03dbe6a6096880d8ac166cb23c38b4896ad235edee789d4e8697152"},
|
||||
{file = "regex-2022.6.2.tar.gz", hash = "sha256:f7b43acb2c46fb2cd506965b2d9cf4c5e64c9c612bac26c1187933c7296bf08c"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"},
|
||||
{file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"},
|
||||
]
|
||||
requests = []
|
||||
requests-toolbelt = [
|
||||
{file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"},
|
||||
{file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
|
||||
@@ -853,92 +839,14 @@ toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
||||
]
|
||||
tomli = []
|
||||
tomlkit = []
|
||||
typed-ast = []
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
|
||||
{file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
|
||||
]
|
||||
ujson = [
|
||||
{file = "ujson-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a933b3a238a48162c382e0ac338b97663d044b0485021b6670565a81e7b7ec98"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:612015c6e5a9bf041b89f1eaa8ab8682469b3a745a00c7c95bbbee8080f6b346"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a720b6eff73415249a3dd02e2b1b337de31bb9fa8220bd572dffba23066e538c"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1408ea1704017289c3023928065233b90953aae3e1d7d06d6d6db667e9fe159"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5192505798a5734a85c763eff11e6f6072d3595c337b52f72922b4e22fe66e2e"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bad1471ccfa8d100a0bc513c6db587c38de99384f2aa54eec1016a131d63d3d9"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b926f2f7a266db8f2c46498f0c2c9fcc7e53c8e0fa8bff7f08ad9c044723a2ec"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed9809bc36292e0d3632d50aae497b5827c1a2e07158f7d4d5c53e8e8662bf66"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-win32.whl", hash = "sha256:522b1d60872bb6368c14ac538adb55ca9d6c39a7a962832819ef1aafb3446ff5"},
|
||||
{file = "ujson-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a609bb1cdda9748e6a8363039926dee5ea2bcc073412279615560b967f92a524"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7455fc3d69315149b95fd011c01496a5e9442c9e7c4d202bed87c5c2e449ed05"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:865225a85e4ce48754d0036fdc0eb796b4aaf4f1e928f0efb9b4e1c081647a4c"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d553f31bceda492c2bda37f48873820d28f07608ae14409c5e9d6c3aa6694840"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a014531468b78c031aa04e5ca8b64385a6edb48a2e66ebf11093213c678fc383"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b3e6431812d8008dce7b2546b1276f649f6c9aa44617762ebd3529a25092816c"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:089965f964d17905c48cdca88b982d525165e549b438ac86f194c6a9d852fd69"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ca5eced4ae4ba1e2c9539fca6451694d31e0243de2acfcd6965e2b6e159ba29b"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:a4fe193050b519ace09f7d053def30b99deadf650c18a8a874ea0f6c9a2992bc"},
|
||||
{file = "ujson-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e7961c493a982c03cffc9ce4dc2b23bed1375352296f946cc36ddeb5145fa62c"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:34592a3c9370745b093ebca60aee6d32f8e7abe3d5c12d54c7dba0b2f81cd863"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:510c3705b29bc3753ec9e6073b99000160320c1cf6e035884295401acb474dfa"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:034c07399dff35385ecc53caf9b1f12b3e203834de27b723daeb2cbb3e02ee7f"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a87e1c05f1efc23c67bfa26be79f12c1f59f71a586b396068d5cf7eb78a2635"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:972c1850cc52e57ccdea70e3c069e2da5c6090e3ee18d167dff2618a8d7dd127"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d45e86101a5cddd295d5870b02244fc87ecd9b8936f440acbd2bb30b4c1fe23c"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:decd32e8d7f934dde484e43431f60b069e87bb30a3a7e186cb6bd69caa0418f3"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c734982d6560356c173817576a1f3fa074a2d2b993e63bffa69105ae9ec144b"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-win32.whl", hash = "sha256:563b7ed1e789f763410c49e6fab51d61982eb94088b25338e65b89ad20b6b107"},
|
||||
{file = "ujson-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:8a2cbb044bc6e6764b9a089a2079432b8bd576dbff5faa808b562a8f3c97452b"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6c5d19fbdd29d5080926c863ba89591a2d3dbf592ea35b456cb2996004433d11"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4dc79db757b0dfa23a111a4573827a6ef57de65dbe8cdb202e45cf9ddf06aad5"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5700a179abacbdc8609737e595a598b7f107cd68615ded3f922f4c0d4b6009d6"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:287dea79473ce4941598c45dc34f9f692d48d7863b451541c5ce960ab54465fb"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:151faa9085c10351a04aea959a2bc25dfa2e21af26d9b614a221d045b7923ea4"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:285082924747958aa69e1dc2146c01db6b0921a0bb04b595beefe7fcffaffaf9"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dd74570fe59c738d4dc12d44eb89538b0b01fae9dda6cfe3ff3f6934877cf35"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6aba1e39ffdd83ec14832ea25bbb18266fea46bc69b8c0acbd996495826c0e6f"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-win32.whl", hash = "sha256:1358621686ddfda55171fc98c171bf5b1a80ce4d444134b70e1e449925fa014f"},
|
||||
{file = "ujson-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d1fab398734634f4b412512ed230d45522fc9f3dd9ca169f579474a491f662aa"},
|
||||
{file = "ujson-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d4830c8df958c45c16dfc43c8353403efd7f1a8e39b91a7e0e848d55b7fa8b48"},
|
||||
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bed7c1f95484644a2cc658efff4d1e75b8c806f6ef2b5c815f59e1cbe0d039"},
|
||||
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2db7cbe415d7329b9bff029a83851d1077836ec728fe1c32be34c9c3a5017ab2"},
|
||||
{file = "ujson-5.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73636001055667bbcc6a73b232da1d272f68a49a1f192efbe99e99ddf8ef1d21"},
|
||||
{file = "ujson-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:47bf966e1041ae8e568d7e8eb421d72d0521c30c28306b76c256832553e316c6"},
|
||||
{file = "ujson-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:66f857d8b8d7ea44e3fd5f2b7e471334f24b735423729771f5a7a7f69ab645ed"},
|
||||
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d2cb50aa526032b8812975c3832058763ee50e1dc3a1302431ed9d0922c3a1b"},
|
||||
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f615ee181b813c8f50a57d55354d0c0304a0be066962efdbef6f44517b26e3b2"},
|
||||
{file = "ujson-5.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5696c99a7dd567566c18490e8e346b2657967feb1e3c2004e91dbb253db0894"},
|
||||
{file = "ujson-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a68d5a8a46712ffe86db8ae1b4311714db534725521c71fd4c9e1cd062dae9a4"},
|
||||
{file = "ujson-5.3.0.tar.gz", hash = "sha256:ab938777b3ac0372231ee654a7f6a13787e587b1ca268d8aa7e6fb6846e477d0"},
|
||||
]
|
||||
ujson = []
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
|
||||
{file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
|
||||
|
||||
+5
-3
@@ -11,7 +11,7 @@ homepage = "https://speckle.systems/"
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.7.0, <4.0"
|
||||
python = ">=3.7.2, <4.0"
|
||||
pydantic = "^1.8.2"
|
||||
appdirs = "^1.4.4"
|
||||
gql = {extras = ["requests", "websockets"], version = "^3.3.0"}
|
||||
@@ -19,12 +19,14 @@ ujson = "^5.3.0"
|
||||
Deprecated = "^1.2.13"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
black = "^22.8.0"
|
||||
isort = "^5.7.0"
|
||||
pytest = "^6.2.2"
|
||||
pytest-ordering = "^0.6"
|
||||
pytest-cov = "^3.0.0"
|
||||
devtools = "^0.8.0"
|
||||
pylint = "^2.14.4"
|
||||
mypy = "^0.971"
|
||||
|
||||
|
||||
[tool.black]
|
||||
@@ -44,7 +46,7 @@ exclude = '''
|
||||
'''
|
||||
include = '\.pyi?$'
|
||||
line-length = 88
|
||||
target-version = ["py36", "py37", "py38"]
|
||||
target-version = ["py37", "py38", "py39", "py310"]
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
+20
-7
@@ -136,6 +136,8 @@ class SpeckleClient:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.account.token}",
|
||||
"Content-Type": "application/json",
|
||||
"apollographql-client-name": metrics.HOST_APP,
|
||||
"apollographql-client-version": metrics.HOST_APP_VERSION,
|
||||
}
|
||||
httptransport = RequestsHTTPTransport(
|
||||
url=self.graphql, headers=headers, verify=True, retries=3
|
||||
@@ -160,9 +162,26 @@ class SpeckleClient:
|
||||
return self.httpclient.execute(query)
|
||||
|
||||
def _init_resources(self) -> None:
|
||||
self.stream = stream.Resource(
|
||||
self.server = server.Resource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
server_version = None
|
||||
try:
|
||||
server_version = self.server.version()
|
||||
except:
|
||||
pass
|
||||
self.user = user.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.stream = stream.Resource(
|
||||
account=self.account,
|
||||
basepath=self.url,
|
||||
client=self.httpclient,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.commit = commit.Resource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
@@ -172,12 +191,6 @@ class SpeckleClient:
|
||||
self.object = object.Resource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.server = server.Resource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.user = user.Resource(
|
||||
account=self.account, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.subscribe = subscriptions.Resource(
|
||||
account=self.account,
|
||||
basepath=self.ws_url,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import os
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
|
||||
from typing import List, Optional
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.api.models import ServerInfo
|
||||
@@ -16,11 +16,11 @@ class UserInfo(BaseModel):
|
||||
|
||||
class Account(BaseModel):
|
||||
isDefault: bool = False
|
||||
token: str = None
|
||||
refreshToken: str = None
|
||||
token: Optional[str] = None
|
||||
refreshToken: Optional[str] = None
|
||||
serverInfo: ServerInfo = Field(default_factory=ServerInfo)
|
||||
userInfo: UserInfo = Field(default_factory=UserInfo)
|
||||
id: str = None
|
||||
id: Optional[str] = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Account(email: {self.userInfo.email}, server: {self.serverInfo.url}, isDefault: {self.isDefault})"
|
||||
@@ -45,12 +45,15 @@ 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)
|
||||
# pylint: disable=protected-access
|
||||
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 = []
|
||||
accounts: List[Account] = []
|
||||
res = account_storage.get_all_objects()
|
||||
account_storage.close()
|
||||
|
||||
if res:
|
||||
accounts.extend(Account.parse_raw(r[1]) for r in res)
|
||||
if json_acct_files:
|
||||
@@ -63,7 +66,8 @@ def get_local_accounts(base_path: str = None) -> List[Account]:
|
||||
raise SpeckleException(
|
||||
"Invalid json accounts could not be read. Please fix or remove them.",
|
||||
ex,
|
||||
)
|
||||
) from ex
|
||||
|
||||
metrics.track(
|
||||
metrics.ACCOUNTS,
|
||||
next(
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from unicodedata import name
|
||||
|
||||
|
||||
class HostAppVersion(Enum):
|
||||
v = "v"
|
||||
v6 = "v6"
|
||||
v7 = "v7"
|
||||
v2019 = "v2019"
|
||||
v2020 = "v2020"
|
||||
v2021 = "v2021"
|
||||
v2022 = "v2022"
|
||||
v2023 = "v2023"
|
||||
v2024 = "v2024"
|
||||
v2025 = "v2025"
|
||||
vSandbox = "vSandbox"
|
||||
vRevit = "vRevit"
|
||||
vRevit2021 = "vRevit2021"
|
||||
vRevit2022 = "vRevit2022"
|
||||
vRevit2023 = "vRevit2023"
|
||||
vRevit2024 = "vRevit2024"
|
||||
vRevit2025 = "vRevit2025"
|
||||
v25 = "v25"
|
||||
v26 = "v26"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.value
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
@dataclass
|
||||
class HostApplication:
|
||||
name: str
|
||||
slug: str
|
||||
|
||||
def get_version(self, version: HostAppVersion) -> str:
|
||||
return f"{name.replace(' ', '')}{str(version).strip('v')}"
|
||||
|
||||
RHINO = HostApplication("Rhino", "rhino")
|
||||
GRASSHOPPER = HostApplication("Grasshopper", "grasshopper")
|
||||
REVIT = HostApplication("Revit", "revit")
|
||||
DYNAMO = HostApplication("Dynamo", "dynamo")
|
||||
UNITY = HostApplication("Unity", "unity")
|
||||
GSA = HostApplication("GSA", "gsa")
|
||||
CIVIL = HostApplication("Civil 3D", "civil3d")
|
||||
AUTOCAD = HostApplication("AutoCAD", "autocad")
|
||||
MICROSTATION = HostApplication("MicroStation", "microstation")
|
||||
OPENROADS = HostApplication("OpenRoads", "openroads")
|
||||
OPENRAIL = HostApplication("OpenRail", "openrail")
|
||||
OPENBUILDINGS = HostApplication("OpenBuildings", "openbuildings")
|
||||
ETABS = HostApplication("ETABS", "etabs")
|
||||
SAP2000 = HostApplication("SAP2000", "sap2000")
|
||||
CSIBRIDGE = HostApplication("CSIBridge", "csibridge")
|
||||
SAFE = HostApplication("SAFE", "safe")
|
||||
TEKLASTRUCTURES = HostApplication("Tekla Structures", "teklastructures")
|
||||
DXF = HostApplication("DXF Converter", "dxf")
|
||||
EXCEL = HostApplication("Excel", "excel")
|
||||
UNREAL = HostApplication("Unreal", "unreal")
|
||||
POWERBI = HostApplication("Power BI", "powerbi")
|
||||
BLENDER = HostApplication("Blender", "blender")
|
||||
QGIS = HostApplication("QGIS", "qgis")
|
||||
ARCGIS = HostApplication("ArcGIS", "arcgis")
|
||||
SKETCHUP = HostApplication("SketchUp", "sketchup")
|
||||
ARCHICAD = HostApplication("Archicad", "archicad")
|
||||
TOPSOLID = HostApplication("TopSolid", "topsolid")
|
||||
PYTHON = HostApplication("Python", "python")
|
||||
NET = HostApplication(".NET", "net")
|
||||
OTHER = HostApplication("Other", "other")
|
||||
|
||||
_app_name_host_app_mapping = {
|
||||
"dynamo": DYNAMO,
|
||||
"revit": REVIT,
|
||||
"autocad": AUTOCAD,
|
||||
"civil": CIVIL,
|
||||
"rhino": RHINO,
|
||||
"grasshopper": GRASSHOPPER,
|
||||
"unity": UNITY,
|
||||
"gsa": GSA,
|
||||
"microstation": MICROSTATION,
|
||||
"openroads": OPENROADS,
|
||||
"openrail": OPENRAIL,
|
||||
"openbuildings": OPENBUILDINGS,
|
||||
"etabs": ETABS,
|
||||
"sap": SAP2000,
|
||||
"csibridge": CSIBRIDGE,
|
||||
"safe": SAFE,
|
||||
"teklastructures": TEKLASTRUCTURES,
|
||||
"dxf": DXF,
|
||||
"excel": EXCEL,
|
||||
"unreal": UNREAL,
|
||||
"powerbi": POWERBI,
|
||||
"blender": BLENDER,
|
||||
"qgis": QGIS,
|
||||
"arcgis": ARCGIS,
|
||||
"sketchup": SKETCHUP,
|
||||
"archicad": ARCHICAD,
|
||||
"topsolid": TOPSOLID,
|
||||
"python": PYTHON,
|
||||
"net": NET
|
||||
}
|
||||
|
||||
def get_host_app_from_string(app_name: str) -> HostApplication:
|
||||
app_name = app_name.lower().replace(" ", "")
|
||||
for partial_app_name, host_app in _app_name_host_app_mapping.items():
|
||||
if (partial_app_name in app_name):
|
||||
return host_app
|
||||
return HostApplication(app_name, app_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(HostAppVersion.v)
|
||||
+21
-3
@@ -3,10 +3,10 @@
|
||||
# timestamp: 2020-11-17T14:33:13+00:00
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
class Collaborator(BaseModel):
|
||||
@@ -110,6 +110,24 @@ class User(BaseModel):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class PendingStreamCollaborator(BaseModel):
|
||||
id: Optional[str]
|
||||
inviteId: Optional[str]
|
||||
streamId: Optional[str]
|
||||
streamName: Optional[str]
|
||||
title: Optional[str]
|
||||
role: Optional[str]
|
||||
invitedBy: Optional[User]
|
||||
user: Optional[User]
|
||||
token: Optional[str]
|
||||
|
||||
def __repr__(self):
|
||||
return f"PendingStreamCollaborator( inviteId: {self.inviteId}, streamId: {self.streamId}, role: {self.role}, title: {self.title}, invitedBy: {self.user.name if self.user else None})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class Activity(BaseModel):
|
||||
actionType: Optional[str]
|
||||
info: Optional[dict]
|
||||
@@ -133,7 +151,7 @@ class ActivityCollection(BaseModel):
|
||||
cursor: Optional[datetime]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"ActivityCollection( totalCount: {self.totalCount}, items: {len(self.items) if self.items else 0}, cursor: {self.cursor.isoformat()} )"
|
||||
return f"ActivityCollection( totalCount: {self.totalCount}, items: {len(self.items) if self.items else 0}, cursor: {self.cursor.isoformat() if self.cursor else None} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@@ -2,7 +2,6 @@ from typing import List
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.transports.server import ServerTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
@@ -29,6 +28,9 @@ def send(
|
||||
message="You need to provide at least one transport: cannot send with an empty transport list and no default cache"
|
||||
)
|
||||
|
||||
if isinstance(transports, AbstractTransport):
|
||||
transports = [transports]
|
||||
|
||||
if transports is None:
|
||||
metrics.track(metrics.SEND)
|
||||
transports = []
|
||||
@@ -50,6 +52,16 @@ def receive(
|
||||
remote_transport: AbstractTransport = None,
|
||||
local_transport: AbstractTransport = None,
|
||||
) -> Base:
|
||||
metrics.track(metrics.RECEIVE, getattr(remote_transport, "account", None))
|
||||
return _untracked_receive(obj_id, remote_transport, local_transport)
|
||||
|
||||
|
||||
def _untracked_receive(
|
||||
obj_id: str,
|
||||
remote_transport: AbstractTransport = None,
|
||||
local_transport: AbstractTransport = None,
|
||||
) -> Base:
|
||||
|
||||
"""Receives an object from a transport.
|
||||
|
||||
Arguments:
|
||||
@@ -61,13 +73,12 @@ def receive(
|
||||
Returns:
|
||||
Base -- the base object
|
||||
"""
|
||||
metrics.track(metrics.RECEIVE, getattr(remote_transport, "account", None))
|
||||
if not local_transport:
|
||||
local_transport = SQLiteTransport()
|
||||
|
||||
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 deserialisation 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)
|
||||
@@ -121,3 +132,6 @@ def deserialize(obj_string: str, read_transport: AbstractTransport = None) -> Ba
|
||||
serializer = BaseObjectSerializer(read_transport=read_transport)
|
||||
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
|
||||
|
||||
__all__ = [receive.__name__, send.__name__, serialize.__name__, deserialize.__name__]
|
||||
|
||||
+55
-20
@@ -1,10 +1,14 @@
|
||||
from graphql import DocumentNode
|
||||
from specklepy.api.credentials import Account
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from typing import Dict, List
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
||||
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.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
UnsupportedException,
|
||||
)
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
|
||||
|
||||
@@ -15,28 +19,30 @@ class ResourceBase(object):
|
||||
basepath: str,
|
||||
client: Client,
|
||||
name: str,
|
||||
methods: list,
|
||||
server_version: Optional[Tuple[Any, ...]] = None,
|
||||
) -> None:
|
||||
self.account = account
|
||||
self.basepath = basepath
|
||||
self.client = client
|
||||
self.name = name
|
||||
self.methods = methods
|
||||
self.schema = None
|
||||
self.server_version = server_version
|
||||
self.schema: Optional[Type] = None
|
||||
|
||||
def _step_into_response(self, response: dict, return_type: str or List):
|
||||
def _step_into_response(self, response: dict, return_type: Union[str, List, None]):
|
||||
"""Step into the dict to get the relevant data"""
|
||||
if return_type is None:
|
||||
return response
|
||||
elif isinstance(return_type, str):
|
||||
if isinstance(return_type, str):
|
||||
return response[return_type]
|
||||
elif isinstance(return_type, List):
|
||||
if isinstance(return_type, List):
|
||||
for key in return_type:
|
||||
response = response[key]
|
||||
return response
|
||||
|
||||
def _parse_response(self, response: dict or list, schema=None):
|
||||
def _parse_response(self, response: Union[dict, list, None], schema=None):
|
||||
"""Try to create a class instance from the response"""
|
||||
if response is None:
|
||||
return None
|
||||
if isinstance(response, list):
|
||||
return [self._parse_response(response=r, schema=schema) for r in response]
|
||||
if schema:
|
||||
@@ -52,26 +58,26 @@ class ResourceBase(object):
|
||||
|
||||
def make_request(
|
||||
self,
|
||||
query: gql,
|
||||
query: DocumentNode,
|
||||
params: Dict = None,
|
||||
return_type: str or List = None,
|
||||
return_type: Union[str, List, None] = None,
|
||||
schema=None,
|
||||
parse_response: bool = True,
|
||||
) -> Dict or GraphQLException:
|
||||
) -> Any:
|
||||
"""Executes the GraphQL query"""
|
||||
try:
|
||||
response = self.client.execute(query, variable_values=params)
|
||||
except Exception as e:
|
||||
if isinstance(e, TransportQueryError):
|
||||
except Exception as ex:
|
||||
if isinstance(ex, TransportQueryError):
|
||||
return GraphQLException(
|
||||
message=f"Failed to execute the GraphQL {self.name} request. Errors: {e.errors}",
|
||||
errors=e.errors,
|
||||
data=e.data,
|
||||
message=f"Failed to execute the GraphQL {self.name} request. Errors: {ex.errors}",
|
||||
errors=ex.errors,
|
||||
data=ex.data,
|
||||
)
|
||||
else:
|
||||
return SpeckleException(
|
||||
message=f"Failed to execute the GraphQL {self.name} request. Inner exception: {e}",
|
||||
exception=e,
|
||||
message=f"Failed to execute the GraphQL {self.name} request. Inner exception: {ex}",
|
||||
exception=ex,
|
||||
)
|
||||
|
||||
response = self._step_into_response(response=response, return_type=return_type)
|
||||
@@ -80,3 +86,32 @@ class ResourceBase(object):
|
||||
return self._parse_response(response=response, schema=schema)
|
||||
else:
|
||||
return response
|
||||
|
||||
def _check_server_version_at_least(
|
||||
self, target_version: Tuple[Any, ...], unsupported_message: str = None
|
||||
):
|
||||
"""Use this check to guard against making unsupported requests on older servers.
|
||||
|
||||
Arguments:
|
||||
target_version {tuple} -- the minimum 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
|
||||
"""
|
||||
if not unsupported_message:
|
||||
unsupported_message = f"The client method used is not supported on Speckle Server versions prior to v{'.'.join(target_version)}"
|
||||
# if version is dev, it should be supported... (or not)
|
||||
if self.server_version == ("dev",):
|
||||
return
|
||||
if self.server_version and self.server_version < target_version:
|
||||
raise UnsupportedException(unsupported_message)
|
||||
|
||||
def _check_invites_supported(self):
|
||||
"""Invites are only supported for Speckle Server >= 2.6.4.
|
||||
Use this check to guard against making unsupported requests on older servers.
|
||||
"""
|
||||
self._check_server_version_at_least(
|
||||
(2, 6, 4),
|
||||
(
|
||||
"Stream invites are only supported as of Speckle Server v2.6.4. "
|
||||
"Please update your Speckle Server to use this method or use the `grant_permission` flow instead."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import inspect
|
||||
import pkgutil
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from specklepy.api.models import Branch
|
||||
from specklepy.logging import metrics
|
||||
|
||||
NAME = "branch"
|
||||
METHODS = ["create"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
@@ -16,7 +15,6 @@ class Resource(ResourceBase):
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
)
|
||||
self.schema = Branch
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from specklepy.logging import metrics
|
||||
|
||||
|
||||
NAME = "commit"
|
||||
METHODS = []
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
@@ -18,7 +17,6 @@ class Resource(ResourceBase):
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
)
|
||||
self.schema = Commit
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from specklepy.api.resource import ResourceBase
|
||||
from specklepy.objects.base import Base
|
||||
|
||||
NAME = "object"
|
||||
METHODS = []
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
@@ -16,7 +15,6 @@ class Resource(ResourceBase):
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
)
|
||||
self.schema = Base
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from typing import Dict, List
|
||||
import re
|
||||
from typing import Any, Dict, List, Tuple
|
||||
from gql import gql
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
|
||||
|
||||
NAME = "server"
|
||||
METHODS = ["get", "apps"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
@@ -18,7 +19,6 @@ class Resource(ResourceBase):
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
)
|
||||
|
||||
def get(self) -> ServerInfo:
|
||||
@@ -61,6 +61,39 @@ class Resource(ResourceBase):
|
||||
query=query, return_type="serverInfo", schema=ServerInfo
|
||||
)
|
||||
|
||||
def version(self) -> Tuple[Any, ...]:
|
||||
"""Get the server version
|
||||
|
||||
Returns:
|
||||
tuple -- 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
|
||||
query = gql(
|
||||
"""
|
||||
query Server {
|
||||
serverInfo {
|
||||
version
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
ver = self.make_request(
|
||||
query=query, return_type=["serverInfo", "version"], parse_response=False
|
||||
)
|
||||
if isinstance(ver, Exception):
|
||||
raise GraphQLException(
|
||||
f"Could not get server version for {self.basepath}", [ver]
|
||||
)
|
||||
|
||||
# pylint: disable=consider-using-generator; (list comp is faster)
|
||||
return tuple(
|
||||
[
|
||||
int(segment) if segment.isdigit() else segment
|
||||
for segment in re.split(r"\.|-", ver)
|
||||
]
|
||||
)
|
||||
|
||||
def apps(self) -> Dict:
|
||||
"""Get the apps registered on the server
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
from deprecated import deprecated
|
||||
from gql import gql
|
||||
from typing import List
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.api.models import ActivityCollection, Stream
|
||||
from specklepy.api.models import ActivityCollection, PendingStreamCollaborator, Stream
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import UnsupportedException, SpeckleException
|
||||
|
||||
|
||||
NAME = "stream"
|
||||
METHODS = ["list", "create", "get", "update", "delete", "search", "activity"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for streams"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
server_version=server_version,
|
||||
)
|
||||
|
||||
self.schema = Stream
|
||||
@@ -342,9 +342,18 @@ class Resource(ResourceBase):
|
||||
query=query, params=params, return_type=["streamFavorite"]
|
||||
)
|
||||
|
||||
@deprecated(
|
||||
version="2.6.4",
|
||||
reason=(
|
||||
"As of Speckle Server v2.6.4, this method is deprecated. "
|
||||
"Users need to be invited and accept the invite before being added to a stream"
|
||||
),
|
||||
)
|
||||
def grant_permission(self, stream_id: str, user_id: str, role: str):
|
||||
"""Grant permissions to a user on a given stream
|
||||
|
||||
Valid for Speckle Server version < 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to grant permissions to
|
||||
user_id {str} -- the id of the user to grant permissions for
|
||||
@@ -354,6 +363,19 @@ class Resource(ResourceBase):
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
metrics.track(metrics.PERMISSION, self.account, {"name": "add", "role": role})
|
||||
# we're checking for the actual version info, and if the version is 'dev' we treat it
|
||||
# as an up to date instance
|
||||
if self.server_version and (
|
||||
self.server_version == ("dev",) or self.server_version >= (2, 6, 4)
|
||||
):
|
||||
raise UnsupportedException(
|
||||
(
|
||||
"Server mutation `grant_permission` is no longer supported as of Speckle Server v2.6.4. "
|
||||
"Please use the new `update_permission` method to change an existing user's permission "
|
||||
"or use the `invite` method to invite a user to a stream."
|
||||
)
|
||||
)
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamGrantPermission($permission_params: StreamGrantPermissionInput !) {
|
||||
@@ -377,6 +399,284 @@ class Resource(ResourceBase):
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def get_all_pending_invites(
|
||||
self, stream_id: str
|
||||
) -> List[PendingStreamCollaborator]:
|
||||
"""Get all of the pending invites on a stream.
|
||||
You must be a `stream:owner` to query this.
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the stream id from which to get the pending invites
|
||||
|
||||
Returns:
|
||||
List[PendingStreamCollaborator] -- a list of pending invites for the specified stream
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "get"})
|
||||
self._check_invites_supported()
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query StreamInvites($streamId: String!) {
|
||||
stream(id: $streamId){
|
||||
pendingCollaborators {
|
||||
id
|
||||
token
|
||||
inviteId
|
||||
streamId
|
||||
streamName
|
||||
title
|
||||
role
|
||||
invitedBy{
|
||||
id
|
||||
name
|
||||
company
|
||||
avatar
|
||||
}
|
||||
user {
|
||||
id
|
||||
name
|
||||
company
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"streamId": stream_id}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type=["stream", "pendingCollaborators"],
|
||||
schema=PendingStreamCollaborator,
|
||||
)
|
||||
|
||||
def invite(
|
||||
self,
|
||||
stream_id: str,
|
||||
email: str = None,
|
||||
user_id: str = None,
|
||||
role: str = "stream:contributor", # should default be reviewer?
|
||||
message: str = None,
|
||||
):
|
||||
"""Invite someone to a stream using either their email or user id
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to invite the user to
|
||||
email {str} -- the email of the user to invite (use this OR `user_id`)
|
||||
user_id {str} -- the id of the user to invite (use this OR `email`)
|
||||
role {str} -- the role to assign to the user (defaults to `stream:contributor`)
|
||||
message {str} -- a message to send along with this invite to the specified user
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "create"})
|
||||
self._check_invites_supported()
|
||||
|
||||
if email is None and user_id is None:
|
||||
raise SpeckleException(
|
||||
"You must provide either an email or a user id to use the `stream.invite` method"
|
||||
)
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamInviteCreate($input: StreamInviteCreateInput!) {
|
||||
streamInviteCreate(input: $input)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {
|
||||
"email": email,
|
||||
"userId": user_id,
|
||||
"streamId": stream_id,
|
||||
"message": message,
|
||||
"role": role,
|
||||
}
|
||||
params = {"input": {k: v for k, v in params.items() if v is not None}}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamInviteCreate",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def invite_batch(
|
||||
self,
|
||||
stream_id: str,
|
||||
emails: List[str] = None,
|
||||
user_ids: List[None] = None,
|
||||
message: str = None,
|
||||
) -> bool:
|
||||
"""Invite a batch of users to a specified stream.
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to invite the user to
|
||||
emails {List[str]} -- the email of the user to invite (use this and/or `user_ids`)
|
||||
user_id {List[str]} -- the id of the user to invite (use this and/or `emails`)
|
||||
message {str} -- a message to send along with this invite to the specified user
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "batch create"})
|
||||
self._check_invites_supported()
|
||||
if emails is None and user_ids is None:
|
||||
raise SpeckleException(
|
||||
"You must provide either an email or a user id to use the `stream.invite` method"
|
||||
)
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamInviteBatchCreate($input: [StreamInviteCreateInput!]!) {
|
||||
streamInviteBatchCreate(input: $input)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
email_invites = [
|
||||
{"streamId": stream_id, "message": message, "email": email}
|
||||
for email in emails
|
||||
if emails is not None
|
||||
]
|
||||
|
||||
user_invites = [
|
||||
{"streamId": stream_id, "message": message, "userId": user_id}
|
||||
for user_id in user_ids
|
||||
if user_ids is not None
|
||||
]
|
||||
|
||||
params = {"input": [*email_invites, *user_invites]}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamInviteBatchCreate",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def invite_cancel(self, stream_id: str, invite_id: str) -> bool:
|
||||
"""Cancel an existing stream invite
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream invite
|
||||
invite_id {str} -- the id of the invite to use
|
||||
|
||||
Returns:
|
||||
bool -- true if the operation was successful
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "cancel"})
|
||||
self._check_invites_supported()
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamInviteCancel($streamId: String!, $inviteId: String!) {
|
||||
streamInviteCancel(streamId: $streamId, inviteId: $inviteId)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"streamId": stream_id, "inviteId": invite_id}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamInviteCancel",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def invite_use(self, stream_id: str, token: str, accept: bool = True) -> bool:
|
||||
"""Accept or decline a stream invite
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream for which the user has a pending invite
|
||||
token {str} -- the token of the invite to use
|
||||
accept {bool} -- whether or not to accept the invite (defaults to True)
|
||||
|
||||
Returns:
|
||||
bool -- true if the operation was successful
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "use"})
|
||||
self._check_invites_supported()
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamInviteUse($accept: Boolean!, $streamId: String!, $token: String!) {
|
||||
streamInviteUse(accept: $accept, streamId: $streamId, token: $token)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"streamId": stream_id, "token": token, "accept": accept}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamInviteUse",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def update_permission(self, stream_id: str, user_id: str, role: str):
|
||||
"""Updates permissions for a user on a given stream
|
||||
|
||||
Valid for Speckle Server >=2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to grant permissions to
|
||||
user_id {str} -- the id of the user to grant permissions for
|
||||
role {str} -- the role to grant the user
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
metrics.track(
|
||||
metrics.PERMISSION, self.account, {"name": "update", "role": role}
|
||||
)
|
||||
if self.server_version and (
|
||||
self.server_version != ("dev",) and self.server_version < (2, 6, 4)
|
||||
):
|
||||
raise UnsupportedException(
|
||||
(
|
||||
"Server mutation `update_permission` is only supported as of Speckle Server v2.6.4. "
|
||||
"Please update your Speckle Server to use this method or use the `grant_permission` method instead."
|
||||
)
|
||||
)
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamUpdatePermission($permission_params: StreamUpdatePermissionInput !) {
|
||||
streamUpdatePermission(permissionParams: $permission_params)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {
|
||||
"permission_params": {
|
||||
"streamId": stream_id,
|
||||
"userId": user_id,
|
||||
"role": role,
|
||||
}
|
||||
}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamUpdatePermission",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def revoke_permission(self, stream_id: str, user_id: str):
|
||||
"""Revoke permissions from a user on a given stream
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
from typing import Callable, Dict, List
|
||||
from typing import Callable, Dict, List, Union
|
||||
from functools import wraps
|
||||
from gql import gql
|
||||
from graphql import DocumentNode
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.resources.stream import Stream
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
NAME = "subscribe"
|
||||
METHODS = [
|
||||
"stream_added",
|
||||
"stream_updated",
|
||||
"stream_removed",
|
||||
]
|
||||
|
||||
|
||||
def check_wsclient(function):
|
||||
@@ -35,7 +31,6 @@ class Resource(ResourceBase):
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
)
|
||||
|
||||
@check_wsclient
|
||||
@@ -109,15 +104,15 @@ class Resource(ResourceBase):
|
||||
@check_wsclient
|
||||
async def subscribe(
|
||||
self,
|
||||
query: gql,
|
||||
query: DocumentNode,
|
||||
params: Dict = None,
|
||||
callback: Callable = None,
|
||||
return_type: str or List = None,
|
||||
return_type: Union[str, List] = None,
|
||||
schema=None,
|
||||
parse_response: bool = True,
|
||||
):
|
||||
# if self.client.transport.websocket is None:
|
||||
# TODO: add multiple subs to the same ws connection
|
||||
# TODO: add multiple subs to the same ws connection
|
||||
async with self.client as session:
|
||||
async for res in session.subscribe(query, variable_values=params):
|
||||
res = self._step_into_response(response=res, return_type=return_type)
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
from typing import List, Optional, Union
|
||||
from datetime import datetime, timezone
|
||||
from gql import gql
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from typing import List
|
||||
from gql import gql
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import ActivityCollection, User
|
||||
from specklepy.api.models import ActivityCollection, PendingStreamCollaborator, User
|
||||
|
||||
NAME = "user"
|
||||
METHODS = ["get", "search", "update", "activity"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for users"""
|
||||
|
||||
def __init__(self, account, basepath, client) -> None:
|
||||
def __init__(self, account, basepath, client, server_version) -> None:
|
||||
super().__init__(
|
||||
account=account,
|
||||
basepath=basepath,
|
||||
client=client,
|
||||
name=NAME,
|
||||
methods=METHODS,
|
||||
server_version=server_version,
|
||||
)
|
||||
self.schema = User
|
||||
|
||||
@@ -55,7 +54,9 @@ class Resource(ResourceBase):
|
||||
|
||||
return self.make_request(query=query, params=params, return_type="user")
|
||||
|
||||
def search(self, search_query: str, limit: int = 25) -> List[User]:
|
||||
def search(
|
||||
self, search_query: str, limit: int = 25
|
||||
) -> Union[List[User], SpeckleException]:
|
||||
"""Searches for user by name or email. The search query must be at least 3 characters long
|
||||
|
||||
Arguments:
|
||||
@@ -188,3 +189,92 @@ class Resource(ResourceBase):
|
||||
return_type=["user", "activity"],
|
||||
schema=ActivityCollection,
|
||||
)
|
||||
|
||||
def get_all_pending_invites(self) -> List[PendingStreamCollaborator]:
|
||||
"""Get all of the active user's pending stream invites
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Returns:
|
||||
List[PendingStreamCollaborator] -- a list of pending invites for the current user
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "get"})
|
||||
self._check_invites_supported()
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query StreamInvites {
|
||||
streamInvites{
|
||||
id
|
||||
token
|
||||
inviteId
|
||||
streamId
|
||||
streamName
|
||||
title
|
||||
role
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
company
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
return_type="streamInvites",
|
||||
schema=PendingStreamCollaborator,
|
||||
)
|
||||
|
||||
def get_pending_invite(
|
||||
self, stream_id: str, token: str = None
|
||||
) -> Optional[PendingStreamCollaborator]:
|
||||
"""Get a particular pending invite for the active user on a given stream.
|
||||
If no invite_id is provided, any valid invite will be returned.
|
||||
|
||||
Requires Speckle Server version >= 2.6.4
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to look for invites on
|
||||
token {str} -- the token of the invite to look for (optional)
|
||||
|
||||
Returns:
|
||||
PendingStreamCollaborator -- the invite for the given stream (or None if it isn't found)
|
||||
"""
|
||||
metrics.track(metrics.INVITE, self.account, {"name": "get"})
|
||||
self._check_invites_supported()
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query StreamInvite($streamId: String!, $token: String) {
|
||||
streamInvite(streamId: $streamId, token: $token) {
|
||||
id
|
||||
token
|
||||
streamId
|
||||
streamName
|
||||
title
|
||||
role
|
||||
invitedBy {
|
||||
id
|
||||
name
|
||||
company
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"streamId": stream_id}
|
||||
if token:
|
||||
params["token"] = token
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamInvite",
|
||||
schema=PendingStreamCollaborator,
|
||||
)
|
||||
|
||||
@@ -1,640 +0,0 @@
|
||||
|
||||
|
||||
scalar DateTime
|
||||
|
||||
scalar EmailAddress
|
||||
|
||||
scalar BigInt
|
||||
|
||||
scalar JSONObject
|
||||
|
||||
|
||||
directive @hasScope(scope: String!) on FIELD_DEFINITION
|
||||
directive @hasRole(role: String!) on FIELD_DEFINITION
|
||||
|
||||
type Query {
|
||||
"""
|
||||
Stare into the void.
|
||||
"""
|
||||
_: String
|
||||
}
|
||||
type Mutation{
|
||||
"""
|
||||
The void stares back.
|
||||
"""
|
||||
_: String
|
||||
}
|
||||
type Subscription{
|
||||
"""
|
||||
It's lonely in the void.
|
||||
"""
|
||||
_: String
|
||||
},extend type Query {
|
||||
"""
|
||||
Gets a specific app from the server.
|
||||
"""
|
||||
app( id: String! ): ServerApp
|
||||
|
||||
"""
|
||||
Returns all the publicly available apps on this server.
|
||||
"""
|
||||
apps: [ServerAppListItem]
|
||||
}
|
||||
|
||||
type ServerApp {
|
||||
id: String!
|
||||
secret: String!
|
||||
name: String!
|
||||
description: String
|
||||
termsAndConditionsLink: String
|
||||
logo: String
|
||||
public: Boolean
|
||||
trustByDefault: Boolean
|
||||
author: AppAuthor
|
||||
createdAt: DateTime!
|
||||
redirectUrl: String!
|
||||
scopes: [Scope]!
|
||||
}
|
||||
|
||||
type ServerAppListItem {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
termsAndConditionsLink: String
|
||||
logo: String
|
||||
author: AppAuthor
|
||||
}
|
||||
|
||||
type AppAuthor {
|
||||
name: String
|
||||
id: String
|
||||
}
|
||||
|
||||
extend type User {
|
||||
"""
|
||||
Returns the apps you have authorized.
|
||||
"""
|
||||
authorizedApps: [ServerAppListItem]
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:read")
|
||||
|
||||
"""
|
||||
Returns the apps you have created.
|
||||
"""
|
||||
createdApps: [ServerAppListItem]
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:read")
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Register a new third party application.
|
||||
"""
|
||||
appCreate(app: AppCreateInput!): String!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:write")
|
||||
|
||||
"""
|
||||
Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.**
|
||||
"""
|
||||
appUpdate(app: AppUpdateInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:write")
|
||||
|
||||
"""
|
||||
Deletes a thirty party application.
|
||||
"""
|
||||
appDelete(appId: String!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:write")
|
||||
|
||||
"""
|
||||
Revokes (de-authorizes) an application that you have previously authorized.
|
||||
"""
|
||||
appRevokeAccess(appId: String!): Boolean
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "apps:write")
|
||||
|
||||
}
|
||||
|
||||
input AppCreateInput {
|
||||
name: String!
|
||||
description: String!
|
||||
termsAndConditionsLink: String
|
||||
logo: String
|
||||
public: Boolean
|
||||
redirectUrl: String!
|
||||
scopes: [String]!
|
||||
}
|
||||
|
||||
input AppUpdateInput {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String!
|
||||
termsAndConditionsLink: String
|
||||
logo: String
|
||||
public: Boolean
|
||||
redirectUrl: String!
|
||||
scopes: [String]!
|
||||
}
|
||||
,extend type ServerInfo {
|
||||
"""
|
||||
The authentication strategies available on this server.
|
||||
"""
|
||||
authStrategies: [AuthStrategy]
|
||||
}
|
||||
|
||||
type AuthStrategy {
|
||||
id: String!,
|
||||
name: String!,
|
||||
icon: String!,
|
||||
url: String!,
|
||||
color: String
|
||||
}
|
||||
,extend type User{
|
||||
"""
|
||||
Returns a list of your personal api tokens.
|
||||
"""
|
||||
apiTokens: [ApiToken]
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "tokens:read")
|
||||
}
|
||||
|
||||
type ApiToken {
|
||||
id: String!
|
||||
name: String!
|
||||
lastChars: String!
|
||||
scopes: [String]!
|
||||
createdAt: DateTime! #date
|
||||
lifespan: BigInt!
|
||||
lastUsed: String! #date
|
||||
}
|
||||
|
||||
input ApiTokenCreateInput {
|
||||
scopes: [String!]!,
|
||||
name: String!,
|
||||
lifespan: BigInt
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Creates an personal api token.
|
||||
"""
|
||||
apiTokenCreate(token: ApiTokenCreateInput!):String!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "tokens:write")
|
||||
"""
|
||||
Revokes (deletes) an personal api token.
|
||||
"""
|
||||
apiTokenRevoke(token: String!):Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "tokens:write")
|
||||
}
|
||||
,extend type Stream {
|
||||
commits(limit: Int! = 25, cursor: String): CommitCollection
|
||||
commit(id: String!): Commit
|
||||
branches(limit: Int! = 25, cursor: String): BranchCollection
|
||||
branch(name: String!): Branch
|
||||
}
|
||||
|
||||
extend type User {
|
||||
commits(limit: Int! = 25, cursor: String): CommitCollectionUser
|
||||
}
|
||||
|
||||
type Branch {
|
||||
id: String!
|
||||
name: String!
|
||||
author: User!
|
||||
description: String
|
||||
commits(limit: Int! = 25, cursor: String): CommitCollection
|
||||
}
|
||||
|
||||
type Commit {
|
||||
id: String!
|
||||
referencedObject: String!
|
||||
message: String
|
||||
authorName: String
|
||||
authorId: String
|
||||
createdAt: DateTime
|
||||
}
|
||||
|
||||
type CommitCollectionUserNode {
|
||||
id: String!
|
||||
referencedObject: String!
|
||||
message: String
|
||||
streamId: String
|
||||
streamName: String
|
||||
createdAt: DateTime
|
||||
}
|
||||
|
||||
type BranchCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [Branch]
|
||||
}
|
||||
|
||||
type CommitCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [Commit]
|
||||
}
|
||||
|
||||
type CommitCollectionUser {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [CommitCollectionUserNode]
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
branchCreate(branch: BranchCreateInput!): String!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
branchUpdate(branch: BranchUpdateInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
branchDelete(branch: BranchDeleteInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
|
||||
commitCreate(commit: CommitCreateInput!): String!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
commitUpdate(commit: CommitUpdateInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
commitDelete(commit: CommitDeleteInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
# TODO: auth for these subscriptions
|
||||
"""
|
||||
Subscribe to branch created event
|
||||
"""
|
||||
branchCreated(streamId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
"""
|
||||
Subscribe to branch updated event.
|
||||
"""
|
||||
branchUpdated(streamId: String!, branchId: String): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
"""
|
||||
Subscribe to branch deleted event
|
||||
"""
|
||||
branchDeleted(streamId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
"""
|
||||
Subscribe to commit created event
|
||||
"""
|
||||
commitCreated(streamId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
"""
|
||||
Subscribe to commit updated event.
|
||||
"""
|
||||
commitUpdated(streamId: String!, commitId: String): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
"""
|
||||
Subscribe to commit deleted event
|
||||
"""
|
||||
commitDeleted(streamId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
}
|
||||
|
||||
input BranchCreateInput {
|
||||
streamId: String!
|
||||
name: String!
|
||||
description: String
|
||||
}
|
||||
|
||||
input BranchUpdateInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
name: String
|
||||
description: String
|
||||
}
|
||||
|
||||
input BranchDeleteInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
}
|
||||
|
||||
input CommitCreateInput {
|
||||
streamId: String!
|
||||
branchName: String!
|
||||
objectId: String!
|
||||
message: String
|
||||
previousCommitIds: [String]
|
||||
}
|
||||
|
||||
input CommitUpdateInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
message: String!
|
||||
}
|
||||
|
||||
input CommitDeleteInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
}
|
||||
,extend type Stream {
|
||||
object( id: String! ): Object
|
||||
}
|
||||
|
||||
type Object {
|
||||
id: String!
|
||||
speckleType: String!
|
||||
applicationId: String
|
||||
createdAt: DateTime
|
||||
totalChildrenCount: Int
|
||||
"""
|
||||
The full object, with all its props & other things. **NOTE:** If you're requesting objects for the purpose of recreating & displaying, you probably only want to request this specific field.
|
||||
"""
|
||||
data: JSONObject
|
||||
"""
|
||||
Get any objects that this object references. In the case of commits, this will give you a commit's constituent objects.
|
||||
**NOTE**: Providing any of the two last arguments ( `query`, `orderBy` ) will trigger a different code branch that executes a much more expensive SQL query. It is not recommended to do so for basic clients that are interested in purely getting all the objects of a given commit.
|
||||
"""
|
||||
children(
|
||||
limit: Int! = 100,
|
||||
depth: Int! = 50,
|
||||
select: [String],
|
||||
cursor: String,
|
||||
query: [JSONObject!],
|
||||
orderBy: JSONObject ): ObjectCollection!
|
||||
}
|
||||
|
||||
type ObjectCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
objects: [Object]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
objectCreate( objectInput: ObjectCreateInput! ): [String]!
|
||||
}
|
||||
|
||||
input ObjectCreateInput {
|
||||
"""
|
||||
The stream against which these objects will be created.
|
||||
"""
|
||||
streamId: String!
|
||||
"""
|
||||
The objects you want to create.
|
||||
"""
|
||||
objects: [JSONObject]!
|
||||
},extend type Query {
|
||||
serverInfo: ServerInfo!
|
||||
}
|
||||
|
||||
"""
|
||||
Information about this server.
|
||||
"""
|
||||
type ServerInfo {
|
||||
name: String!
|
||||
company: String
|
||||
description: String
|
||||
adminContact: String
|
||||
canonicalUrl: String
|
||||
termsOfService: String
|
||||
roles: [Role]!
|
||||
scopes: [Scope]!
|
||||
}
|
||||
|
||||
"""
|
||||
Available roles.
|
||||
"""
|
||||
type Role {
|
||||
name: String!
|
||||
description: String!
|
||||
resourceTarget: String!
|
||||
}
|
||||
|
||||
"""
|
||||
Available scopes.
|
||||
"""
|
||||
type Scope {
|
||||
name: String!
|
||||
description: String!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
serverInfoUpdate(info: ServerInfoUpdateInput!): Boolean
|
||||
@hasRole(role: "server:admin")
|
||||
@hasScope(scope: "server:setup")
|
||||
}
|
||||
|
||||
input ServerInfoUpdateInput {
|
||||
name: String!
|
||||
company: String
|
||||
description: String
|
||||
adminContact: String
|
||||
termsOfService: String
|
||||
}
|
||||
,extend type Query {
|
||||
"""
|
||||
Returns a specific stream.
|
||||
"""
|
||||
stream( id: String! ): Stream
|
||||
|
||||
"""
|
||||
All the streams of the current user, pass in the `query` parameter to search by name, description or ID.
|
||||
"""
|
||||
streams( query: String, limit: Int = 25, cursor: String ): StreamCollection
|
||||
@hasScope(scope: "streams:read")
|
||||
}
|
||||
|
||||
type Stream {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
isPublic: Boolean!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
collaborators: [ StreamCollaborator ]!
|
||||
}
|
||||
|
||||
extend type User {
|
||||
"""
|
||||
All the streams that a user has access to.
|
||||
"""
|
||||
streams( limit: Int! = 25, cursor: String ): StreamCollection
|
||||
}
|
||||
|
||||
type StreamCollaborator {
|
||||
id: String!
|
||||
name: String!
|
||||
role: String!
|
||||
company: String
|
||||
avatar: String
|
||||
}
|
||||
|
||||
type StreamCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [ Stream ]
|
||||
}
|
||||
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Creates a new stream.
|
||||
"""
|
||||
streamCreate( stream: StreamCreateInput! ): String
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Updates an existing stream.
|
||||
"""
|
||||
streamUpdate( stream: StreamUpdateInput! ): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Deletes an existing stream.
|
||||
"""
|
||||
streamDelete( id: String! ): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Grants permissions to a user on a given stream.
|
||||
"""
|
||||
streamGrantPermission( permissionParams: StreamGrantPermissionInput! ): Boolean
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Revokes the permissions of a user on a given stream.
|
||||
"""
|
||||
streamRevokePermission( permissionParams: StreamRevokePermissionInput! ): Boolean
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
|
||||
#
|
||||
# User bound subscriptions that operate on the stream collection of an user
|
||||
# Example relevant view/usecase: updating the list of streams for a user.
|
||||
#
|
||||
|
||||
"""
|
||||
Subscribes to new stream added event for your profile. Use this to display an up-to-date list of streams.
|
||||
**NOTE**: If someone shares a stream with you, this subscription will be triggered with an extra value of `sharedBy` in the payload.
|
||||
"""
|
||||
userStreamAdded: JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "profile:read")
|
||||
|
||||
"""
|
||||
Subscribes to stream removed event for your profile. Use this to display an up-to-date list of streams for your profile.
|
||||
**NOTE**: If someone revokes your permissions on a stream, this subscription will be triggered with an extra value of `revokedBy` in the payload.
|
||||
"""
|
||||
userStreamRemoved: JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "profile:read")
|
||||
|
||||
#
|
||||
# Stream bound subscriptions that operate on the stream itself.
|
||||
# Example relevant view/usecase: a single stream connector, or view, or component in a web app
|
||||
#
|
||||
|
||||
"""
|
||||
Subscribes to stream updated event. Use this in clients/components that pertain only to this stream.
|
||||
"""
|
||||
streamUpdated( streamId: String ): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
"""
|
||||
Subscribes to stream deleted event. Use this in clients/components that pertain only to this stream.
|
||||
"""
|
||||
streamDeleted( streamId: String ): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
}
|
||||
|
||||
input StreamCreateInput {
|
||||
name: String
|
||||
description: String
|
||||
isPublic: Boolean
|
||||
}
|
||||
|
||||
input StreamUpdateInput {
|
||||
id: String!
|
||||
name: String
|
||||
description: String
|
||||
isPublic: Boolean
|
||||
}
|
||||
|
||||
input StreamGrantPermissionInput {
|
||||
streamId: String!,
|
||||
userId: String!,
|
||||
role: String!
|
||||
}
|
||||
|
||||
input StreamRevokePermissionInput {
|
||||
streamId: String!,
|
||||
userId: String!
|
||||
}
|
||||
,extend type Query {
|
||||
"""
|
||||
Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header).
|
||||
"""
|
||||
user(id: String): User
|
||||
userSearch(
|
||||
query: String!
|
||||
limit: Int! = 25
|
||||
cursor: String
|
||||
): UserSearchResultCollection
|
||||
userPwdStrength(pwd: String!): JSONObject
|
||||
}
|
||||
|
||||
"""
|
||||
Base user type.
|
||||
"""
|
||||
type User {
|
||||
id: String!
|
||||
suuid: String
|
||||
email: String
|
||||
name: String
|
||||
bio: String
|
||||
company: String
|
||||
avatar: String
|
||||
verified: Boolean
|
||||
profiles: JSONObject
|
||||
role: String
|
||||
}
|
||||
|
||||
type UserSearchResultCollection {
|
||||
cursor: String
|
||||
items: [UserSearchResult]
|
||||
}
|
||||
|
||||
type UserSearchResult {
|
||||
id: String!
|
||||
name: String
|
||||
bio: String
|
||||
company: String
|
||||
avatar: String
|
||||
verified: Boolean
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Edits a user's profile.
|
||||
"""
|
||||
userUpdate(user: UserUpdateInput!): Boolean!
|
||||
}
|
||||
|
||||
input UserUpdateInput {
|
||||
name: String
|
||||
company: String
|
||||
bio: String
|
||||
}
|
||||
@@ -110,7 +110,11 @@ class StreamWrapper:
|
||||
return self._account
|
||||
|
||||
self._account = next(
|
||||
(a for a in get_local_accounts() if self.host in a.serverInfo.url),
|
||||
(
|
||||
a
|
||||
for a in get_local_accounts()
|
||||
if self.host == urlparse(a.serverInfo.url).netloc
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Optional
|
||||
|
||||
|
||||
class SpeckleException(Exception):
|
||||
def __init__(self, message: str, exception: Exception = None) -> None:
|
||||
super().__init__()
|
||||
self.message = message
|
||||
self.exception = exception
|
||||
|
||||
@@ -10,18 +11,29 @@ class SpeckleException(Exception):
|
||||
return f"SpeckleException: {self.message}"
|
||||
|
||||
|
||||
class SpeckleInvalidUnitException(SpeckleException):
|
||||
def __init__(self, invalid_unit: Any) -> None:
|
||||
|
||||
super().__init__(
|
||||
message=f"Invalid units: expected type str but received {type(invalid_unit)} ({invalid_unit}).",
|
||||
exception=None,
|
||||
)
|
||||
|
||||
|
||||
class SerializationException(SpeckleException):
|
||||
def __init__(self, message: str, object: Any, exception: Exception = None) -> None:
|
||||
super().__init__(message=message)
|
||||
self.object = object
|
||||
self.unhandled_type = type(object)
|
||||
def __init__(self, message: str, obj: Any, exception: Exception = None) -> None:
|
||||
super().__init__(message=message, exception=exception)
|
||||
self.obj = obj
|
||||
self.unhandled_type = type(obj)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SpeckleException: Could not serialize object of type {self.unhandled_type}"
|
||||
|
||||
|
||||
class GraphQLException(SpeckleException):
|
||||
def __init__(self, message: str, errors: List, data=None) -> None:
|
||||
def __init__(
|
||||
self, message: str, errors: Optional[List[Any]] = None, data=None
|
||||
) -> None:
|
||||
super().__init__(message=message)
|
||||
self.errors = errors
|
||||
self.data = data
|
||||
@@ -30,6 +42,14 @@ class GraphQLException(SpeckleException):
|
||||
return f"GraphQLException: {self.message}"
|
||||
|
||||
|
||||
class UnsupportedException(SpeckleException):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message=message)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"UnsupportedException: {self.message}"
|
||||
|
||||
|
||||
class SpeckleWarning(Warning):
|
||||
def __init__(self, *args: object) -> None:
|
||||
super().__init__(*args)
|
||||
|
||||
@@ -26,6 +26,7 @@ RECEIVE = "Receive"
|
||||
SEND = "Send"
|
||||
STREAM = "Stream Action"
|
||||
PERMISSION = "Permission Action"
|
||||
INVITE = "Invite Action"
|
||||
COMMIT = "Commit Action"
|
||||
BRANCH = "Branch Action"
|
||||
USER = "User Action"
|
||||
@@ -78,7 +79,8 @@ def track(action: str, account: "Account" = None, custom_props: dict = None):
|
||||
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!
|
||||
LOG.error(f"Error queueing metrics request: {str(ex)}")
|
||||
LOG.debug(f"Error queueing metrics request: {str(ex)}")
|
||||
|
||||
|
||||
def initialise_tracker(account: "Account" = None):
|
||||
global METRICS_TRACKER
|
||||
@@ -141,6 +143,6 @@ class MetricsTracker(metaclass=Singleton):
|
||||
try:
|
||||
session.post(self.analytics_url, json=event_params)
|
||||
except Exception as ex:
|
||||
LOG.error(f"Error sending metrics request: {str(ex)}")
|
||||
LOG.debug(f"Error sending metrics request: {str(ex)}")
|
||||
|
||||
self.queue.task_done()
|
||||
self.queue.task_done()
|
||||
|
||||
+32
-20
@@ -14,8 +14,8 @@ import contextlib
|
||||
from enum import EnumMeta
|
||||
from warnings import warn
|
||||
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects.units import get_units_from_string
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
|
||||
from specklepy.objects.units import get_units_from_string, Units
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
|
||||
PRIMITIVES = (int, float, str, bool)
|
||||
@@ -92,12 +92,19 @@ class _RegisteringBase:
|
||||
speckle_type: ClassVar[str]
|
||||
_type_registry: ClassVar[Dict[str, "Base"]] = {}
|
||||
_attr_types: ClassVar[Dict[str, Type]] = {}
|
||||
# dict of chunkable props and their max chunk size
|
||||
_chunkable: Dict[str, int] = {}
|
||||
_chunk_size_default: int = 1000
|
||||
_detachable: Set[str] = set() # list of defined detachable props
|
||||
_serialize_ignore: Set[str] = set()
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
|
||||
@classmethod
|
||||
def get_registered_type(cls, speckle_type: str) -> Optional[Type["Base"]]:
|
||||
def get_registered_type(
|
||||
cls, speckle_type: str
|
||||
) -> Union["Base", Type["Base"], None]:
|
||||
"""Get the registered type from the protected mapping via the `speckle_type`"""
|
||||
return cls._type_registry.get(speckle_type, None)
|
||||
|
||||
@@ -139,15 +146,10 @@ class _RegisteringBase:
|
||||
|
||||
|
||||
class Base(_RegisteringBase):
|
||||
id: Optional[str] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[str] = None
|
||||
_units: str = None
|
||||
# dict of chunkable props and their max chunk size
|
||||
_chunkable: Dict[str, int] = {}
|
||||
_chunk_size_default: int = 1000
|
||||
_detachable: Set[str] = set() # list of defined detachable props
|
||||
_serialize_ignore: Set[str] = set()
|
||||
id: Union[str, None] = None
|
||||
totalChildrenCount: Union[int, None] = None
|
||||
applicationId: Union[str, None] = None
|
||||
_units: Union[Units, None] = None
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__()
|
||||
@@ -311,14 +313,23 @@ class Base(_RegisteringBase):
|
||||
self._detachable = self._detachable.union(names)
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
return self._units
|
||||
def units(self) -> Union[str, None]:
|
||||
if self._units:
|
||||
return self._units.value
|
||||
return None
|
||||
|
||||
@units.setter
|
||||
def units(self, value: str):
|
||||
units = get_units_from_string(value)
|
||||
if units:
|
||||
self._units = units
|
||||
def units(self, value: Union[str, Units, None]):
|
||||
if value == None:
|
||||
units = value
|
||||
elif isinstance(value, Units):
|
||||
units: Units = value
|
||||
else:
|
||||
units = get_units_from_string(value)
|
||||
self._units = units
|
||||
# except SpeckleInvalidUnitException as ex:
|
||||
# warn(f"Units are reset to None. Reason {ex.message}")
|
||||
# self._units = None
|
||||
|
||||
def get_member_names(self) -> List[str]:
|
||||
"""Get all of the property names on this object, dynamic or not"""
|
||||
@@ -331,7 +342,7 @@ class Base(_RegisteringBase):
|
||||
|
||||
def get_serializable_attributes(self) -> List[str]:
|
||||
"""Get the attributes that should be serialized"""
|
||||
return list(set(self.get_member_names()) - self._serialize_ignore)
|
||||
return sorted(list(set(self.get_member_names()) - self._serialize_ignore))
|
||||
|
||||
def get_typed_member_names(self) -> List[str]:
|
||||
"""Get all of the names of the defined (typed) properties of this object"""
|
||||
@@ -378,6 +389,7 @@ class Base(_RegisteringBase):
|
||||
)
|
||||
|
||||
def _handle_object_count(self, obj: Any, parsed: List) -> int:
|
||||
# pylint: disable=isinstance-second-argument-not-valid-type
|
||||
count = 0
|
||||
if obj is None:
|
||||
return count
|
||||
@@ -406,7 +418,7 @@ Base.update_forward_refs()
|
||||
|
||||
|
||||
class DataChunk(Base, speckle_type="Speckle.Core.Models.DataChunk"):
|
||||
data: List[Any] = None
|
||||
data: Union[List[Any], None] = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, List, Type
|
||||
from typing import Any, Callable, List, Type, Dict
|
||||
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects.base import Base
|
||||
@@ -43,8 +43,8 @@ def curve_from_list(args: List[float]):
|
||||
|
||||
|
||||
class ObjectArray:
|
||||
def __init__(self) -> None:
|
||||
self.data = []
|
||||
def __init__(self, data: list = None) -> None:
|
||||
self.data = data or []
|
||||
|
||||
@classmethod
|
||||
def from_objects(cls, objects: List[Base]) -> "ObjectArray":
|
||||
@@ -60,18 +60,17 @@ class ObjectArray:
|
||||
"All objects in chunk should have the same speckle_type. "
|
||||
f"Found {speckle_type} and {obj.speckle_type}"
|
||||
)
|
||||
data_list.encode_object(object=obj)
|
||||
data_list.encode_object(obj=obj)
|
||||
|
||||
return data_list
|
||||
|
||||
@staticmethod
|
||||
def decode_data(
|
||||
data: List[Any], decoder: Callable[[List[Any]], Base]
|
||||
data: List[Any], decoder: Callable[[List[Any]], Base], **kwargs: Dict[str, Any]
|
||||
) -> List[Base]:
|
||||
bases = []
|
||||
if not data:
|
||||
return bases
|
||||
|
||||
index = 0
|
||||
while index < len(data):
|
||||
item_length = int(data[index])
|
||||
@@ -79,19 +78,16 @@ class ObjectArray:
|
||||
item_end = item_start + item_length
|
||||
item_data = data[item_start:item_end]
|
||||
index = item_end
|
||||
# TODO: investigate what's going on w this fail
|
||||
try:
|
||||
decoded_data = decoder(item_data)
|
||||
bases.append(decoded_data)
|
||||
except ValueError:
|
||||
continue
|
||||
decoded_data = decoder(item_data, **kwargs)
|
||||
bases.append(decoded_data)
|
||||
|
||||
return bases
|
||||
|
||||
def decode(self, decoder: Callable[[List[Any]], Any]):
|
||||
return self.decode_data(data=self.data, decoder=decoder)
|
||||
def decode(self, decoder: Callable[[List[Any]], Any], **kwargs: Dict[str, Any]):
|
||||
return self.decode_data(data=self.data, decoder=decoder, **kwargs)
|
||||
|
||||
def encode_object(self, object: Base):
|
||||
encoded = object.to_list()
|
||||
def encode_object(self, obj: Base):
|
||||
encoded = obj.to_list()
|
||||
encoded.insert(0, len(encoded))
|
||||
self.data.extend(encoded)
|
||||
|
||||
@@ -128,8 +124,7 @@ class CurveArray(ObjectArray):
|
||||
|
||||
@classmethod
|
||||
def _curve_decoder(cls, data: List[float]) -> Base:
|
||||
crv_array = cls()
|
||||
crv_array.data = data
|
||||
crv_array = cls(data)
|
||||
return crv_array.to_curve()
|
||||
|
||||
def to_curves(self) -> List[Base]:
|
||||
|
||||
+284
-167
@@ -64,19 +64,21 @@ class Plane(Base, speckle_type=GEOMETRY + "Plane"):
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any]) -> "Plane":
|
||||
return cls(
|
||||
origin=Point.from_list(args[0:3]),
|
||||
origin=Point.from_list(args[:3]),
|
||||
normal=Vector.from_list(args[3:6]),
|
||||
xdir=Vector.from_list(args[6:9]),
|
||||
ydir=Vector.from_list(args[9:12]),
|
||||
units=get_units_from_encoding(args[-1]),
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.extend(self.origin.to_list())
|
||||
encoded.extend(self.normal.to_list())
|
||||
encoded.extend(self.xdir.to_list())
|
||||
encoded.extend(self.ydir.to_list())
|
||||
return encoded
|
||||
return [
|
||||
*self.origin.to_list(),
|
||||
*self.normal.to_list(),
|
||||
*self.xdir.to_list(),
|
||||
*self.ydir.to_list(),
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Box(Base, speckle_type=GEOMETRY + "Box"):
|
||||
@@ -98,17 +100,21 @@ class Line(Base, speckle_type=GEOMETRY + "Line"):
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any]) -> "Line":
|
||||
return cls(
|
||||
start=Point.from_list(args[0:3]),
|
||||
end=Point.from_list(args[3:6]),
|
||||
domain=Interval.from_list(args[6:9]),
|
||||
start=Point.from_list(args[1:4]),
|
||||
end=Point.from_list(args[4:7]),
|
||||
domain=Interval.from_list(args[7:10]),
|
||||
units=get_units_from_encoding(args[-1]),
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.extend(self.start.to_list())
|
||||
encoded.extend(self.end.to_list())
|
||||
encoded.extend(self.domain.to_list())
|
||||
return encoded
|
||||
domain = self.domain.to_list() if self.domain else [0, 1]
|
||||
return [
|
||||
CurveTypeEncoding.Line.value,
|
||||
*self.start.to_list(),
|
||||
*self.end.to_list(),
|
||||
*domain,
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Arc(Base, speckle_type=GEOMETRY + "Arc"):
|
||||
@@ -134,20 +140,26 @@ class Arc(Base, speckle_type=GEOMETRY + "Arc"):
|
||||
angleRadians=args[4],
|
||||
domain=Interval.from_list(args[5:7]),
|
||||
plane=Plane.from_list(args[7:20]),
|
||||
startPoint=Point.from_list(args[20:23]),
|
||||
midPoint=Point.from_list(args[23:26]),
|
||||
endPoint=Point.from_list(args[26:29]),
|
||||
units=get_units_from_encoding(args[-1]),
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Arc.value)
|
||||
encoded.append(self.radius)
|
||||
encoded.append(self.startAngle)
|
||||
encoded.append(self.endAngle)
|
||||
encoded.append(self.angleRadians)
|
||||
encoded.extend(self.domain.to_list())
|
||||
encoded.extend(self.plane.to_list())
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
CurveTypeEncoding.Arc.value,
|
||||
self.radius,
|
||||
self.startAngle,
|
||||
self.endAngle,
|
||||
self.angleRadians,
|
||||
*self.domain.to_list(),
|
||||
*self.plane.to_list(),
|
||||
*self.startPoint.to_list(),
|
||||
*self.midPoint.to_list(),
|
||||
*self.endPoint.to_list(),
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Circle(Base, speckle_type=GEOMETRY + "Circle"):
|
||||
@@ -168,13 +180,13 @@ class Circle(Base, speckle_type=GEOMETRY + "Circle"):
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Circle.value)
|
||||
encoded.append(self.radius),
|
||||
encoded.extend(self.domain.to_list())
|
||||
encoded.extend(self.plane.to_list())
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
CurveTypeEncoding.Circle.value,
|
||||
self.radius,
|
||||
*self.domain.to_list(),
|
||||
*self.plane.to_list(),
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Ellipse(Base, speckle_type=GEOMETRY + "Ellipse"):
|
||||
@@ -198,14 +210,14 @@ class Ellipse(Base, speckle_type=GEOMETRY + "Ellipse"):
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Ellipse.value)
|
||||
encoded.append(self.firstRadius)
|
||||
encoded.append(self.secondRadius)
|
||||
encoded.extend(self.domain.to_list())
|
||||
encoded.extend(self.plane.to_list())
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
CurveTypeEncoding.Ellipse.value,
|
||||
self.firstRadius,
|
||||
self.secondRadius,
|
||||
*self.domain.to_list(),
|
||||
*self.plane.to_list(),
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Polyline(Base, speckle_type=GEOMETRY + "Polyline", chunkable={"value": 20000}):
|
||||
@@ -237,14 +249,14 @@ class Polyline(Base, speckle_type=GEOMETRY + "Polyline", chunkable={"value": 200
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Polyline.value)
|
||||
encoded.append(int(self.closed))
|
||||
encoded.extend(self.domain.to_list())
|
||||
encoded.append(len(self.value))
|
||||
encoded.extend(self.value)
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
CurveTypeEncoding.Polyline.value,
|
||||
int(self.closed),
|
||||
*self.domain.to_list(),
|
||||
len(self.value),
|
||||
*self.value,
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
def as_points(self) -> List[Point]:
|
||||
"""Converts the `value` attribute to a list of Points"""
|
||||
@@ -315,21 +327,21 @@ class Curve(
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Curve.value)
|
||||
encoded.append(self.degree)
|
||||
encoded.append(int(self.periodic))
|
||||
encoded.append(int(self.rational))
|
||||
encoded.append(int(self.closed))
|
||||
encoded.extend(self.domain.to_list())
|
||||
encoded.append(len(self.points))
|
||||
encoded.append(len(self.weights))
|
||||
encoded.append(len(self.knots))
|
||||
encoded.extend(self.points)
|
||||
encoded.extend(self.weights)
|
||||
encoded.extend(self.knots)
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
CurveTypeEncoding.Curve.value,
|
||||
self.degree,
|
||||
int(self.periodic),
|
||||
int(self.rational),
|
||||
int(self.closed),
|
||||
*self.domain.to_list(),
|
||||
len(self.points),
|
||||
len(self.weights),
|
||||
len(self.knots),
|
||||
*self.points,
|
||||
*self.weights,
|
||||
*self.knots,
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Polycurve(Base, speckle_type=GEOMETRY + "Polycurve"):
|
||||
@@ -342,8 +354,7 @@ class Polycurve(Base, speckle_type=GEOMETRY + "Polycurve"):
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any]) -> "Polycurve":
|
||||
curve_arrays = CurveArray()
|
||||
curve_arrays.data = args[4:-1]
|
||||
curve_arrays = CurveArray(args[5:-1])
|
||||
return cls(
|
||||
closed=bool(args[1]),
|
||||
domain=Interval.from_list(args[2:4]),
|
||||
@@ -352,14 +363,15 @@ class Polycurve(Base, speckle_type=GEOMETRY + "Polycurve"):
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(CurveTypeEncoding.Polycurve.value)
|
||||
encoded.append(int(self.closed))
|
||||
encoded.extend(self.domain.to_list())
|
||||
curve_array = CurveArray.from_curves(self.segments)
|
||||
encoded.extend(curve_array.data)
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
curve_array = CurveArray.from_curves(self.segments).data
|
||||
return [
|
||||
CurveTypeEncoding.Polycurve.value,
|
||||
int(self.closed),
|
||||
*self.domain.to_list(),
|
||||
len(curve_array),
|
||||
*curve_array,
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class Extrusion(Base, speckle_type=GEOMETRY + "Extrusion"):
|
||||
@@ -460,46 +472,65 @@ class Surface(Base, speckle_type=GEOMETRY + "Surface"):
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(self.degreeU)
|
||||
encoded.append(self.degreeV)
|
||||
encoded.append(self.countU)
|
||||
encoded.append(self.countV)
|
||||
encoded.append(int(self.rational))
|
||||
encoded.append(int(self.closedU))
|
||||
encoded.append(int(self.closedV))
|
||||
encoded.extend(self.domainU.to_list())
|
||||
encoded.extend(self.domainV.to_list())
|
||||
encoded.append(len(self.pointData))
|
||||
encoded.append(len(self.knotsU))
|
||||
encoded.append(len(self.knotsV))
|
||||
encoded.extend(self.pointData)
|
||||
encoded.extend(self.knotsU)
|
||||
encoded.extend(self.knotsV)
|
||||
encoded.append(get_encoding_from_units(self.units))
|
||||
return encoded
|
||||
return [
|
||||
self.degreeU,
|
||||
self.degreeV,
|
||||
self.countU,
|
||||
self.countV,
|
||||
int(self.rational),
|
||||
int(self.closedU),
|
||||
int(self.closedV),
|
||||
*self.domainU.to_list(),
|
||||
*self.domainV.to_list(),
|
||||
len(self.pointData),
|
||||
len(self.knotsU),
|
||||
len(self.knotsV),
|
||||
*self.pointData,
|
||||
*self.knotsU,
|
||||
*self.knotsV,
|
||||
get_encoding_from_units(self._units),
|
||||
]
|
||||
|
||||
|
||||
class BrepFace(Base, speckle_type=GEOMETRY + "BrepFace"):
|
||||
_Brep: "Brep" = None
|
||||
SurfaceIndex: int = None
|
||||
LoopIndices: List[int] = None
|
||||
OuterLoopIndex: int = None
|
||||
OrientationReversed: bool = None
|
||||
LoopIndices: List[int] = None
|
||||
|
||||
@property
|
||||
def _outer_loop(self):
|
||||
return self._Brep.Loops[self.OuterLoopIndex]
|
||||
return self._Brep.Loops[self.OuterLoopIndex] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def _surface(self):
|
||||
return self._Brep.Surfaces[self.SurfaceIndex]
|
||||
return self._Brep.Surfaces[self.SurfaceIndex] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def _loops(self):
|
||||
if self.LoopIndices:
|
||||
# pylint: disable=not-an-iterable, no-member
|
||||
return [self._Brep.Loops[i] for i in self.LoopIndices]
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any], brep: "Brep" = None) -> "BrepFace":
|
||||
return cls(
|
||||
_Brep=brep,
|
||||
SurfaceIndex=args[0],
|
||||
OuterLoopIndex=args[1],
|
||||
OrientationReversed=bool(args[2]),
|
||||
LoopIndices=args[3:],
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
return [
|
||||
self.SurfaceIndex,
|
||||
self.OuterLoopIndex,
|
||||
int(self.OrientationReversed),
|
||||
*self.LoopIndices,
|
||||
]
|
||||
|
||||
|
||||
class BrepEdge(Base, speckle_type=GEOMETRY + "BrepEdge"):
|
||||
_Brep: "Brep" = None
|
||||
@@ -521,18 +552,58 @@ class BrepEdge(Base, speckle_type=GEOMETRY + "BrepEdge"):
|
||||
@property
|
||||
def _trims(self):
|
||||
if self.TrimIndices:
|
||||
# pylint: disable=not-an-iterable
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
@property
|
||||
def _curve(self):
|
||||
return self._Brep.Curve3D[self.Curve3dIndex]
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any], brep: "Brep" = None) -> "BrepEdge":
|
||||
domain_start = args[4]
|
||||
domain_end = args[5]
|
||||
domain = (
|
||||
Interval(start=domain_start, end=domain_end)
|
||||
if None not in (domain_start, domain_end)
|
||||
else None
|
||||
)
|
||||
return cls(
|
||||
_Brep=brep,
|
||||
Curve3dIndex=int(args[0]),
|
||||
TrimIndices=[int(t) for t in args[6:]],
|
||||
StartIndex=int(args[1]),
|
||||
EndIndex=int(args[2]),
|
||||
ProxyCurveIsReversed=bool(args[3]),
|
||||
Domain=domain,
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
return [
|
||||
self.Curve3dIndex,
|
||||
self.StartIndex,
|
||||
self.EndIndex,
|
||||
int(self.ProxyCurveIsReversed),
|
||||
self.Domain.start,
|
||||
self.Domain.end,
|
||||
*self.TrimIndices,
|
||||
]
|
||||
|
||||
|
||||
class BrepLoopType(int, Enum):
|
||||
Unknown = 0
|
||||
Outer = 1
|
||||
Inner = 2
|
||||
Slit = 3
|
||||
CurveOnSurface = 4
|
||||
PointOnSurface = 5
|
||||
|
||||
|
||||
class BrepLoop(Base, speckle_type=GEOMETRY + "BrepLoop"):
|
||||
_Brep: "Brep" = None
|
||||
FaceIndex: int = None
|
||||
TrimIndices: List[int] = None
|
||||
Type: str = None
|
||||
Type: BrepLoopType = None
|
||||
|
||||
@property
|
||||
def _face(self):
|
||||
@@ -541,10 +612,27 @@ class BrepLoop(Base, speckle_type=GEOMETRY + "BrepLoop"):
|
||||
@property
|
||||
def _trims(self):
|
||||
if self.TrimIndices:
|
||||
# pylint: disable=not-an-iterable
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, args: List[any], brep: "Brep" = None):
|
||||
return cls(
|
||||
_Brep=brep,
|
||||
FaceIndex=args[0],
|
||||
Type=BrepLoopType(args[1]),
|
||||
TrimIndices=args[2:],
|
||||
)
|
||||
|
||||
class BrepTrimTypeEnum(int, Enum):
|
||||
def to_list(self) -> List[int]:
|
||||
return [
|
||||
self.FaceIndex,
|
||||
self.Type.value,
|
||||
*self.TrimIndices,
|
||||
]
|
||||
|
||||
|
||||
class BrepTrimType(int, Enum):
|
||||
Unknown = 0
|
||||
Boundary = 1
|
||||
Mated = 2
|
||||
@@ -564,29 +652,35 @@ class BrepTrim(Base, speckle_type=GEOMETRY + "BrepTrim"):
|
||||
LoopIndex: int = None
|
||||
CurveIndex: int = None
|
||||
IsoStatus: int = None
|
||||
TrimType: str = None
|
||||
TrimType: BrepTrimType = None
|
||||
IsReversed: bool = None
|
||||
Domain: Interval = None
|
||||
|
||||
@property
|
||||
def _face(self):
|
||||
return self._Brep.Faces[self.FaceIndex]
|
||||
if self._Brep:
|
||||
return self._Brep.Faces[self.FaceIndex] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def _loop(self):
|
||||
return self._Brep.Loops[self.LoopIndex]
|
||||
if self._Brep:
|
||||
return self._Brep.Loops[self.LoopIndex] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def _edge(self):
|
||||
return self._Brep.Edges[self.EdgeIndex] if self.EdgeIndex != -1 else None
|
||||
if self._Brep:
|
||||
# pylint: disable=no-member
|
||||
return self._Brep.Edges[self.EdgeIndex] if self.EdgeIndex != -1 else None
|
||||
|
||||
@property
|
||||
def _curve_2d(self):
|
||||
return self._Brep.Curve2D[self.CurveIndex]
|
||||
if self._Brep:
|
||||
return self._Brep.Curve2D[self.CurveIndex] # pylint: disable=no-member
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, args: List[Any]) -> "BrepTrim":
|
||||
def from_list(cls, args: List[Any], brep: "Brep" = None) -> "BrepTrim":
|
||||
return cls(
|
||||
_Brep=brep,
|
||||
EdgeIndex=args[0],
|
||||
StartIndex=args[1],
|
||||
EndIndex=args[2],
|
||||
@@ -594,39 +688,48 @@ class BrepTrim(Base, speckle_type=GEOMETRY + "BrepTrim"):
|
||||
LoopIndex=args[4],
|
||||
CurveIndex=args[5],
|
||||
IsoStatus=args[6],
|
||||
TrimType=BrepTrimTypeEnum(args[7]).name,
|
||||
TrimType=BrepTrimType(args[7]),
|
||||
IsReversed=bool(args[8]),
|
||||
)
|
||||
|
||||
def to_list(self) -> List[Any]:
|
||||
encoded = []
|
||||
encoded.append(self.EdgeIndex)
|
||||
encoded.append(self.StartIndex)
|
||||
encoded.append(self.EndIndex)
|
||||
encoded.append(self.FaceIndex)
|
||||
encoded.append(self.LoopIndex)
|
||||
encoded.append(self.CurveIndex)
|
||||
encoded.append(self.IsoStatus)
|
||||
encoded.append(getattr(BrepTrimTypeEnum, self.TrimType).value)
|
||||
encoded.append(self.IsReversed)
|
||||
return encoded
|
||||
return [
|
||||
self.EdgeIndex,
|
||||
self.StartIndex,
|
||||
self.EndIndex,
|
||||
self.FaceIndex,
|
||||
self.LoopIndex,
|
||||
self.CurveIndex,
|
||||
self.IsoStatus,
|
||||
self.TrimType.value,
|
||||
int(self.IsReversed),
|
||||
]
|
||||
|
||||
|
||||
class Brep(
|
||||
Base,
|
||||
speckle_type=GEOMETRY + "Brep",
|
||||
chunkable={
|
||||
"SurfacesValue": 200,
|
||||
"Curve3DValues": 200,
|
||||
"Curve2DValues": 200,
|
||||
"VerticesValue": 5000,
|
||||
"Edges": 5000,
|
||||
"Loops": 5000,
|
||||
"TrimsValue": 5000,
|
||||
"Faces": 5000,
|
||||
"SurfacesValue": 31250,
|
||||
"Curve3DValues": 31250,
|
||||
"Curve2DValues": 31250,
|
||||
"VerticesValue": 31250,
|
||||
"EdgesValue": 62500,
|
||||
"LoopsValue": 62500,
|
||||
"FacesValue": 62500,
|
||||
"TrimsValue": 62500,
|
||||
},
|
||||
detachable={"displayValue"},
|
||||
serialize_ignore={"Surfaces", "Curve3D", "Curve2D", "Vertices", "Trims"},
|
||||
serialize_ignore={
|
||||
"Surfaces",
|
||||
"Curve3D",
|
||||
"Curve2D",
|
||||
"Vertices",
|
||||
"Trims",
|
||||
"Edges",
|
||||
"Loops",
|
||||
"Faces",
|
||||
},
|
||||
):
|
||||
provenance: str = None
|
||||
bbox: Box = None
|
||||
@@ -637,6 +740,10 @@ class Brep(
|
||||
Curve3D: List[Base] = None
|
||||
Curve2D: List[Base] = None
|
||||
Vertices: List[Point] = None
|
||||
Edges: List[BrepEdge] = None
|
||||
Loops: List[BrepLoop] = None
|
||||
Faces: List[BrepFace] = None
|
||||
Trims: List[BrepTrim] = None
|
||||
IsClosed: bool = None
|
||||
Orientation: int = None
|
||||
|
||||
@@ -645,7 +752,7 @@ class Brep(
|
||||
return children
|
||||
|
||||
for child in children:
|
||||
child._Brep = self
|
||||
child._Brep = self # pylint: disable=protected-access
|
||||
return children
|
||||
|
||||
# set as prop for now for backwards compatibility
|
||||
@@ -661,68 +768,80 @@ class Brep(
|
||||
self._displayValue = value
|
||||
|
||||
@property
|
||||
def Edges(self) -> List[BrepEdge]:
|
||||
return self._inject_self_into_children(self._Edges)
|
||||
def EdgesValue(self) -> List[BrepEdge]:
|
||||
return None if self.Edges is None else ObjectArray.from_objects(self.Edges).data
|
||||
|
||||
@Edges.setter
|
||||
def Edges(self, value: List[BrepEdge]):
|
||||
self._Edges = value
|
||||
@EdgesValue.setter
|
||||
def EdgesValue(self, value: List[float]):
|
||||
if not value:
|
||||
return
|
||||
|
||||
self.Edges = ObjectArray.decode_data(value, BrepEdge.from_list, brep=self)
|
||||
|
||||
@property
|
||||
def Loops(self) -> List[BrepLoop]:
|
||||
return self._inject_self_into_children(self._Loops)
|
||||
def LoopsValue(self) -> List[BrepLoop]:
|
||||
return None if self.Loops is None else ObjectArray.from_objects(self.Loops).data
|
||||
|
||||
@Loops.setter
|
||||
def Loops(self, value: List[BrepLoop]):
|
||||
self._Loops = value
|
||||
@LoopsValue.setter
|
||||
def LoopsValue(self, value: List[int]):
|
||||
if not value:
|
||||
return
|
||||
|
||||
self.Loops = ObjectArray.decode_data(value, BrepLoop.from_list, brep=self)
|
||||
|
||||
@property
|
||||
def Faces(self) -> List[BrepFace]:
|
||||
return self._inject_self_into_children(self._Faces)
|
||||
def FacesValue(self) -> List[int]:
|
||||
return None if self.Faces is None else ObjectArray.from_objects(self.Faces).data
|
||||
|
||||
@Faces.setter
|
||||
def Faces(self, value: List[BrepFace]):
|
||||
self._Faces = value
|
||||
@FacesValue.setter
|
||||
def FacesValue(self, value: List[int]):
|
||||
if not value:
|
||||
return
|
||||
|
||||
self.Faces = ObjectArray.decode_data(value, BrepFace.from_list, brep=self)
|
||||
|
||||
@property
|
||||
def SurfacesValue(self) -> List[float]:
|
||||
if self.Surfaces is None:
|
||||
return None
|
||||
return ObjectArray.from_objects(self.Surfaces).data
|
||||
return (
|
||||
None
|
||||
if self.Surfaces is None
|
||||
else ObjectArray.from_objects(self.Surfaces).data
|
||||
)
|
||||
|
||||
@SurfacesValue.setter
|
||||
def SurfacesValue(self, value: List[float]):
|
||||
if not value:
|
||||
return
|
||||
|
||||
self.Surfaces = ObjectArray.decode_data(value, Surface.from_list)
|
||||
|
||||
@property
|
||||
def Curve3DValues(self) -> List[float]:
|
||||
if self.Curve3D is None:
|
||||
return None
|
||||
return CurveArray.from_curves(self.Curve3D).data
|
||||
return (
|
||||
None if self.Curve3D is None else CurveArray.from_curves(self.Curve3D).data
|
||||
)
|
||||
|
||||
@Curve3DValues.setter
|
||||
def Curve3DValues(self, value: List[float]):
|
||||
crv_array = CurveArray()
|
||||
crv_array.data = value
|
||||
crv_array = CurveArray(value)
|
||||
self.Curve3D = crv_array.to_curves()
|
||||
|
||||
@property
|
||||
def Curve2DValues(self) -> List[Base]:
|
||||
if self.Curve2D is None:
|
||||
return None
|
||||
return CurveArray.from_curves(self.Curve2D).data
|
||||
return (
|
||||
None if self.Curve2D is None else CurveArray.from_curves(self.Curve2D).data
|
||||
)
|
||||
|
||||
@Curve2DValues.setter
|
||||
def Curve2DValues(self, value: List[float]):
|
||||
crv_array = CurveArray()
|
||||
crv_array.data = value
|
||||
crv_array = CurveArray(value)
|
||||
self.Curve2D = crv_array.to_curves()
|
||||
|
||||
@property
|
||||
def VerticesValue(self) -> List[Point]:
|
||||
if self.Vertices is None:
|
||||
return None
|
||||
encoded_unit = get_encoding_from_units(self.Vertices[0].units)
|
||||
encoded_unit = get_encoding_from_units(self.Vertices[0]._units)
|
||||
values = [encoded_unit]
|
||||
for vertex in self.Vertices:
|
||||
values.extend(vertex.to_list())
|
||||
@@ -742,27 +861,25 @@ class Brep(
|
||||
|
||||
self.Vertices = vertices
|
||||
|
||||
@property
|
||||
def Trims(self) -> List[BrepTrim]:
|
||||
return self._inject_self_into_children(self._Trims)
|
||||
|
||||
@Trims.setter
|
||||
def Trims(self, value: List[BrepTrim]):
|
||||
self._Trims = value
|
||||
|
||||
# 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]:
|
||||
if self.Trims is None:
|
||||
return None
|
||||
values = []
|
||||
# return None if self.Trims is None else ObjectArray.from_objects(self.Trims).data
|
||||
if not self.Trims:
|
||||
return
|
||||
value = []
|
||||
for trim in self.Trims:
|
||||
values.extend(trim.to_list())
|
||||
return values
|
||||
value.extend(trim.to_list())
|
||||
return value
|
||||
|
||||
@TrimsValue.setter
|
||||
def TrimsValue(self, value: List[float]):
|
||||
if not value:
|
||||
return
|
||||
|
||||
# self.Trims = ObjectArray.decode_data(value, BrepTrim.from_list, brep=self)
|
||||
self.Trims = [
|
||||
BrepTrim.from_list(value[i : i + 9]) for i in range(0, len(value), 9)
|
||||
BrepTrim.from_list(value[i : i + 9], self) for i in range(0, len(value), 9)
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -5,4 +5,4 @@ from ..geometry import Plane
|
||||
class Axis(Base, speckle_type="Objects.Structural.Geometry.Axis"):
|
||||
name: str = None
|
||||
axisType: str = None
|
||||
plane: Plane = None
|
||||
plane: Plane = None
|
||||
|
||||
@@ -42,8 +42,8 @@ class Concrete(Material, speckle_type=STRUCTURAL_MATERIALS + ".Concrete"):
|
||||
compressiveStrength: float = 0.0
|
||||
tensileStrength: float = 0.0
|
||||
flexuralStrength: float = 0.0
|
||||
maxCompressiveStrength: float = 0.0
|
||||
maxTensileStrength: float = 0.0
|
||||
maxCompressiveStrain: float = 0.0
|
||||
maxTensileStrain: float = 0.0
|
||||
maxAggregateSize: float = 0.0
|
||||
lightweight: bool = None
|
||||
|
||||
|
||||
+40
-33
@@ -1,49 +1,54 @@
|
||||
from warnings import warn
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
|
||||
from typing import Union
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Units(Enum):
|
||||
mm = "mm"
|
||||
cm = "cm"
|
||||
m = "m"
|
||||
km = "km"
|
||||
inches = "in"
|
||||
feet = "ft"
|
||||
yards = "yd"
|
||||
miles = "mi"
|
||||
none = "none"
|
||||
|
||||
UNITS = ["mm", "cm", "m", "in", "ft", "yd", "mi"]
|
||||
|
||||
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"],
|
||||
"mi": ["mi", "mile", "miles"],
|
||||
"none": ["none", "null"],
|
||||
Units.mm: ["mm", "mil", "millimeters", "millimetres"],
|
||||
Units.cm: ["cm", "centimetre", "centimeter", "centimetres", "centimeters"],
|
||||
Units.m: ["m", "meter", "meters", "metre", "metres"],
|
||||
Units.km: ["km", "kilometer", "kilometre", "kilometers", "kilometres"],
|
||||
Units.inches: ["in", "inch", "inches"],
|
||||
Units.feet: ["ft", "foot", "feet"],
|
||||
Units.yards: ["yd", "yard", "yards"],
|
||||
Units.miles: ["mi", "mile", "miles"],
|
||||
Units.none: ["none", "null"],
|
||||
}
|
||||
|
||||
UNITS_ENCODINGS = {
|
||||
Units.none: 0,
|
||||
None: 0,
|
||||
"none": 0,
|
||||
"mm": 1,
|
||||
"cm": 2,
|
||||
"m": 3,
|
||||
"km": 4,
|
||||
"in": 5,
|
||||
"ft": 6,
|
||||
"yd": 7,
|
||||
"mi": 8,
|
||||
Units.mm: 1,
|
||||
Units.cm: 2,
|
||||
Units.m: 3,
|
||||
Units.km: 4,
|
||||
Units.inches: 5,
|
||||
Units.feet: 6,
|
||||
Units.yards: 7,
|
||||
Units.miles: 8,
|
||||
}
|
||||
|
||||
|
||||
def get_units_from_string(unit: str):
|
||||
def get_units_from_string(unit: str) -> Units:
|
||||
if not isinstance(unit, str):
|
||||
warn(
|
||||
f"Invalid units: expected type str but received {type(unit)} ({unit}). Skipping - no units will be set.",
|
||||
SpeckleWarning,
|
||||
)
|
||||
return
|
||||
raise SpeckleInvalidUnitException(unit)
|
||||
unit = str.lower(unit)
|
||||
for name, alternates in UNITS_STRINGS.items():
|
||||
if unit in alternates:
|
||||
return name
|
||||
|
||||
raise SpeckleException(
|
||||
message=f"Could not understand what unit {unit} is referring to. Please enter a valid unit (eg {UNITS})."
|
||||
)
|
||||
raise SpeckleInvalidUnitException(unit)
|
||||
|
||||
|
||||
def get_units_from_encoding(unit: int):
|
||||
@@ -56,8 +61,10 @@ def get_units_from_encoding(unit: int):
|
||||
)
|
||||
|
||||
|
||||
def get_encoding_from_units(unit: str):
|
||||
def get_encoding_from_units(unit: Union[Units, None]):
|
||||
try:
|
||||
return UNITS_ENCODINGS[unit]
|
||||
except KeyError as e:
|
||||
raise SpeckleException(message=f"No encoding exists for unit {unit}. Please enter a valid unit to encode (eg {UNITS_ENCODINGS}).") from e
|
||||
raise SpeckleException(
|
||||
message=f"No encoding exists for unit {unit}. Please enter a valid unit to encode (eg {UNITS_ENCODINGS})."
|
||||
) from e
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from appdirs import user_data_dir
|
||||
|
||||
|
||||
def base_path(app_name) -> Path:
|
||||
# from appdirs https://github.com/ActiveState/appdirs/blob/master/appdirs.py
|
||||
# default mac path is not the one we use (we use unix path), so using special case for this
|
||||
system = sys.platform
|
||||
if system.startswith("java"):
|
||||
import platform
|
||||
|
||||
os_name = platform.java_ver()[3][0]
|
||||
if os_name.startswith("Mac"):
|
||||
system = "darwin"
|
||||
|
||||
if system == "darwin":
|
||||
return Path(Path.home(), ".config", app_name)
|
||||
|
||||
return Path(user_data_dir(appname=app_name, appauthor=False, roaming=True))
|
||||
|
||||
|
||||
def accounts_path(app_name: str = "Speckle") -> Path:
|
||||
"""
|
||||
Gets the path where the Speckle applications are looking for accounts.
|
||||
"""
|
||||
return base_path(app_name).joinpath("Accounts")
|
||||
@@ -40,16 +40,24 @@ def safe_json_loads(obj: str, obj_id=None) -> Any:
|
||||
class BaseObjectSerializer:
|
||||
read_transport: AbstractTransport
|
||||
write_transports: List[AbstractTransport]
|
||||
detach_lineage: List[bool] = [] # tracks depth and whether or not to detach
|
||||
lineage: List[str] = [] # keeps track of hash chain through the object tree
|
||||
family_tree: Dict[str, Dict[str, int]] = {}
|
||||
closure_table: Dict[str, Dict[str, int]] = {}
|
||||
detach_lineage: List[bool] # tracks depth and whether or not to detach
|
||||
lineage: List[str] # keeps track of hash chain through the object tree
|
||||
family_tree: Dict[str, Dict[str, int]]
|
||||
closure_table: Dict[str, Dict[str, int]]
|
||||
deserialized: Dict[
|
||||
str, Base
|
||||
] # holds deserialized objects so objects with same id return the same instance
|
||||
|
||||
def __init__(
|
||||
self, write_transports: List[AbstractTransport] = [], read_transport=None
|
||||
self, write_transports: List[AbstractTransport] = None, read_transport=None
|
||||
) -> None:
|
||||
self.write_transports = write_transports
|
||||
self.write_transports = write_transports or []
|
||||
self.read_transport = read_transport
|
||||
self.detach_lineage = []
|
||||
self.lineage = []
|
||||
self.family_tree = {}
|
||||
self.closure_table = {}
|
||||
self.deserialized = {}
|
||||
|
||||
def write_json(self, base: Base):
|
||||
"""Serializes a given base object into a json string
|
||||
@@ -57,12 +65,12 @@ class BaseObjectSerializer:
|
||||
base {Base} -- the base object to be decomposed and serialized
|
||||
|
||||
Returns:
|
||||
(str, str) -- a tuple containing the hash (id) of the base object and the serialized object string
|
||||
(str, str) -- a tuple containing the object id of the base object and the serialized object string
|
||||
"""
|
||||
|
||||
hash, obj = self.traverse_base(base)
|
||||
obj_id, obj = self.traverse_base(base)
|
||||
|
||||
return hash, ujson.dumps(obj)
|
||||
return obj_id, ujson.dumps(obj)
|
||||
|
||||
def traverse_base(self, base: Base) -> Tuple[str, Dict]:
|
||||
"""Decomposes the given base object and builds a serializable dictionary
|
||||
@@ -71,7 +79,7 @@ class BaseObjectSerializer:
|
||||
base {Base} -- the base object to be decomposed and serialized
|
||||
|
||||
Returns:
|
||||
(str, dict) -- a tuple containing the hash (id) of the base object and the constructed serializable dictionary
|
||||
(str, dict) -- a tuple containing the object id of the base object and the constructed serializable dictionary
|
||||
"""
|
||||
self.__reset_writer()
|
||||
|
||||
@@ -79,13 +87,13 @@ class BaseObjectSerializer:
|
||||
for wt in self.write_transports:
|
||||
wt.begin_write()
|
||||
|
||||
hash, obj = self._traverse_base(base)
|
||||
obj_id, obj = self._traverse_base(base)
|
||||
|
||||
if self.write_transports:
|
||||
for wt in self.write_transports:
|
||||
wt.end_write()
|
||||
|
||||
return hash, obj
|
||||
return obj_id, obj
|
||||
|
||||
def _traverse_base(self, base: Base) -> Tuple[str, Dict]:
|
||||
if not self.detach_lineage:
|
||||
@@ -110,11 +118,6 @@ class BaseObjectSerializer:
|
||||
if prop == "id":
|
||||
continue
|
||||
|
||||
# allow serialisation of nulls
|
||||
if value is None:
|
||||
object_builder[prop] = value
|
||||
continue
|
||||
|
||||
# only bother with chunking and detaching if there is a write transport
|
||||
if self.write_transports:
|
||||
dynamic_chunk_match = prop.startswith("@") and re.match(
|
||||
@@ -131,8 +134,8 @@ class BaseObjectSerializer:
|
||||
prop.startswith("@") or prop in base._detachable or chunkable
|
||||
)
|
||||
|
||||
# 1. handle primitives (ints, floats, strings, and bools)
|
||||
if isinstance(value, PRIMITIVES):
|
||||
# 1. handle None and primitives (ints, floats, strings, and bools)
|
||||
if value is None or isinstance(value, PRIMITIVES):
|
||||
object_builder[prop] = value
|
||||
continue
|
||||
|
||||
@@ -145,8 +148,8 @@ class BaseObjectSerializer:
|
||||
elif isinstance(value, Base):
|
||||
child_obj = self.traverse_value(value, detach=detach)
|
||||
if detach and self.write_transports:
|
||||
ref_hash = child_obj["id"]
|
||||
object_builder[prop] = self.detach_helper(ref_hash=ref_hash)
|
||||
ref_id = child_obj["id"]
|
||||
object_builder[prop] = self.detach_helper(ref_id=ref_id)
|
||||
else:
|
||||
object_builder[prop] = child_obj
|
||||
|
||||
@@ -165,8 +168,8 @@ class BaseObjectSerializer:
|
||||
chunk_refs = []
|
||||
for c in chunks:
|
||||
self.detach_lineage.append(detach)
|
||||
ref_hash, _ = self._traverse_base(c)
|
||||
ref_obj = self.detach_helper(ref_hash=ref_hash)
|
||||
ref_id, _ = self._traverse_base(c)
|
||||
ref_obj = self.detach_helper(ref_id=ref_id)
|
||||
chunk_refs.append(ref_obj)
|
||||
object_builder[prop] = chunk_refs
|
||||
|
||||
@@ -185,20 +188,20 @@ class BaseObjectSerializer:
|
||||
}
|
||||
object_builder["totalChildrenCount"] = len(closure)
|
||||
|
||||
hash = hash_obj(object_builder)
|
||||
obj_id = hash_obj(object_builder)
|
||||
|
||||
object_builder["id"] = hash
|
||||
object_builder["id"] = obj_id
|
||||
if closure:
|
||||
object_builder["__closure"] = self.closure_table[hash] = closure
|
||||
object_builder["__closure"] = self.closure_table[obj_id] = closure
|
||||
|
||||
# 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=ujson.dumps(object_builder))
|
||||
t.save_object(id=obj_id, serialized_object=ujson.dumps(object_builder))
|
||||
|
||||
del self.lineage[-1]
|
||||
|
||||
return hash, object_builder
|
||||
return obj_id, object_builder
|
||||
|
||||
def traverse_value(self, obj: Any, detach: bool = False) -> Any:
|
||||
"""Decomposes a given object and constructs a serializable object or dictionary
|
||||
@@ -224,8 +227,8 @@ class BaseObjectSerializer:
|
||||
for o in obj:
|
||||
if isinstance(o, Base):
|
||||
self.detach_lineage.append(detach)
|
||||
hash, _ = self._traverse_base(o)
|
||||
detached_list.append(self.detach_helper(ref_hash=hash))
|
||||
ref_id, _ = self._traverse_base(o)
|
||||
detached_list.append(self.detach_helper(ref_id=ref_id))
|
||||
else:
|
||||
detached_list.append(self.traverse_value(o, detach))
|
||||
return detached_list
|
||||
@@ -254,11 +257,11 @@ class BaseObjectSerializer:
|
||||
|
||||
return str(obj)
|
||||
|
||||
def detach_helper(self, ref_hash: str) -> Dict[str, str]:
|
||||
def detach_helper(self, ref_id: str) -> Dict[str, str]:
|
||||
"""Helper to keep track of detached objects and their depth in the family tree and create reference objects to place in the parent object
|
||||
|
||||
Arguments:
|
||||
ref_hash {str} -- the hash of the fully traversed object
|
||||
ref_id {str} -- the id of the fully traversed object
|
||||
|
||||
Returns:
|
||||
dict -- a reference object to be inserted into the given object's parent
|
||||
@@ -267,13 +270,13 @@ class BaseObjectSerializer:
|
||||
for parent in self.lineage:
|
||||
if parent not in self.family_tree:
|
||||
self.family_tree[parent] = {}
|
||||
if ref_hash not in self.family_tree[parent] or self.family_tree[parent][
|
||||
ref_hash
|
||||
if ref_id not in self.family_tree[parent] or self.family_tree[parent][
|
||||
ref_id
|
||||
] > len(self.detach_lineage):
|
||||
self.family_tree[parent][ref_hash] = len(self.detach_lineage)
|
||||
self.family_tree[parent][ref_id] = len(self.detach_lineage)
|
||||
|
||||
return {
|
||||
"referencedId": ref_hash,
|
||||
"referencedId": ref_id,
|
||||
"speckle_type": "reference",
|
||||
}
|
||||
|
||||
@@ -295,6 +298,8 @@ class BaseObjectSerializer:
|
||||
"""
|
||||
if not obj_string:
|
||||
return None
|
||||
|
||||
self.deserialized = {}
|
||||
obj = safe_json_loads(obj_string)
|
||||
return self.recompose_base(obj=obj)
|
||||
|
||||
@@ -313,6 +318,9 @@ class BaseObjectSerializer:
|
||||
if isinstance(obj, str):
|
||||
obj = safe_json_loads(obj)
|
||||
|
||||
if "id" in obj and obj["id"] in self.deserialized:
|
||||
return self.deserialized[obj["id"]]
|
||||
|
||||
if "speckle_type" in obj and obj["speckle_type"] == "reference":
|
||||
obj = self.get_child(obj=obj)
|
||||
|
||||
@@ -343,14 +351,14 @@ class BaseObjectSerializer:
|
||||
|
||||
# 2. handle referenced child objects
|
||||
elif "referencedId" in value:
|
||||
ref_hash = value["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_hash)
|
||||
ref_id = value["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_id)
|
||||
if ref_obj_str:
|
||||
ref_obj = safe_json_loads(ref_obj_str, ref_hash)
|
||||
ref_obj = safe_json_loads(ref_obj_str, ref_id)
|
||||
base.__setattr__(prop, self.recompose_base(obj=ref_obj))
|
||||
else:
|
||||
warnings.warn(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}",
|
||||
f"Could not find the referenced child object of id `{ref_id}` in the given read transport: {self.read_transport.name}",
|
||||
SpeckleWarning,
|
||||
)
|
||||
base.__setattr__(prop, self.handle_value(value))
|
||||
@@ -359,6 +367,9 @@ class BaseObjectSerializer:
|
||||
else:
|
||||
base.__setattr__(prop, self.handle_value(value))
|
||||
|
||||
if "id" in obj:
|
||||
self.deserialized[obj["id"]] = base
|
||||
|
||||
return base
|
||||
|
||||
def handle_value(self, obj: Any):
|
||||
@@ -404,13 +415,13 @@ class BaseObjectSerializer:
|
||||
return obj
|
||||
|
||||
def get_child(self, obj: Dict):
|
||||
ref_hash = obj["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_hash)
|
||||
ref_id = obj["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_id)
|
||||
if not ref_obj_str:
|
||||
warnings.warn(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}",
|
||||
f"Could not find the referenced child object of id `{ref_id}` in the given read transport: {self.read_transport.name}",
|
||||
SpeckleWarning,
|
||||
)
|
||||
return obj
|
||||
|
||||
return safe_json_loads(ref_obj_str, ref_hash)
|
||||
return safe_json_loads(ref_obj_str, ref_id)
|
||||
|
||||
@@ -8,6 +8,7 @@ from appdirs import user_data_dir
|
||||
from contextlib import closing
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.paths import base_path
|
||||
|
||||
|
||||
class SQLiteTransport(AbstractTransport):
|
||||
@@ -56,21 +57,23 @@ class SQLiteTransport(AbstractTransport):
|
||||
|
||||
@staticmethod
|
||||
def get_base_path(app_name):
|
||||
# from appdirs https://github.com/ActiveState/appdirs/blob/master/appdirs.py
|
||||
# default mac path is not the one we use (we use unix path), so using special case for this
|
||||
system = sys.platform
|
||||
if system.startswith("java"):
|
||||
import platform
|
||||
# # from appdirs https://github.com/ActiveState/appdirs/blob/master/appdirs.py
|
||||
# # default mac path is not the one we use (we use unix path), so using special case for this
|
||||
# system = sys.platform
|
||||
# if system.startswith("java"):
|
||||
# import platform
|
||||
|
||||
os_name = platform.java_ver()[3][0]
|
||||
if os_name.startswith("Mac"):
|
||||
system = "darwin"
|
||||
# os_name = platform.java_ver()[3][0]
|
||||
# if os_name.startswith("Mac"):
|
||||
# system = "darwin"
|
||||
|
||||
if system != "darwin":
|
||||
return user_data_dir(appname=app_name, appauthor=False, roaming=True)
|
||||
# if system != "darwin":
|
||||
# return user_data_dir(appname=app_name, appauthor=False, roaming=True)
|
||||
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, app_name)
|
||||
# path = os.path.expanduser("~/.config/")
|
||||
# return os.path.join(path, app_name)
|
||||
|
||||
return str(base_path(app_name))
|
||||
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
@@ -188,4 +191,4 @@ class SQLiteTransport(AbstractTransport):
|
||||
self.__connection = sqlite3.connect(self._root_path)
|
||||
|
||||
def __del__(self):
|
||||
self.__connection.close()
|
||||
self.close()
|
||||
|
||||
@@ -65,6 +65,13 @@ def client(host, user_dict):
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def second_client(host, second_user_dict):
|
||||
client = SpeckleClient(host=host, use_ssl=False)
|
||||
client.authenticate_with_token(second_user_dict["token"])
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sample_stream(client):
|
||||
stream = Stream(
|
||||
|
||||
+37
-5
@@ -1,11 +1,13 @@
|
||||
from codecs import ascii_encode
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Union
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
|
||||
import pytest
|
||||
from specklepy.api import operations
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
|
||||
from specklepy.objects.base import Base, DataChunk
|
||||
from specklepy.objects.units import Units
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -82,13 +84,22 @@ def test_setting_units():
|
||||
b = Base(units="foot")
|
||||
assert b.units == "ft"
|
||||
|
||||
with pytest.raises(SpeckleException):
|
||||
with pytest.raises(SpeckleInvalidUnitException):
|
||||
b.units = "big"
|
||||
|
||||
b.units = None # invalid args are skipped
|
||||
b.units = 7
|
||||
with pytest.raises(SpeckleInvalidUnitException):
|
||||
b.units = 7 # invalid args are skipped
|
||||
assert b.units == "ft"
|
||||
|
||||
b.units = None # None should be a valid arg
|
||||
assert b.units == None
|
||||
|
||||
b.units = Units.none
|
||||
assert b.units == "none"
|
||||
|
||||
b.units = Units.cm
|
||||
assert b.units == Units.cm.value
|
||||
|
||||
|
||||
def test_base_of_custom_speckle_type() -> None:
|
||||
b1 = Base.of_type("BirdHouse", name="Tweety's Crib")
|
||||
@@ -111,6 +122,7 @@ class FrozenYoghurt(Base):
|
||||
add_ons: Optional[Dict[str, float]] # dict item types won't be checked
|
||||
price: float = 0.0
|
||||
dietary: DietaryRestrictions
|
||||
tag: Union[int, str]
|
||||
|
||||
|
||||
def test_type_checking() -> None:
|
||||
@@ -120,6 +132,8 @@ def test_type_checking() -> None:
|
||||
order.price = "7" # will get converted
|
||||
order.customer = "izzy"
|
||||
order.dietary = DietaryRestrictions.VEGAN
|
||||
order.tag = "preorder"
|
||||
order.tag = 4411
|
||||
|
||||
with pytest.raises(SpeckleException):
|
||||
order.flavours = "not a list"
|
||||
@@ -129,8 +143,26 @@ def test_type_checking() -> None:
|
||||
order.add_ons = ["sprinkles"]
|
||||
with pytest.raises(SpeckleException):
|
||||
order.dietary = "no nuts plz"
|
||||
with pytest.raises(SpeckleException):
|
||||
order.tag = ["tag01", "tag02"]
|
||||
|
||||
order.add_ons = {"sprinkles": 0.2, "chocolate": 1.0}
|
||||
order.flavours = ["strawberry", "lychee", "peach", "pineapple"]
|
||||
|
||||
assert order.price == 7.0
|
||||
|
||||
|
||||
def test_cached_deserialization() -> None:
|
||||
material = Base(color="blue", opacity=0.5)
|
||||
|
||||
a = Base(name="a")
|
||||
a["@material"] = material
|
||||
b = Base(name="b")
|
||||
b["@material"] = material
|
||||
|
||||
root = Base(a=a, b=b)
|
||||
|
||||
serialized = operations.serialize(root)
|
||||
deserialized = operations.deserialize(serialized)
|
||||
|
||||
assert deserialized["a"]["@material"] is deserialized["b"]["@material"]
|
||||
|
||||
@@ -45,4 +45,4 @@ def test_account_from_token_and_url():
|
||||
acct = get_account_from_token(token, url)
|
||||
|
||||
assert acct.token == token
|
||||
assert acct.serverInfo.url == url
|
||||
assert acct.serverInfo.url == url
|
||||
|
||||
+112
-54
@@ -1,10 +1,11 @@
|
||||
# pylint: disable=redefined-outer-name
|
||||
import json
|
||||
from typing import Callable
|
||||
|
||||
import pytest
|
||||
from specklepy.api import operations
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.units import Units
|
||||
from specklepy.objects.encoding import CurveArray, ObjectArray
|
||||
from specklepy.objects.geometry import (
|
||||
Arc,
|
||||
@@ -13,8 +14,9 @@ from specklepy.objects.geometry import (
|
||||
BrepEdge,
|
||||
BrepFace,
|
||||
BrepLoop,
|
||||
BrepLoopType,
|
||||
BrepTrim,
|
||||
BrepTrimTypeEnum,
|
||||
BrepTrimType,
|
||||
Circle,
|
||||
Curve,
|
||||
Ellipse,
|
||||
@@ -48,12 +50,7 @@ def vector():
|
||||
|
||||
@pytest.fixture()
|
||||
def plane(point, vector):
|
||||
return Plane(
|
||||
origin=point,
|
||||
normal=vector,
|
||||
xdir=vector,
|
||||
ydir=vector,
|
||||
)
|
||||
return Plane(origin=point, normal=vector, xdir=vector, ydir=vector, units="m")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@@ -74,6 +71,7 @@ def line(point, interval):
|
||||
start=point,
|
||||
end=point,
|
||||
domain=interval,
|
||||
units="none"
|
||||
# These attributes are not handled in C#
|
||||
# bbox=None,
|
||||
# length=None
|
||||
@@ -81,7 +79,7 @@ def line(point, interval):
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def arc(plane, interval):
|
||||
def arc(plane, interval, point):
|
||||
return Arc(
|
||||
radius=2.3,
|
||||
startAngle=22.1,
|
||||
@@ -90,13 +88,13 @@ def arc(plane, interval):
|
||||
plane=plane,
|
||||
domain=interval,
|
||||
units="m",
|
||||
startPoint=point,
|
||||
midPoint=point,
|
||||
endPoint=point,
|
||||
# These attributes are not handled in C#
|
||||
# bbox=None,
|
||||
# area=None,
|
||||
# length=None,
|
||||
# startPoint=None,
|
||||
# midPoint=None,
|
||||
# endPoint=None,
|
||||
)
|
||||
|
||||
|
||||
@@ -236,7 +234,7 @@ def brep_edge(interval):
|
||||
|
||||
@pytest.fixture()
|
||||
def brep_loop():
|
||||
return BrepLoop(FaceIndex=5, TrimIndices=[3, 4, 5], Type="unknown")
|
||||
return BrepLoop(FaceIndex=5, TrimIndices=[3, 4, 5], Type=BrepLoopType.Unknown)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@@ -249,7 +247,7 @@ def brep_trim():
|
||||
LoopIndex=4,
|
||||
CurveIndex=7,
|
||||
IsoStatus=6,
|
||||
TrimType="Mated",
|
||||
TrimType=BrepTrimType.Mated,
|
||||
IsReversed=False,
|
||||
# These attributes are not handled in C#
|
||||
# Domain=None,
|
||||
@@ -338,22 +336,22 @@ def geometry_objects_dict(
|
||||
],
|
||||
)
|
||||
def test_to_and_from_list(object_name: str, geometry_objects_dict):
|
||||
object = geometry_objects_dict[object_name]
|
||||
assert hasattr(object, "to_list")
|
||||
assert hasattr(object, "from_list")
|
||||
obj = geometry_objects_dict[object_name]
|
||||
assert hasattr(obj, "to_list")
|
||||
assert hasattr(obj, "from_list")
|
||||
|
||||
chunks = object.to_list()
|
||||
chunks = obj.to_list()
|
||||
assert isinstance(chunks, list)
|
||||
|
||||
object_class = object.__class__
|
||||
object_class = obj.__class__
|
||||
decoded_object: Base = object_class.from_list(chunks)
|
||||
assert decoded_object.get_id() == object.get_id()
|
||||
assert decoded_object.get_id() == obj.get_id()
|
||||
|
||||
|
||||
def test_brep_surfaces_value_serialization(surface):
|
||||
brep = Brep()
|
||||
assert brep.Surfaces == None
|
||||
assert brep.SurfacesValue == None
|
||||
assert brep.Surfaces is None
|
||||
assert brep.SurfacesValue is None
|
||||
brep.Surfaces = [surface, surface]
|
||||
assert brep.SurfacesValue == ObjectArray.from_objects([surface, surface]).data
|
||||
|
||||
@@ -364,8 +362,8 @@ def test_brep_surfaces_value_serialization(surface):
|
||||
|
||||
def test_brep_curve2d_values_serialization(curve, polyline, circle):
|
||||
brep = Brep()
|
||||
assert brep.Curve2D == None
|
||||
assert brep.Curve2DValues == None
|
||||
assert brep.Curve2D is None
|
||||
assert brep.Curve2DValues is None
|
||||
brep.Curve2D = [curve, polyline]
|
||||
assert brep.Curve2DValues == CurveArray.from_curves([curve, polyline]).data
|
||||
|
||||
@@ -376,8 +374,8 @@ def test_brep_curve2d_values_serialization(curve, polyline, circle):
|
||||
|
||||
def test_brep_curve3d_values_serialization(curve, polyline, circle):
|
||||
brep = Brep()
|
||||
assert brep.Curve3D == None
|
||||
assert brep.Curve3DValues == None
|
||||
assert brep.Curve3D is None
|
||||
assert brep.Curve3DValues is None
|
||||
brep.Curve3D = [curve, polyline]
|
||||
assert brep.Curve3DValues == CurveArray.from_curves([curve, polyline]).data
|
||||
|
||||
@@ -389,9 +387,9 @@ def test_brep_curve3d_values_serialization(curve, polyline, circle):
|
||||
def test_brep_vertices_values_serialization():
|
||||
brep = Brep()
|
||||
brep.VerticesValue = [1, 1, 1, 1, 2, 2, 2, 3, 3, 3]
|
||||
brep.Vertices[0].get_id() == Point(x=1, y=1, z=1, _units="mm").get_id()
|
||||
brep.Vertices[1].get_id() == Point(x=2, y=2, z=2, _units="mm").get_id()
|
||||
brep.Vertices[2].get_id() == Point(x=3, y=3, z=3, _units="mm").get_id()
|
||||
assert brep.Vertices[0].get_id() == Point(x=1, y=1, z=1, _units=Units.mm).get_id()
|
||||
assert brep.Vertices[1].get_id() == Point(x=2, y=2, z=2, _units=Units.mm).get_id()
|
||||
assert brep.Vertices[2].get_id() == Point(x=3, y=3, z=3, _units=Units.mm).get_id()
|
||||
|
||||
|
||||
def test_trims_value_serialization():
|
||||
@@ -405,7 +403,7 @@ def test_trims_value_serialization():
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
@@ -414,32 +412,82 @@ def test_trims_value_serialization():
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
]
|
||||
|
||||
brep.Trims[0].get_id() == BrepTrim(
|
||||
EdgeIndex=0,
|
||||
StartIndex=0,
|
||||
EndIndex=0,
|
||||
FaceIndex=0,
|
||||
LoopIndex=0,
|
||||
CurveIndex=0,
|
||||
IsoStatus=1,
|
||||
TrimType=BrepTrimTypeEnum.Boundary,
|
||||
IsReversed=False,
|
||||
).get_id()
|
||||
assert (
|
||||
brep.Trims[0].get_id()
|
||||
== BrepTrim(
|
||||
EdgeIndex=0,
|
||||
StartIndex=0,
|
||||
EndIndex=0,
|
||||
FaceIndex=0,
|
||||
LoopIndex=0,
|
||||
CurveIndex=0,
|
||||
IsoStatus=1,
|
||||
TrimType=BrepTrimType.Boundary,
|
||||
IsReversed=False,
|
||||
).get_id()
|
||||
)
|
||||
|
||||
brep.Trims[1].get_id() == BrepTrim(
|
||||
EdgeIndex=1,
|
||||
StartIndex=0,
|
||||
EndIndex=0,
|
||||
FaceIndex=0,
|
||||
LoopIndex=0,
|
||||
CurveIndex=1,
|
||||
IsoStatus=2,
|
||||
TrimType=BrepTrimTypeEnum.Boundary,
|
||||
IsReversed=True,
|
||||
).get_id()
|
||||
assert (
|
||||
brep.Trims[1].get_id()
|
||||
== BrepTrim(
|
||||
EdgeIndex=1,
|
||||
StartIndex=0,
|
||||
EndIndex=0,
|
||||
FaceIndex=0,
|
||||
LoopIndex=0,
|
||||
CurveIndex=1,
|
||||
IsoStatus=2,
|
||||
TrimType=BrepTrimType.Boundary,
|
||||
IsReversed=True,
|
||||
).get_id()
|
||||
)
|
||||
|
||||
|
||||
def test_loops_value_serialization():
|
||||
brep = Brep()
|
||||
brep.LoopsValue = [6, 0, 1, 0, 1, 2, 3]
|
||||
|
||||
assert brep == brep.Loops[0]._Brep # pylint: disable=protected-access
|
||||
assert (
|
||||
brep.Loops[0].get_id()
|
||||
== BrepLoop(
|
||||
FaceIndex=0, Type=BrepLoopType(1), TrimIndices=[0, 1, 2, 3]
|
||||
).get_id()
|
||||
)
|
||||
|
||||
|
||||
def test_edges_value_serialization():
|
||||
brep = Brep()
|
||||
brep.EdgesValue = [8, 0, 0, 1, 0, -8.13345756858629, 8.13345756858629, 1, 3]
|
||||
|
||||
assert brep == brep.Edges[0]._Brep # pylint: disable=protected-access
|
||||
assert (
|
||||
brep.Edges[0].get_id()
|
||||
== BrepEdge(
|
||||
Curve3dIndex=0,
|
||||
StartIndex=0,
|
||||
EndIndex=1,
|
||||
ProxyCurveIsReversed=False,
|
||||
Domain=Interval(start=-8.13345756858629, end=8.13345756858629),
|
||||
TrimIndices=[1, 3],
|
||||
).get_id()
|
||||
)
|
||||
|
||||
|
||||
def test_faces_value_serialization():
|
||||
brep = Brep()
|
||||
brep.FacesValue = [4, 0, 0, 1, 0]
|
||||
|
||||
assert brep == brep.Faces[0]._Brep # pylint: disable=protected-access
|
||||
assert (
|
||||
brep.Faces[0].get_id()
|
||||
== BrepFace(
|
||||
SurfaceIndex=0, OuterLoopIndex=0, OrientationReversed=True, LoopIndices=[0]
|
||||
).get_id()
|
||||
)
|
||||
|
||||
|
||||
def test_serialized_brep_attributes(brep: Brep):
|
||||
@@ -447,7 +495,16 @@ def test_serialized_brep_attributes(brep: Brep):
|
||||
serialized = operations.serialize(brep, [transport])
|
||||
serialized_dict = json.loads(serialized)
|
||||
|
||||
removed_keys = ["Surfaces", "Curve3D", "Curve2D", "Vertices", "Trims"]
|
||||
removed_keys = [
|
||||
"Surfaces",
|
||||
"Curve3D",
|
||||
"Curve2D",
|
||||
"Vertices",
|
||||
"Trims",
|
||||
"Loops",
|
||||
"Edges",
|
||||
"Faces",
|
||||
]
|
||||
|
||||
for k in removed_keys:
|
||||
assert k not in serialized_dict.keys()
|
||||
@@ -459,6 +516,7 @@ def test_mesh_create():
|
||||
mesh = Mesh.create(vertices, faces)
|
||||
|
||||
with pytest.raises(SpeckleException):
|
||||
# pylint: disable=unused-variable
|
||||
bad_mesh = Mesh.create(vertices=7, faces=faces)
|
||||
|
||||
assert mesh.vertices == vertices
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import pytest
|
||||
from specklepy.api.host_applications import get_host_app_from_string, _app_name_host_app_mapping
|
||||
|
||||
def test_get_host_app_from_string_returns_fallback_app():
|
||||
not_existing_app_name = "gmail"
|
||||
host_app = get_host_app_from_string(not_existing_app_name)
|
||||
assert host_app.name == not_existing_app_name
|
||||
assert host_app.slug == not_existing_app_name
|
||||
|
||||
@pytest.mark.parametrize("app_name", _app_name_host_app_mapping.keys())
|
||||
def test_get_host_app_from_string_matches_for_predefined_apps(app_name) -> None:
|
||||
host_app = get_host_app_from_string(app_name)
|
||||
assert app_name in host_app.slug.lower()
|
||||
@@ -91,7 +91,7 @@ class TestSerialization:
|
||||
assert deserialised == {"foo": "bar"}
|
||||
|
||||
def test_big_int(self):
|
||||
big_int = '{"big": ' + str(2 ** 64) + "}"
|
||||
big_int = '{"big": ' + str(2**64) + "}"
|
||||
deserialised = operations.deserialize(big_int)
|
||||
|
||||
assert deserialised == {"big": 2 ** 64}
|
||||
assert deserialised == {"big": 2**64}
|
||||
|
||||
+13
-2
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.client import SpeckleClient
|
||||
|
||||
|
||||
class TestServer:
|
||||
@@ -12,12 +13,22 @@ class TestServer:
|
||||
"lifespan": 9001,
|
||||
}
|
||||
|
||||
def test_server_get(self, client):
|
||||
def test_server_get(self, client: SpeckleClient):
|
||||
server = client.server.get()
|
||||
|
||||
assert isinstance(server, ServerInfo)
|
||||
|
||||
def test_server_apps(self, client):
|
||||
def test_server_version(self, client: SpeckleClient):
|
||||
version = client.server.version()
|
||||
|
||||
assert isinstance(version, tuple)
|
||||
if len(version) == 1:
|
||||
assert version[0] == "dev"
|
||||
else:
|
||||
assert isinstance(version[0], int)
|
||||
assert len(version) >= 3
|
||||
|
||||
def test_server_apps(self, client: SpeckleClient):
|
||||
apps = client.server.apps()
|
||||
|
||||
assert isinstance(apps, list)
|
||||
|
||||
+117
-12
@@ -1,8 +1,18 @@
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from specklepy.api.models import ActivityCollection, Activity, Stream
|
||||
from specklepy.api.models import (
|
||||
ActivityCollection,
|
||||
Activity,
|
||||
PendingStreamCollaborator,
|
||||
Stream,
|
||||
User,
|
||||
)
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import GraphQLException
|
||||
from specklepy.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
UnsupportedException,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.run(order=2)
|
||||
@@ -25,6 +35,10 @@ class TestStream:
|
||||
isPublic=False,
|
||||
)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def second_user(self, second_client: SpeckleClient):
|
||||
return second_client.user.get()
|
||||
|
||||
def test_stream_create(self, client, stream, updated_stream):
|
||||
stream.id = updated_stream.id = client.stream.create(
|
||||
name=stream.name,
|
||||
@@ -79,22 +93,67 @@ class TestStream:
|
||||
assert isinstance(favorited, Stream)
|
||||
assert unfavorited.favoritedDate is None
|
||||
|
||||
def test_stream_grant_permission(self, client, stream, second_user_dict):
|
||||
granted = client.stream.grant_permission(
|
||||
def test_stream_grant_permission(self, client, stream, second_user):
|
||||
# deprecated as of Speckle Server 2.6.4
|
||||
with pytest.raises(UnsupportedException):
|
||||
client.stream.grant_permission(
|
||||
stream_id=stream.id,
|
||||
user_id=second_user.id,
|
||||
role="stream:contributor",
|
||||
)
|
||||
|
||||
def test_stream_invite(
|
||||
self, client: SpeckleClient, stream: Stream, second_user_dict: dict
|
||||
):
|
||||
invited = client.stream.invite(
|
||||
stream_id=stream.id,
|
||||
user_id=second_user_dict["id"],
|
||||
role="stream:contributor",
|
||||
email=second_user_dict["email"],
|
||||
role="stream:reviewer",
|
||||
message="welcome to my stream!",
|
||||
)
|
||||
|
||||
fetched_stream = client.stream.get(stream.id)
|
||||
assert invited is True
|
||||
|
||||
assert granted is True
|
||||
assert len(fetched_stream.collaborators) == 2
|
||||
assert fetched_stream.collaborators[0].name == second_user_dict["name"]
|
||||
# fail if no email or id
|
||||
with pytest.raises(SpeckleException):
|
||||
client.stream.invite(stream_id=stream.id)
|
||||
|
||||
def test_stream_revoke_permission(self, client, stream, second_user_dict):
|
||||
def test_stream_invite_get_all_for_user(
|
||||
self, second_client: SpeckleClient, stream: Stream
|
||||
):
|
||||
# NOTE: these are user queries, but testing here to contain the flow
|
||||
invites = second_client.user.get_all_pending_invites()
|
||||
|
||||
assert isinstance(invites, list)
|
||||
assert isinstance(invites[0], PendingStreamCollaborator)
|
||||
assert len(invites) == 1
|
||||
|
||||
invite = second_client.user.get_pending_invite(stream_id=stream.id)
|
||||
assert isinstance(invite, PendingStreamCollaborator)
|
||||
|
||||
def test_stream_invite_use(self, second_client: SpeckleClient, stream: Stream):
|
||||
invite: PendingStreamCollaborator = (
|
||||
second_client.user.get_all_pending_invites()[0]
|
||||
)
|
||||
|
||||
accepted = second_client.stream.invite_use(
|
||||
stream_id=stream.id, token=invite.token
|
||||
)
|
||||
|
||||
assert accepted is True
|
||||
|
||||
def test_stream_update_permission(
|
||||
self, client: SpeckleClient, stream: Stream, second_user: User
|
||||
):
|
||||
updated = client.stream.update_permission(
|
||||
stream_id=stream.id, user_id=second_user.id, role="stream:contributor"
|
||||
)
|
||||
|
||||
assert updated is True
|
||||
|
||||
def test_stream_revoke_permission(self, client, stream, second_user):
|
||||
revoked = client.stream.revoke_permission(
|
||||
stream_id=stream.id, user_id=second_user_dict["id"]
|
||||
stream_id=stream.id, user_id=second_user.id
|
||||
)
|
||||
|
||||
fetched_stream = client.stream.get(stream.id)
|
||||
@@ -102,6 +161,52 @@ class TestStream:
|
||||
assert revoked is True
|
||||
assert len(fetched_stream.collaborators) == 1
|
||||
|
||||
def test_stream_invite_cancel(
|
||||
self,
|
||||
client: SpeckleClient,
|
||||
stream: Stream,
|
||||
second_user: User,
|
||||
):
|
||||
invited = client.stream.invite(
|
||||
stream_id=stream.id,
|
||||
user_id=second_user.id,
|
||||
message="welcome to my stream!",
|
||||
)
|
||||
assert invited is True
|
||||
|
||||
invites = client.stream.get_all_pending_invites(stream_id=stream.id)
|
||||
|
||||
cancelled = client.stream.invite_cancel(
|
||||
invite_id=invites[0].inviteId, stream_id=stream.id
|
||||
)
|
||||
|
||||
assert cancelled is True
|
||||
|
||||
def test_stream_invite_batch(
|
||||
self, client: SpeckleClient, stream: Stream, second_user: User
|
||||
):
|
||||
# NOTE: only works for server admins
|
||||
# invited = client.stream.invite_batch(
|
||||
# stream_id=stream.id,
|
||||
# emails=["userA@speckle.xyz", "userB@speckle.xyz"],
|
||||
# user_ids=[second_user.id],
|
||||
# message="yeehaw 🤠",
|
||||
# )
|
||||
|
||||
# assert invited is True
|
||||
|
||||
# invited_only_email = client.stream.invite_batch(
|
||||
# stream_id=stream.id,
|
||||
# emails=["userC@speckle.xyz"],
|
||||
# message="yeehaw 🤠",
|
||||
# )
|
||||
|
||||
# assert invited_only_email is True
|
||||
|
||||
# fail if no emails or user ids
|
||||
with pytest.raises(SpeckleException):
|
||||
client.stream.invite_batch(stream_id=stream.id)
|
||||
|
||||
def test_stream_activity(self, client: SpeckleClient, stream: Stream):
|
||||
activity = client.stream.activity(stream.id)
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
import json
|
||||
from typing import Callable
|
||||
|
||||
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 (
|
||||
Line,
|
||||
Mesh,
|
||||
Point,
|
||||
Vector,
|
||||
)
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.objects.structural.geometry import (
|
||||
Node,
|
||||
Element1D,
|
||||
Element2D,
|
||||
Restraint,
|
||||
ElementType1D,
|
||||
ElementType2D,
|
||||
)
|
||||
from specklepy.objects.structural.properties import (
|
||||
Property1D,
|
||||
Property2D,
|
||||
SectionProfile,
|
||||
MemberType,
|
||||
ShapeType,
|
||||
)
|
||||
from specklepy.objects.structural.material import (
|
||||
Material,
|
||||
)
|
||||
from specklepy.objects.structural.analysis import Model
|
||||
|
||||
from specklepy.objects.structural.loading import LoadGravity
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def point():
|
||||
return Point(x=1, y=10, z=0)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def vector():
|
||||
return Vector(x=0, y=0, z=-1)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def line(point, interval):
|
||||
return Line(
|
||||
start=point,
|
||||
end=point,
|
||||
domain=interval,
|
||||
# These attributes are not handled in C#
|
||||
# bbox=None,
|
||||
# length=None
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mesh(box):
|
||||
return Mesh(
|
||||
vertices=[2, 1, 2, 4, 77.3, 5, 33, 4, 2],
|
||||
faces=[1, 2, 3, 4, 5, 6, 7],
|
||||
colors=[111, 222, 333, 444, 555, 666, 777],
|
||||
bbox=box,
|
||||
area=233,
|
||||
volume=232.2,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def restraint():
|
||||
return Restraint(code="FFFFFF")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def node(restraint, point):
|
||||
return Node(basePoint=point, restraint=restraint, name="node1")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def material():
|
||||
return Material(name="TestMaterial")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def memberType():
|
||||
return MemberType(0)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def shapeType():
|
||||
return ShapeType(8)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sectionProfile(shapeType):
|
||||
return SectionProfile(name="Test", shapeType=shapeType)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def property1D(memberType, sectionProfile, material):
|
||||
return Property1D(
|
||||
Material=material,
|
||||
SectionProfile=sectionProfile,
|
||||
memberType=memberType,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def elementType1D():
|
||||
return ElementType1D(0)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def element1D(line, restraint, elementType1D, property1D):
|
||||
return Element1D(
|
||||
baseLine=line,
|
||||
end1Releases=restraint,
|
||||
end2Releases=restraint,
|
||||
type=elementType1D,
|
||||
property=property1D,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def property2D(material):
|
||||
return Property2D(Material=material)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def elementType2D():
|
||||
return ElementType2D(0)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def element2D(point, elementType2D):
|
||||
return Element2D(
|
||||
topology=[point],
|
||||
type=elementType2D,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def loadGravity(element1D, element2D, vector):
|
||||
return LoadGravity(elements=[element1D, element2D], gravityFactors=vector)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def model(loadGravity, element1D, element2D, material, property1D, property2D):
|
||||
return Model(
|
||||
loads=[loadGravity],
|
||||
elements=[element1D, element2D],
|
||||
materials=[material],
|
||||
properties=[property1D, property2D],
|
||||
)
|
||||
@@ -129,4 +129,4 @@ def test_transform_serialisation(transform: Transform):
|
||||
serialized = operations.serialize(transform)
|
||||
deserialized = operations.deserialize(serialized)
|
||||
|
||||
assert transform.get_id() == deserialized.get_id()
|
||||
assert transform.get_id() == deserialized.get_id()
|
||||
|
||||
+4
-1
@@ -29,7 +29,10 @@ class TestUser:
|
||||
|
||||
assert isinstance(fetched_user, User)
|
||||
assert fetched_user.name == second_user_dict["name"]
|
||||
assert fetched_user.email == second_user_dict["email"]
|
||||
# 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
|
||||
# assert fetched_user.email == None
|
||||
|
||||
second_user_dict["id"] = fetched_user.id
|
||||
|
||||
|
||||
+41
-1
@@ -1,5 +1,9 @@
|
||||
import pytest
|
||||
import json
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.paths import accounts_path
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
|
||||
def test_parse_stream():
|
||||
@@ -79,3 +83,39 @@ def test_get_transport_with_token():
|
||||
|
||||
assert transport is not None
|
||||
assert client.account.token == "super-secret-token"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user_path() -> Path:
|
||||
path = accounts_path().joinpath("test_acc.json")
|
||||
# hey, py37 doesn't support the missing_ok argument
|
||||
try:
|
||||
path.unlink()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
path.unlink(missing_ok=True)
|
||||
except:
|
||||
pass
|
||||
path.parent.absolute().mkdir(exist_ok=True)
|
||||
yield path
|
||||
path.unlink()
|
||||
|
||||
|
||||
def test_wrapper_url_match(user_path) -> None:
|
||||
"""
|
||||
The stream wrapper should only recognize exact url matches for the account
|
||||
definitions and not match for subdomains.
|
||||
"""
|
||||
account = {
|
||||
"token": "foobar",
|
||||
"serverInfo": {"name": "foo", "url": "http://foo.bar.baz", "company": "Foo"},
|
||||
"userInfo": {"id": "bla", "name": "A rando tester", "email": "rando@tester.me"},
|
||||
}
|
||||
|
||||
user_path.write_text(json.dumps(account))
|
||||
wrap = StreamWrapper("http://bar.baz/streams/bogus")
|
||||
|
||||
account = wrap.get_account()
|
||||
|
||||
assert account.userInfo.email is None
|
||||
|
||||
Reference in New Issue
Block a user