From 990cf4eb2ff4e77013dbebf666eab9f720aa4a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 20 Dec 2022 10:45:22 +0100 Subject: [PATCH] style(whole-project): fixing linting and typing errors --- .pre-commit-config.yaml | 10 +++- example/many_children.py | 1 - example/send_receive.py | 15 ++++++ example/stream_objects.py | 1 - patch_version.py | 2 +- poetry.lock | 52 ++++++++++++++++++- pyproject.toml | 5 +- src/specklepy/api/client.py | 41 ++++++++++----- src/specklepy/api/credentials.py | 22 +++++--- src/specklepy/api/models.py | 33 +++++++++--- src/specklepy/api/operations.py | 31 +++++++---- src/specklepy/api/resource.py | 33 +++++++----- src/specklepy/api/resources/__init__.py | 3 +- src/specklepy/api/resources/active_user.py | 48 ++++++++++++----- src/specklepy/api/resources/object.py | 17 ++++-- src/specklepy/api/resources/other_user.py | 35 ++++++++++--- src/specklepy/api/resources/server.py | 4 +- src/specklepy/api/resources/stream.py | 31 +++++------ src/specklepy/api/resources/subscriptions.py | 38 +++++++++----- src/specklepy/api/resources/user.py | 9 +++- src/specklepy/api/wrapper.py | 49 +++++++++++------ src/specklepy/logging/exceptions.py | 11 ++-- src/specklepy/logging/metrics.py | 8 ++- src/specklepy/objects/base.py | 30 +++++++---- src/specklepy/objects/geometry.py | 12 +++-- src/specklepy/objects/other.py | 23 +++++--- src/specklepy/objects/structural/loading.py | 7 --- .../objects/structural/properties.py | 1 - .../serialization/base_object_serializer.py | 40 +++++++++----- src/specklepy/transports/server/__init__.py | 2 +- .../transports/server/batch_sender.py | 12 +++-- src/specklepy/transports/server/server.py | 44 +++++++++------- src/specklepy/transports/sqlite.py | 32 +++++++----- tests/test_base.py | 12 ++--- tests/test_commit.py | 2 +- tests/test_geometry.py | 3 +- tests/test_other_user.py | 1 - tests/test_serialization.py | 1 - tests/test_stream.py | 2 - tests/test_transforms.py | 9 +--- tests/test_wrapper.py | 11 ++-- 41 files changed, 506 insertions(+), 237 deletions(-) create mode 100644 example/send_receive.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f8693834..600e0faf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,9 @@ repos: + - repo: https://github.com/charliermarsh/ruff-pre-commit + hooks: + - id: ruff + rev: v0.0.186 + - repo: https://github.com/commitizen-tools/commitizen hooks: - id: commitizen @@ -8,9 +13,10 @@ repos: rev: v2.38.0 - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: v5.11.3 hooks: - id: isort + - repo: https://github.com/psf/black rev: 22.12.0 hooks: @@ -21,7 +27,7 @@ repos: # https://pre-commit.com/#top_level-default_language_version # language_version: python3.11 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace diff --git a/example/many_children.py b/example/many_children.py index 64a15543..1f5849a3 100644 --- a/example/many_children.py +++ b/example/many_children.py @@ -27,7 +27,6 @@ def clean_db(): def one_pass(clean: bool, randomize: bool, child_count: int): - foo = Base() for i in range(child_count): stuff = random_string() if randomize else "stuff" diff --git a/example/send_receive.py b/example/send_receive.py new file mode 100644 index 00000000..59d3fc03 --- /dev/null +++ b/example/send_receive.py @@ -0,0 +1,15 @@ +from devtools import debug + +from specklepy.api import operations +from specklepy.api.wrapper import StreamWrapper + +if __name__ == "__main__": + stream_url = "https://latest.speckle.dev/streams/7d051a6449" + wrapper = StreamWrapper(stream_url) + + transport = wrapper.get_transport() + + rec = operations.receive("98396753f8bf7fe1cb60c5193e9f9d86", transport) + + # hash = operations.send(base=foo, transports=[transport], use_default_cache=False) + debug(rec) diff --git a/example/stream_objects.py b/example/stream_objects.py index cc4ecb82..ca8e50e9 100644 --- a/example/stream_objects.py +++ b/example/stream_objects.py @@ -25,7 +25,6 @@ def create_object(child_count: int) -> Base: if __name__ == "__main__": - stream_url = "http://hyperion:3000/streams/2372b54c35" child_count = 10 diff --git a/patch_version.py b/patch_version.py index 10bd2bf0..1c7262cb 100644 --- a/patch_version.py +++ b/patch_version.py @@ -9,7 +9,7 @@ def patch(tag): lines = f.readlines() if "version" not in lines[2]: - raise Exception(f"Invalid pyproject.toml. Could not patch version.") + raise Exception("Invalid pyproject.toml. Could not patch version.") lines[2] = f'version = "{tag}"\n' with open("pyproject.toml", "w") as file: diff --git a/poetry.lock b/poetry.lock index 0e6304f7..33ede636 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1172,6 +1172,32 @@ files = [ [package.dependencies] requests = ">=2.0.1,<3.0.0" +[[package]] +name = "ruff" +version = "0.0.187" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.187-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:d90cbd30c585a9551695af09c91c07542a4bfebe9870f8ca4de5d9ac34478665"}, + {file = "ruff-0.0.187-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:bca4d85f182c7452f5b607e99c03a0957126bfb260815b824f7fabde519708d8"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c498a7e183c2cd58e8cc0bed93d88e44305365a93132463911680d20d158ae8a"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb525861aae5b005e92c7f32dff8382bbd13a2da5a06b950a11ba879bb9fef15"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41cfed788d5f8bd618710432583b0f61195769919e8bc1a77f080d5db2ebbb93"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:25a9f74ee9b9472b750c7f0e25551b6f3b298a62126d0386bc9bdda1fed00c35"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24d0e61041abd2811cf841b43572fed1d0343e96704aedfac388f03dc2f1f53e"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88722ddbeec8614c9a5fd90eac3aa13372ede98559c36ef95581a26954a85fb5"}, + {file = "ruff-0.0.187-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9032f221d28897852c5de173cd6890185f4c86a93eca4ed0c215076e8f0f626"}, + {file = "ruff-0.0.187-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11833a0e80ff9d244d11c16cc4d1283e84cf0ac47f2440d88093ee3076687fcb"}, + {file = "ruff-0.0.187-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:debcd3740da13b786656fcf23caa1a328c678b19ab822243bd00a25efb00ab7b"}, + {file = "ruff-0.0.187-py3-none-musllinux_1_2_i686.whl", hash = "sha256:51f4bf4940a57b4e04254f2823a0e4de52595987c65e0c9ed7b4546c5362f60e"}, + {file = "ruff-0.0.187-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab7e50ba517f3bfaaadaba3f9e68e0a95ff04abab5c480f05015a156f58f16d3"}, + {file = "ruff-0.0.187-py3-none-win32.whl", hash = "sha256:25e55cc000ca6fe83df6d25080adec86acb3a39cd623ed9ff122ccede02ab6ba"}, + {file = "ruff-0.0.187-py3-none-win_amd64.whl", hash = "sha256:a81acd2b111b267cda09c507ad72a7ca47fa6f47e1ec240e594eef37db1bdf8d"}, + {file = "ruff-0.0.187.tar.gz", hash = "sha256:d209efb6cb4f03b31c76d7be770cb387cbdf0592c604f0d0639c1a6f742db04c"}, +] + [[package]] name = "setuptools" version = "65.6.3" @@ -1286,6 +1312,30 @@ files = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] +[[package]] +name = "types-deprecated" +version = "1.2.9" +description = "Typing stubs for Deprecated" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-Deprecated-1.2.9.tar.gz", hash = "sha256:e04ce58929509865359e91dcc38720123262b4cd68fa2a8a90312d50390bb6fa"}, + {file = "types_Deprecated-1.2.9-py3-none-any.whl", hash = "sha256:53d05621e1d75de537f5a57d93508c8df17e37c07ee60b9fb09d39e1b7586c1e"}, +] + +[[package]] +name = "types-ujson" +version = "5.6.0.0" +description = "Typing stubs for ujson" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-ujson-5.6.0.0.tar.gz", hash = "sha256:1a20cf7946772756736582612e0da5656d2dbeccd24be4c1e97d1e66b072b97e"}, + {file = "types_ujson-5.6.0.0-py3-none-any.whl", hash = "sha256:010b221260c24a915c6e713a83f366b91390766850ec110304de5b20c86b4b11"}, +] + [[package]] name = "typing-extensions" version = "4.4.0" @@ -1670,4 +1720,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.7.2, <4.0" -content-hash = "fba4eaa4891e1f626c0d6a04a919985d90a8d0a8bbf334d2846e2a16da28304c" +content-hash = "18bc7c2f98f551538bf1dfca42a0c3c352e98f2c0a7a59619b8fd16bd2c13e39" diff --git a/pyproject.toml b/pyproject.toml index 9dcdb08d..b03344b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,9 @@ pylint = "^2.14.4" mypy = "^0.982" pre-commit = "^2.20.0" commitizen = "^2.38.0" +ruff = "^0.0.187" +types-deprecated = "^1.2.9" +types-ujson = "^5.6.0.0" [tool.black] exclude = ''' @@ -50,7 +53,7 @@ exclude = ''' ''' include = '\.pyi?$' line-length = 88 -target-version = ["py37", "py38", "py39", "py310"] +target-version = ["py37", "py38", "py39", "py310", "py311"] [tool.commitizen] diff --git a/src/specklepy/api/client.py b/src/specklepy/api/client.py index d1b78eed..40207da3 100644 --- a/src/specklepy/api/client.py +++ b/src/specklepy/api/client.py @@ -26,10 +26,13 @@ class SpeckleClient: """ - The `SpeckleClient` is your entry point for interacting with your Speckle Server's GraphQL API. - You'll need to have access to a server to use it, or you can use our public server `speckle.xyz`. + The `SpeckleClient` is your entry point for interacting with + your Speckle Server's GraphQL API. + You'll need to have access to a server to use it, + or you can use our public server `speckle.xyz`. - To authenticate the client, you'll need to have downloaded the [Speckle Manager](https://speckle.guide/#speckle-manager) + To authenticate the client, you'll need to have downloaded + the [Speckle Manager](https://speckle.guide/#speckle-manager) and added your account. ```py @@ -92,15 +95,22 @@ def __init__(self, host: str = DEFAULT_HOST, use_ssl: bool = USE_SSL) -> None: # ) from ex def __repr__(self): - return f"SpeckleClient( server: {self.url}, authenticated: {self.account.token is not None} )" + return ( + f"SpeckleClient( server: {self.url}, authenticated:" + f" {self.account.token is not None} )" + ) @deprecated( version="2.6.0", - reason="Renamed: please use `authenticate_with_account` or `authenticate_with_token` instead.", + reason=( + "Renamed: please use `authenticate_with_account` or" + " `authenticate_with_token` instead." + ), ) def authenticate(self, token: str) -> None: """Authenticate the client using a personal access token - The token is saved in the client object and a synchronous GraphQL entrypoint is created + The token is saved in the client object and a synchronous GraphQL + entrypoint is created Arguments: token {str} -- an api token @@ -109,8 +119,10 @@ def authenticate(self, token: str) -> None: self._set_up_client() def authenticate_with_token(self, token: str) -> None: - """Authenticate the client using a personal access token - The token is saved in the client object and a synchronous GraphQL entrypoint is created + """ + Authenticate the client using a personal access token. + The token is saved in the client object and a synchronous GraphQL + entrypoint is created Arguments: token {str} -- an api token @@ -121,10 +133,12 @@ def authenticate_with_token(self, token: str) -> None: def authenticate_with_account(self, account: Account) -> None: """Authenticate the client using an Account object - The account is saved in the client object and a synchronous GraphQL entrypoint is created + The account is saved in the client object and a synchronous GraphQL + entrypoint is created Arguments: - account {Account} -- the account object which can be found with `get_default_account` or `get_local_accounts` + account {Account} -- the account object which can be found with + `get_default_account` or `get_local_accounts` """ metrics.track(metrics.CLIENT, account, {"name": "authenticate with account"}) self.account = account @@ -153,7 +167,8 @@ def _set_up_client(self) -> None: if self.user.get() is None: warn( SpeckleWarning( - f"Possibly invalid token - could not authenticate Speckle Client for server {self.url}" + "Possibly invalid token - could not authenticate Speckle Client" + f" for server {self.url}" ) ) @@ -167,7 +182,7 @@ def _init_resources(self) -> None: server_version = None try: server_version = self.server.version() - except: + except Exception: pass self.user = user.Resource( account=self.account, @@ -214,7 +229,7 @@ def __getattr__(self, name): return attr.Resource( account=self.account, basepath=self.url, client=self.httpclient ) - except: + except AttributeError: raise SpeckleException( f"Method {name} is not supported by the SpeckleClient class" ) diff --git a/src/specklepy/api/credentials.py b/src/specklepy/api/credentials.py index 689c5b9c..c18fe797 100644 --- a/src/specklepy/api/credentials.py +++ b/src/specklepy/api/credentials.py @@ -26,7 +26,10 @@ class Account(BaseModel): id: Optional[str] = None def __repr__(self) -> str: - return f"Account(email: {self.userInfo.email}, server: {self.serverInfo.url}, isDefault: {self.isDefault})" + return ( + f"Account(email: {self.userInfo.email}, server: {self.serverInfo.url}," + f" isDefault: {self.isDefault})" + ) def __str__(self) -> str: return self.__repr__() @@ -45,7 +48,8 @@ def get_local_accounts(base_path: Optional[str] = None) -> List[Account]: base_path {str} -- custom base path if you are not using the system default Returns: - List[Account] -- list of all local accounts or an empty list if no accounts were found + List[Account] -- list of all local accounts or an empty list if + no accounts were found """ accounts: List[Account] = [] try: @@ -95,7 +99,9 @@ def get_local_accounts(base_path: Optional[str] = None) -> List[Account]: def get_default_account(base_path: Optional[str] = None) -> Optional[Account]: - """Gets this environment's default account if any. If there is no default, the first found will be returned and set as default. + """ + Gets this environment's default account if any. If there is no default, + the first found will be returned and set as default. Arguments: base_path {str} -- custom base path if you are not using the system default @@ -121,7 +127,8 @@ def get_account_from_token(token: str, server_url: str = None) -> Account: token {str} -- the api token Returns: - Account -- the local account with this token or a shell account containing just the token and url if no local account is found + Account -- the local account with this token or a shell account containing + just the token and url if no local account is found """ accounts = get_local_accounts() if not accounts: @@ -145,6 +152,9 @@ def get_account_from_token(token: str, server_url: str = None) -> Account: class StreamWrapper: def __init__(self, url: str = None) -> None: raise SpeckleException( - message="The StreamWrapper has moved as of v2.6.0! Please import from specklepy.api.wrapper", - exception=DeprecationWarning, + message=( + "The StreamWrapper has moved as of v2.6.0! Please import from" + " specklepy.api.wrapper" + ), + exception=DeprecationWarning(), ) diff --git a/src/specklepy/api/models.py b/src/specklepy/api/models.py index 4729095c..defec361 100644 --- a/src/specklepy/api/models.py +++ b/src/specklepy/api/models.py @@ -25,7 +25,11 @@ class Commit(BaseModel): parents: Optional[List[str]] def __repr__(self) -> str: - return f"Commit( id: {self.id}, message: {self.message}, referencedObject: {self.referencedObject}, authorName: {self.authorName}, branchName: {self.branchName}, createdAt: {self.createdAt} )" + return ( + f"Commit( id: {self.id}, message: {self.message}, referencedObject:" + f" {self.referencedObject}, authorName: {self.authorName}, branchName:" + f" {self.branchName}, createdAt: {self.createdAt} )" + ) def __str__(self) -> str: return self.__repr__() @@ -75,7 +79,10 @@ class Stream(BaseModel): favoritesCount: Optional[int] = None def __repr__(self): - return f"Stream( id: {self.id}, name: {self.name}, description: {self.description}, isPublic: {self.isPublic})" + return ( + f"Stream( id: {self.id}, name: {self.name}, description:" + f" {self.description}, isPublic: {self.isPublic})" + ) def __str__(self) -> str: return self.__repr__() @@ -99,7 +106,10 @@ class User(BaseModel): streams: Optional[Streams] def __repr__(self): - return f"User( id: {self.id}, name: {self.name}, email: {self.email}, company: {self.company} )" + return ( + f"User( id: {self.id}, name: {self.name}, email: {self.email}, company:" + f" {self.company} )" + ) def __str__(self) -> str: return self.__repr__() @@ -129,7 +139,11 @@ class PendingStreamCollaborator(BaseModel): 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})" + return ( + f"PendingStreamCollaborator( inviteId: {self.inviteId}, streamId:" + f" {self.streamId}, role: {self.role}, title: {self.title}, invitedBy:" + f" {self.user.name if self.user else None})" + ) def __str__(self) -> str: return self.__repr__() @@ -146,7 +160,10 @@ class Activity(BaseModel): time: Optional[datetime] def __repr__(self) -> str: - return f"Activity( streamId: {self.streamId}, actionType: {self.actionType}, message: {self.message}, userId: {self.userId} )" + return ( + f"Activity( streamId: {self.streamId}, actionType: {self.actionType}," + f" message: {self.message}, userId: {self.userId} )" + ) def __str__(self) -> str: return self.__repr__() @@ -158,7 +175,11 @@ 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() if self.cursor else None} )" + return ( + f"ActivityCollection( totalCount: {self.totalCount}, items:" + f" {len(self.items) if self.items else 0}, cursor:" + f" {self.cursor.isoformat() if self.cursor else None} )" + ) def __str__(self) -> str: return self.__repr__() diff --git a/src/specklepy/api/operations.py b/src/specklepy/api/operations.py index 722cd40f..600f0641 100644 --- a/src/specklepy/api/operations.py +++ b/src/specklepy/api/operations.py @@ -18,7 +18,8 @@ def send( Arguments: obj {Base} -- the object you want to send transports {list} -- where you want to send them - use_default_cache {bool} -- toggle for the default cache. If set to false, it will only send to the provided transports + use_default_cache {bool} -- toggle for the default cache. + If set to false, it will only send to the provided transports Returns: str -- the object id of the sent object @@ -26,7 +27,10 @@ def send( if not transports and not use_default_cache: raise SpeckleException( - message="You need to provide at least one transport: cannot send with an empty transport list and no default cache" + message=( + "You need to provide at least one transport: cannot send with an empty" + " transport list and no default cache" + ) ) if isinstance(transports, AbstractTransport): @@ -62,7 +66,6 @@ def _untracked_receive( remote_transport: Optional[AbstractTransport] = None, local_transport: Optional[AbstractTransport] = None, ) -> Base: - """Receives an object from a transport. Arguments: @@ -86,7 +89,10 @@ def _untracked_receive( if not remote_transport: raise SpeckleException( - message="Could not find the specified object using the local transport, and you didn't provide a fallback remote from which to pull it." + message=( + "Could not find the specified object using the local transport, and you" + " didn't provide a fallback remote from which to pull it." + ) ) obj_string = remote_transport.copy_object_and_children( @@ -98,12 +104,14 @@ def _untracked_receive( def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str: """ - Serialize a base object. If no write transports are provided, the object will be serialized + Serialize a base object. If no write transports are provided, + the object will be serialized without detaching or chunking any of the attributes. Arguments: base {Base} -- the object to serialize - write_transports {List[AbstractTransport]} -- optional: the transports to write to + write_transports {List[AbstractTransport]} + -- optional: the transports to write to Returns: str -- the serialized object @@ -118,12 +126,17 @@ def deserialize( obj_string: str, read_transport: Optional[AbstractTransport] = None ) -> Base: """ - Deserialize a string object into a Base object. If the object contains referenced child objects that are not stored in the local db, a read transport needs to be provided in order to recompose the base with the children objects. + Deserialize a string object into a Base object. + + If the object contains referenced child objects that are not stored in the local db, + a read transport needs to be provided in order to recompose + the base with the children objects. Arguments: obj_string {str} -- the string object to deserialize - read_transport {AbstractTransport} -- the transport to fetch children objects from - (defaults to SQLiteTransport) + read_transport {AbstractTransport} + -- the transport to fetch children objects from + (defaults to SQLiteTransport) Returns: Base -- the deserialized object diff --git a/src/specklepy/api/resource.py b/src/specklepy/api/resource.py index fd1519b1..47e24dfa 100644 --- a/src/specklepy/api/resource.py +++ b/src/specklepy/api/resource.py @@ -52,7 +52,7 @@ def _parse_response(self, response: Union[dict, list, None], schema=None): elif self.schema: try: return self.schema.parse_obj(response) - except: + except Exception: s = BaseObjectSerializer(read_transport=SQLiteTransport()) return s.recompose_base(response) else: @@ -61,7 +61,7 @@ def _parse_response(self, response: Union[dict, list, None], schema=None): def make_request( self, query: DocumentNode, - params: Dict = None, + params: Optional[Dict] = None, return_type: Union[str, List, None] = None, schema=None, parse_response: bool = True, @@ -72,13 +72,19 @@ def make_request( except Exception as ex: if isinstance(ex, TransportQueryError): return GraphQLException( - message=f"Failed to execute the GraphQL {self.name} request. Errors: {ex.errors}", + message=( + f"Failed to execute the GraphQL {self.name} request. Errors:" + f" {ex.errors}" + ), errors=ex.errors, data=ex.data, ) else: return SpeckleException( - message=f"Failed to execute the GraphQL {self.name} request. Inner exception: {ex}", + message=( + f"Failed to execute the GraphQL {self.name} request. Inner" + f" exception: {ex}" + ), exception=ex, ) @@ -90,16 +96,20 @@ def make_request( return response def _check_server_version_at_least( - self, target_version: Tuple[Any, ...], unsupported_message: str = None + self, target_version: Tuple[Any, ...], unsupported_message: Optional[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 + 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)}" + unsupported_message = ( + "The client method used is not supported on Speckle Server versions" + f" prior to v{'.'.join(target_version)}" + ) # if version is dev, it should be supported... (or not) if self.server_version == ("dev",): return @@ -112,8 +122,7 @@ def _check_invites_supported(self): """ 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." - ), + "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.", ) diff --git a/src/specklepy/api/resources/__init__.py b/src/specklepy/api/resources/__init__.py index 42512fa4..13beff43 100644 --- a/src/specklepy/api/resources/__init__.py +++ b/src/specklepy/api/resources/__init__.py @@ -2,8 +2,7 @@ import sys from importlib import import_module -for (_, name, _) in pkgutil.iter_modules(__path__): - +for _, name, _ in pkgutil.iter_modules(__path__): imported_module = import_module("." + name, package=__name__) if hasattr(imported_module, "Resource"): diff --git a/src/specklepy/api/resources/active_user.py b/src/specklepy/api/resources/active_user.py index 46fb6b71..9f9128a6 100644 --- a/src/specklepy/api/resources/active_user.py +++ b/src/specklepy/api/resources/active_user.py @@ -25,7 +25,9 @@ def __init__(self, account, basepath, client, server_version) -> None: self.schema = User def get(self) -> User: - """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). + """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). Arguments: id {str} -- the user id @@ -88,7 +90,9 @@ def update( if not params["user"]: return SpeckleException( - message="You must provide at least one field to update your user profile" + message=( + "You must provide at least one field to update your user profile" + ) ) return self.make_request( @@ -104,24 +108,42 @@ def activity( cursor: Optional[datetime] = None, ): """ - Get the activity from a given stream in an Activity collection. Step into the activity `items` for the list of activity. - If no id argument is provided, will return the current authenticated user's activity (as extracted from the authorization header). + Get the activity from a given stream in an Activity collection. + Step into the activity `items` for the list of activity. + If no id argument is provided, will return the current authenticated user's + activity (as extracted from the authorization header). - Note: all timestamps arguments should be `datetime` of any tz as they will be converted to UTC ISO format strings + Note: all timestamps arguments should be `datetime` of any tz as they will be + converted to UTC ISO format strings user_id {str} -- the id of the user to get the activity from - action_type {str} -- filter results to a single action type (eg: `commit_create` or `commit_receive`) + action_type {str} -- filter results to a single action type + (eg: `commit_create` or `commit_receive`) limit {int} -- max number of Activity items to return - before {datetime} -- latest cutoff for activity (ie: return all activity _before_ this time) - after {datetime} -- oldest cutoff for activity (ie: return all activity _after_ this time) + before {datetime} -- latest cutoff for activity + (ie: return all activity _before_ this time) + after {datetime} -- oldest cutoff for activity + (ie: return all activity _after_ this time) cursor {datetime} -- timestamp cursor for pagination """ query = gql( """ - query UserActivity($action_type: String, $before:DateTime, $after: DateTime, $cursor: DateTime, $limit: Int){ + query UserActivity( + $action_type: String, + $before:DateTime, + $after: DateTime, + $cursor: DateTime, + $limit: Int + ){ activeUser { - activity(actionType: $action_type, before: $before, after: $after, cursor: $cursor, limit: $limit) { + activity( + actionType: $action_type, + before: $before, + after: $after, + cursor: $cursor, + limit: $limit + ) { totalCount cursor items { @@ -161,7 +183,8 @@ def get_all_pending_invites(self) -> List[PendingStreamCollaborator]: Requires Speckle Server version >= 2.6.4 Returns: - List[PendingStreamCollaborator] -- a list of pending invites for the current user + List[PendingStreamCollaborator] + -- a list of pending invites for the current user """ metrics.track(metrics.INVITE, self.account, {"name": "get"}) self._check_invites_supported() @@ -207,7 +230,8 @@ def get_pending_invite( 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) + 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() diff --git a/src/specklepy/api/resources/object.py b/src/specklepy/api/resources/object.py index dd802382..70e116b5 100644 --- a/src/specklepy/api/resources/object.py +++ b/src/specklepy/api/resources/object.py @@ -61,21 +61,28 @@ def create(self, stream_id: str, objects: List[Dict]) -> str: """ Not advised - generally, you want to use `operations.send()`. - Create a new object on a stream. To send a base object, you can prepare it by running it through the - `BaseObjectSerializer.traverse_base()` function to get a valid (serialisable) object to send. + Create a new object on a stream. + To send a base object, you can prepare it by running it through the + `BaseObjectSerializer.traverse_base()` function to get a valid (serialisable) + object to send. - NOTE: this does not create a commit - you can create one with `SpeckleClient.commit.create`. Dynamic fields will be located in the 'data' dict of the received `Base` object + NOTE: this does not create a commit - you can create one with + `SpeckleClient.commit.create`. + Dynamic fields will be located in the 'data' dict of the received `Base` object Arguments: stream_id {str} -- the id of the stream you want to send the object to - objects {List[Dict]} -- a list of base dictionary objects (NOTE: must be json serialisable) + objects {List[Dict]} + -- a list of base dictionary objects (NOTE: must be json serialisable) Returns: str -- the id of the object """ query = gql( """ - mutation ObjectCreate($object_input: ObjectCreateInput!) { objectCreate(objectInput: $object_input) } + mutation ObjectCreate($object_input: ObjectCreateInput!) { + objectCreate(objectInput: $object_input) + } """ ) params = {"object_input": {"streamId": stream_id, "objects": objects}} diff --git a/src/specklepy/api/resources/other_user.py b/src/specklepy/api/resources/other_user.py index 8496c794..a95f3700 100644 --- a/src/specklepy/api/resources/other_user.py +++ b/src/specklepy/api/resources/other_user.py @@ -58,7 +58,8 @@ def get(self, id: str) -> LimitedUser: def search( self, search_query: str, limit: int = 25 ) -> Union[List[LimitedUser], SpeckleException]: - """Searches for user by name or email. The search query must be at least 3 characters long + """Searches for user by name or email. The search query must be at least + 3 characters long Arguments: search_query {str} -- a string to search for @@ -104,23 +105,41 @@ def activity( cursor: Optional[datetime] = None, ) -> ActivityCollection: """ - Get the activity from a given stream in an Activity collection. Step into the activity `items` for the list of activity. + Get the activity from a given stream in an Activity collection. + Step into the activity `items` for the list of activity. - Note: all timestamps arguments should be `datetime` of any tz as they will be converted to UTC ISO format strings + Note: all timestamps arguments should be `datetime` of + any tz as they will be converted to UTC ISO format strings user_id {str} -- the id of the user to get the activity from - action_type {str} -- filter results to a single action type (eg: `commit_create` or `commit_receive`) + action_type {str} -- filter results to a single action type + (eg: `commit_create` or `commit_receive`) limit {int} -- max number of Activity items to return - before {datetime} -- latest cutoff for activity (ie: return all activity _before_ this time) - after {datetime} -- oldest cutoff for activity (ie: return all activity _after_ this time) + before {datetime} -- latest cutoff for activity + (ie: return all activity _before_ this time) + after {datetime} -- oldest cutoff for activity + (ie: return all activity _after_ this time) cursor {datetime} -- timestamp cursor for pagination """ query = gql( """ - query UserActivity($user_id: String!, $action_type: String, $before:DateTime, $after: DateTime, $cursor: DateTime, $limit: Int){ + query UserActivity( + $user_id: String!, + $action_type: String, + $before:DateTime, + $after: DateTime, + $cursor: DateTime, + $limit: Int + ){ otherUser(id: $user_id) { - activity(actionType: $action_type, before: $before, after: $after, cursor: $cursor, limit: $limit) { + activity( + actionType: $action_type, + before: $before, + after: $after, + cursor: $cursor, + limit: $limit + ) { totalCount cursor items { diff --git a/src/specklepy/api/resources/server.py b/src/specklepy/api/resources/server.py index c2d4163d..c7566cc4 100644 --- a/src/specklepy/api/resources/server.py +++ b/src/specklepy/api/resources/server.py @@ -66,8 +66,8 @@ 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 + 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( diff --git a/src/specklepy/api/resources/stream.py b/src/specklepy/api/resources/stream.py index a1306cf1..a6cd267f 100644 --- a/src/specklepy/api/resources/stream.py +++ b/src/specklepy/api/resources/stream.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import List, Optional +from typing import List from deprecated import deprecated from gql import gql @@ -346,8 +346,8 @@ def favorite(self, stream_id: str, favorited: bool = True): @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" + "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): @@ -370,11 +370,10 @@ def grant_permission(self, stream_id: str, user_id: str, role: str): 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." - ) + "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( @@ -482,7 +481,8 @@ def invite( 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" + "You must provide either an email or a user id to use the" + " `stream.invite` method" ) query = gql( @@ -533,7 +533,8 @@ def invite_batch( 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" + "You must provide either an email or a user id to use the" + " `stream.invite` method" ) query = gql( @@ -650,10 +651,9 @@ def update_permission(self, stream_id: str, user_id: str, role: str): 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." - ) + "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( """ @@ -764,7 +764,8 @@ def activity( } except AttributeError as e: raise SpeckleException( - "Could not get stream activity - `before`, `after`, and `cursor` must be in `datetime` format if provided", + "Could not get stream activity - `before`, `after`, and `cursor` must" + " be in `datetime` format if provided", ValueError, ) from e diff --git a/src/specklepy/api/resources/subscriptions.py b/src/specklepy/api/resources/subscriptions.py index 8bdf021b..9651a0fd 100644 --- a/src/specklepy/api/resources/subscriptions.py +++ b/src/specklepy/api/resources/subscriptions.py @@ -1,5 +1,5 @@ from functools import wraps -from typing import Callable, Dict, List, Union +from typing import Callable, Dict, List, Optional, Union from gql import gql from graphql import DocumentNode @@ -36,11 +36,13 @@ def __init__(self, account, basepath, client) -> None: ) @check_wsclient - async def stream_added(self, callback: Callable = None): - """Subscribes to new stream added event for your profile. Use this to display an up-to-date list of streams. + async def stream_added(self, callback: Optional[Callable] = None): + """Subscribes to new stream added event for your profile. + Use this to display an up-to-date list of streams. Arguments: - callback {Callable[Stream]} -- a function that takes the updated stream as an argument and executes each time a stream is added + callback {Callable[Stream]} -- a function that takes the updated stream + as an argument and executes each time a stream is added Returns: Stream -- the update stream @@ -55,12 +57,16 @@ async def stream_added(self, callback: Callable = None): ) @check_wsclient - async def stream_updated(self, id: str, callback: Callable = None): - """Subscribes to stream updated event. Use this in clients/components that pertain only to this stream. + async def stream_updated(self, id: str, callback: Optional[Callable] = None): + """ + Subscribes to stream updated event. + Use this in clients/components that pertain only to this stream. Arguments: id {str} -- the stream id of the stream to subscribe to - callback {Callable[Stream]} -- a function that takes the updated stream as an argument and executes each time the stream is updated + callback {Callable[Stream]} + -- a function that takes the updated stream + as an argument and executes each time the stream is updated Returns: Stream -- the update stream @@ -81,11 +87,17 @@ async def stream_updated(self, id: str, callback: Callable = None): ) @check_wsclient - async def stream_removed(self, callback: Callable = None): - """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. + async def stream_removed(self, callback: Optional[Callable] = None): + """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. Arguments: - callback {Callable[Dict]} -- a function that takes the returned dict as an argument and executes each time a stream is removed + callback {Callable[Dict]} + -- a function that takes the returned dict as an argument + and executes each time a stream is removed Returns: dict -- dict containing 'id' of stream removed and optionally 'revokedBy' @@ -107,9 +119,9 @@ async def stream_removed(self, callback: Callable = None): async def subscribe( self, query: DocumentNode, - params: Dict = None, - callback: Callable = None, - return_type: Union[str, List] = None, + params: Optional[Dict] = None, + callback: Optional[Callable] = None, + return_type: Optional[Union[str, List]] = None, schema=None, parse_response: bool = True, ): diff --git a/src/specklepy/api/resources/user.py b/src/specklepy/api/resources/user.py index 4e234173..602a3708 100644 --- a/src/specklepy/api/resources/user.py +++ b/src/specklepy/api/resources/user.py @@ -12,7 +12,10 @@ NAME = "user" DEPRECATION_VERSION = "2.9.0" -DEPRECATION_TEXT = "The user resource is deprecated, please use the active_user or other_user resources" +DEPRECATION_TEXT = ( + "The user resource is deprecated, please use the active_user or other_user" + " resources" +) class Resource(ResourceBase): @@ -137,7 +140,9 @@ def update( if not params["user"]: return SpeckleException( - message="You must provide at least one field to update your user profile" + message=( + "You must provide at least one field to update your user profile" + ) ) return self.make_request( diff --git a/src/specklepy/api/wrapper.py b/src/specklepy/api/wrapper.py index d6e3b018..b2074c6c 100644 --- a/src/specklepy/api/wrapper.py +++ b/src/specklepy/api/wrapper.py @@ -14,11 +14,15 @@ class StreamWrapper: """ - The `StreamWrapper` gives you some handy helpers to deal with urls and get authenticated clients and transports. - - Construct a `StreamWrapper` with a stream, branch, commit, or object URL. The corresponding ids will be stored - in the wrapper. If you have local accounts on the machine, you can use the `get_account` and `get_client` methods - to get a local account for the server. You can also pass a token into `get_client` if you don't have a corresponding + The `StreamWrapper` gives you some handy helpers to deal with urls and + get authenticated clients and transports. + + Construct a `StreamWrapper` with a stream, branch, commit, or object URL. + The corresponding ids will be stored + in the wrapper. If you have local accounts on the machine, + you can use the `get_account` and `get_client` methods + to get a local account for the server. You can also pass a token into `get_client` + if you don't have a corresponding local account for the server. ```py @@ -46,7 +50,10 @@ class StreamWrapper: _account: Account = None def __repr__(self): - return f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type: {self.type} )" + return ( + f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type:" + f" {self.type} )" + ) def __str__(self) -> str: return self.__repr__() @@ -72,7 +79,8 @@ def __init__(self, url: str) -> None: if not segments or len(segments) < 2: raise SpeckleException( - f"Cannot parse {url} into a stream wrapper class - invalid URL provided." + f"Cannot parse {url} into a stream wrapper class - invalid URL" + " provided." ) while segments: @@ -91,7 +99,8 @@ def __init__(self, url: str) -> None: self.commit_id = segments.pop(0) else: raise SpeckleException( - f"Cannot parse {url} into a stream wrapper class - invalid URL provided." + f"Cannot parse {url} into a stream wrapper class - invalid URL" + " provided." ) if not self.stream_id: @@ -105,7 +114,8 @@ def server_url(self): def get_account(self, token: str = None) -> Account: """ - Gets an account object for this server from the local accounts db (added via Speckle Manager or a json file) + Gets an account object for this server from the local accounts db + (added via Speckle Manager or a json file) """ if self._account and self._account.token: return self._account @@ -129,14 +139,18 @@ def get_account(self, token: str = None) -> Account: def get_client(self, token: str = None) -> SpeckleClient: """ - Gets an authenticated client for this server. You may provide a token if there aren't any local accounts on this - machine. If no account is found and no token is provided, an unauthenticated client is returned. + Gets an authenticated client for this server. + You may provide a token if there aren't any local accounts on this + machine. If no account is found and no token is provided, + an unauthenticated client is returned. Arguments: - token {str} -- optional token if no local account is available (defaults to None) + token {str} + -- optional token if no local account is available (defaults to None) Returns: - SpeckleClient -- authenticated with a corresponding local account or the provided token + SpeckleClient + -- authenticated with a corresponding local account or the provided token """ if self._client and token is None: return self._client @@ -160,11 +174,14 @@ def get_client(self, token: str = None) -> SpeckleClient: def get_transport(self, token: str = None) -> ServerTransport: """ - Gets a server transport for this stream using an authenticated client. If there is no local account for this - server and the client was not authenticated with a token, this will throw an exception. + Gets a server transport for this stream using an authenticated client. + If there is no local account for this + server and the client was not authenticated with a token, + this will throw an exception. Returns: - ServerTransport -- constructed for this stream with a pre-authenticated client + ServerTransport -- constructed for this stream + with a pre-authenticated client """ if not self._account or not self._account.token: self.get_account(token) diff --git a/src/specklepy/logging/exceptions.py b/src/specklepy/logging/exceptions.py index 04eb452c..bf7ecefa 100644 --- a/src/specklepy/logging/exceptions.py +++ b/src/specklepy/logging/exceptions.py @@ -13,9 +13,11 @@ def __str__(self) -> str: 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}).", + message=( + "Invalid units: expected type str but received" + f" {type(invalid_unit)} ({invalid_unit})." + ), exception=None, ) @@ -27,7 +29,10 @@ def __init__(self, message: str, obj: Any, exception: Exception = None) -> None: self.unhandled_type = type(obj) def __str__(self) -> str: - return f"SpeckleException: Could not serialize object of type {self.unhandled_type}" + return ( + "SpeckleException: Could not serialize object of type" + f" {self.unhandled_type}" + ) class GraphQLException(SpeckleException): diff --git a/src/specklepy/logging/metrics.py b/src/specklepy/logging/metrics.py index fc197aaf..56e9302c 100644 --- a/src/specklepy/logging/metrics.py +++ b/src/specklepy/logging/metrics.py @@ -58,7 +58,11 @@ def set_host_app(host_app: str, host_app_version: Optional[str] = None): HOST_APP_VERSION = host_app_version or HOST_APP_VERSION -def track(action: str, account: "Account" = None, custom_props: Optional[dict] = None): +def track( + action: str, + account: Optional["Account"] = None, + custom_props: Optional[dict] = None, +): if not TRACK: return try: @@ -84,7 +88,7 @@ def track(action: str, account: "Account" = None, custom_props: Optional[dict] = LOG.debug(f"Error queueing metrics request: {str(ex)}") -def initialise_tracker(account: "Account" = None): +def initialise_tracker(account: Optional["Account"] = None): global METRICS_TRACKER if not METRICS_TRACKER: METRICS_TRACKER = MetricsTracker() diff --git a/src/specklepy/objects/base.py b/src/specklepy/objects/base.py index 6e5ac0ab..11150682 100644 --- a/src/specklepy/objects/base.py +++ b/src/specklepy/objects/base.py @@ -149,7 +149,7 @@ def __init_subclass__( raise ValueError( f"The speckle_type: {speckle_type} is already registered for type: " f"{cls._type_registry[cls.speckle_type].__name__}. " - f"Please choose a different type name." + "Please choose a different type name." ) cls._type_registry[cls.speckle_type] = cls # type: ignore try: @@ -334,11 +334,14 @@ def __setattr__(self, name: str, value: Any) -> None: @classmethod def update_forward_refs(cls) -> None: """ - Attempts to populate the internal defined types dict for type checking sometime after defining the class. - This is already done when defining the class, but can be called again if references to undefined types were + Attempts to populate the internal defined types dict for type checking + sometime after defining the class. + This is already done when defining the class, but can be called + again if references to undefined types were included. - See `objects.geometry` for an example of how this is used with the Brep class definitions + See `objects.geometry` for an example of how this is used with + the Brep class definitions. """ try: cls._attr_types = get_type_hints(cls) @@ -387,7 +390,8 @@ def add_chunkable_attrs(self, **kwargs: int) -> None: Mark defined attributes as chunkable for serialisation Arguments: - kwargs {int} -- the name of the attribute as the keyword and the chunk size as the arg + kwargs {int} -- the name of the attribute as the keyword + and the chunk size as the arg """ chunkable = {k: v for k, v in kwargs.items() if isinstance(v, int)} self._chunkable = dict(self._chunkable, **chunkable) @@ -397,7 +401,7 @@ def add_detachable_attrs(self, names: Set[str]) -> None: Mark defined attributes as detachable for serialisation Arguments: - names {Set[str]} -- the names of the attributes to detach as a set of strings + names {Set[str]} -- the names of the attributes to detach as a set of string """ self._detachable = self._detachable.union(names) @@ -409,7 +413,7 @@ def units(self) -> Union[str, None]: @units.setter def units(self, value: Union[str, Units, None]): - if value == None: + if value is None: units = value elif isinstance(value, Units): units: Units = value @@ -448,13 +452,17 @@ def get_children_count(self) -> int: def get_id(self, decompose: bool = False) -> str: """ - Gets the id (a unique hash) of this object. ⚠️ This method fully serializes the object which, - in the case of large objects (with many sub-objects), has a tangible cost. Avoid using it! + Gets the id (a unique hash) of this object. + ⚠️ This method fully serializes the object which, + in the case of large objects (with many sub-objects), has a tangible cost. + Avoid using it! - Note: the hash of a decomposed object differs from that of a non-decomposed object + Note: the hash of a decomposed object differs from that of a + non-decomposed object Arguments: - decompose {bool} -- if True, will decompose the object in the process of hashing it + decompose {bool} -- if True, will decompose the object in + the process of hashing it Returns: str -- the hash (id) of the fully serialized object diff --git a/src/specklepy/objects/geometry.py b/src/specklepy/objects/geometry.py index 78d9a349..469cc65e 100644 --- a/src/specklepy/objects/geometry.py +++ b/src/specklepy/objects/geometry.py @@ -1,4 +1,4 @@ -from enum import Enum, IntEnum, auto +from enum import Enum from typing import Any, List, Optional from specklepy.objects.base import Base @@ -15,11 +15,17 @@ class Point(Base, speckle_type=GEOMETRY + "Point"): z: float = 0.0 def __repr__(self) -> str: - return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, id: {self.id}, speckle_type: {self.speckle_type})" + return ( + f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, id:" + f" {self.id}, speckle_type: {self.speckle_type})" + ) @classmethod def from_list(cls, args: List[float]) -> "Point": - """Create a new Point from a list of three floats representing the x, y, and z coordinates""" + """ + Create a new Point from a list of three floats + representing the x, y, and z coordinates + """ return cls(x=args[0], y=args[1], z=args[2]) def to_list(self) -> List[Any]: diff --git a/src/specklepy/objects/other.py b/src/specklepy/objects/other.py index 705e05d2..9ba3921a 100644 --- a/src/specklepy/objects/other.py +++ b/src/specklepy/objects/other.py @@ -77,7 +77,8 @@ class Transform( """The 4x4 transformation matrix The 3x3 sub-matrix determines scaling. - The 4th column defines translation, where the last value is a divisor (usually equal to 1). + The 4th column defines translation, + where the last value is a divisor (usually equal to 1). """ _value: Optional[List[float]] = None @@ -93,12 +94,14 @@ def value(self, value: List[float]) -> None: value = [float(x) for x in value] except (ValueError, TypeError) as error: raise ValueError( - f"Could not create a Transform object with the requested value. Input must be a 16 element list of numbers. Value provided: {value}" + "Could not create a Transform object with the requested value. Input" + f" must be a 16 element list of numbers. Value provided: {value}" ) from error if len(value) != 16: raise ValueError( - f"Could not create a Transform object: input list should be 16 floats long, but was {len(value)} long" + "Could not create a Transform object: input list should be 16 floats" + f" long, but was {len(value)} long" ) self._value = value @@ -163,14 +166,16 @@ def apply_to_points_values(self, points_value: List[float]) -> List[float]: """Transform a list of speckle Points Arguments: - points {List[float]} -- a flat list of floats representing points to transform + points {List[float]} + -- a flat list of floats representing points to transform Returns: List[float] -- a new transformed list """ if len(points_value) % 3 != 0: raise ValueError( - "Cannot apply transform as the points list is malformed: expected length to be multiple of 3" + "Cannot apply transform as the points list is malformed: expected" + " length to be multiple of 3" ) transformed = [] for i in range(0, len(points_value), 3): @@ -207,11 +212,13 @@ def apply_to_vector_value(self, vector_value: List[float]) -> List[float]: ][:3] @classmethod - def from_list(cls, value: List[float] = None) -> "Transform": - """Returns a Transform object from a list of 16 numbers. If no value is provided, an identity transform will be returned. + def from_list(cls, value: Optional[List[float]] = None) -> "Transform": + """Returns a Transform object from a list of 16 numbers. + If no value is provided, an identity transform will be returned. Arguments: - value {List[float]} -- the matrix as a flat list of 16 numbers (defaults to the identity transform) + value {List[float]} -- the matrix as a flat list of 16 numbers + (defaults to the identity transform) Returns: Transform -- a complete transform object diff --git a/src/specklepy/objects/structural/loading.py b/src/specklepy/objects/structural/loading.py index 95d92fdc..9e6f1e59 100644 --- a/src/specklepy/objects/structural/loading.py +++ b/src/specklepy/objects/structural/loading.py @@ -9,7 +9,6 @@ class LoadType(int, Enum): - none = 0 Dead = 1 SuperDead = 2 @@ -32,7 +31,6 @@ class LoadType(int, Enum): class ActionType(int, Enum): - none = 0 Permanent = 1 Variable = 2 @@ -40,7 +38,6 @@ class ActionType(int, Enum): class BeamLoadType(int, Enum): - Point = 0 Uniform = 1 Linear = 2 @@ -49,21 +46,18 @@ class BeamLoadType(int, Enum): class FaceLoadType(int, Enum): - Constant = 0 Variable = 1 Point = 2 class LoadDirection2D(int, Enum): - X = 0 Y = 1 Z = 2 class LoadDirection(int, Enum): - X = 0 Y = 1 Z = 2 @@ -81,7 +75,6 @@ class LoadAxisType(int, Enum): class CombinationType(int, Enum): - LinearAdd = 0 Envelope = 1 AbsoluteAdd = 2 diff --git a/src/specklepy/objects/structural/properties.py b/src/specklepy/objects/structural/properties.py index a3e3138e..2d10780c 100644 --- a/src/specklepy/objects/structural/properties.py +++ b/src/specklepy/objects/structural/properties.py @@ -38,7 +38,6 @@ class ReferenceSurface(int, Enum): class PropertyType2D(int, Enum): - Stress = 0 Fabric = 1 Plate = 2 diff --git a/src/specklepy/serialization/base_object_serializer.py b/src/specklepy/serialization/base_object_serializer.py index 7d4f1548..acf11d72 100644 --- a/src/specklepy/serialization/base_object_serializer.py +++ b/src/specklepy/serialization/base_object_serializer.py @@ -2,15 +2,13 @@ import re import warnings from enum import Enum -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Optional, Tuple from uuid import uuid4 from warnings import warn import ujson # import for serialization -import specklepy.objects.geometry -import specklepy.objects.other from specklepy.logging.exceptions import SpeckleException, SpeckleWarning from specklepy.objects.base import Base, DataChunk from specklepy.transports.abstract_transport import AbstractTransport @@ -29,7 +27,8 @@ def safe_json_loads(obj: str, obj_id=None) -> Any: import json warn( - f"Failed to deserialise object (id: {obj_id}). This is likely a ujson big int error - falling back to json. \nError: {err}", + f"Failed to deserialise object (id: {obj_id}). This is likely a ujson big" + f" int error - falling back to json. \nError: {err}", SpeckleWarning, ) return json.loads(obj) @@ -47,7 +46,9 @@ class BaseObjectSerializer: ] # holds deserialized objects so objects with same id return the same instance def __init__( - self, write_transports: List[AbstractTransport] = None, read_transport=None + self, + write_transports: Optional[List[AbstractTransport]] = None, + read_transport: Optional[AbstractTransport] = None, ) -> None: self.write_transports = write_transports or [] self.read_transport = read_transport @@ -63,7 +64,8 @@ def write_json(self, base: Base): base {Base} -- the base object to be decomposed and serialized Returns: - (str, str) -- a tuple containing the object 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 """ obj_id, obj = self.traverse_base(base) @@ -77,7 +79,8 @@ def traverse_base(self, base: Base) -> Tuple[str, Dict]: base {Base} -- the base object to be decomposed and serialized Returns: - (str, dict) -- a tuple containing the object 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() @@ -247,16 +250,19 @@ def traverse_value(self, obj: Any, detach: bool = False) -> Any: else: try: return obj.dict() - except: + except Exception: warn( - f"Failed to handle {type(obj)} in `BaseObjectSerializer.traverse_value`", + f"Failed to handle {type(obj)} in" + " `BaseObjectSerializer.traverse_value`", SpeckleWarning, ) return str(obj) 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 + """ + 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_id {str} -- the id of the fully traversed object @@ -279,7 +285,10 @@ def detach_helper(self, ref_id: str) -> Dict[str, str]: } def __reset_writer(self) -> None: - """Reinitializes the lineage, and other variables that get used during the json writing process""" + """ + Reinitializes the lineage, and other variables that get used during the json + writing process + """ self.detach_lineage = [True] self.lineage = [] self.family_tree = {} @@ -356,7 +365,8 @@ def recompose_base(self, obj: dict) -> Base: base.__setattr__(prop, self.recompose_base(obj=ref_obj)) else: warnings.warn( - f"Could not find the referenced child object of id `{ref_id}` in the given read transport: {self.read_transport.name}", + f"Could not find the referenced child object of id `{ref_id}`" + f" in the given read transport: {self.read_transport.name}", SpeckleWarning, ) base.__setattr__(prop, self.handle_value(value)) @@ -371,7 +381,8 @@ def recompose_base(self, obj: dict) -> Base: return base def handle_value(self, obj: Any): - """Helper for recomposing a base object by handling the dictionary representation's values + """Helper for recomposing a base object by handling the dictionary + representation's values Arguments: obj {Any} -- a value from the base object dictionary @@ -417,7 +428,8 @@ def get_child(self, obj: Dict): 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_id}` in the given read transport: {self.read_transport.name}", + f"Could not find the referenced child object of id `{ref_id}` in the" + f" given read transport: {self.read_transport.name}", SpeckleWarning, ) return obj diff --git a/src/specklepy/transports/server/__init__.py b/src/specklepy/transports/server/__init__.py index 60bf56d1..2eb5762e 100644 --- a/src/specklepy/transports/server/__init__.py +++ b/src/specklepy/transports/server/__init__.py @@ -1 +1 @@ -from .server import ServerTransport +from specklepy.transports.server.server import ServerTransport diff --git a/src/specklepy/transports/server/batch_sender.py b/src/specklepy/transports/server/batch_sender.py index 55c7f2a0..45daf1a5 100644 --- a/src/specklepy/transports/server/batch_sender.py +++ b/src/specklepy/transports/server/batch_sender.py @@ -108,7 +108,8 @@ def _bg_send_batch(self, session, batch): if not new_objects: LOG.info( - f"Uploading batch of {len(batch)} objects: all objects are already in the server" + f"Uploading batch of {len(batch)} objects: all objects are already in" + " the server" ) return @@ -127,11 +128,16 @@ def _bg_send_batch(self, session, batch): if r.status_code != 201: LOG.warning("Upload server response: %s", r.text) raise SpeckleException( - message=f"Could not save the object to the server - status code {r.status_code}" + message=( + "Could not save the object to the server - status code" + f" {r.status_code}" + ) ) except json.JSONDecodeError as error: return SpeckleException( - f"Failed to send objects to {self.server_url}. Please ensure this stream ({self.stream_id}) exists on this server and that you have permission to send to it.", + f"Failed to send objects to {self.server_url}. Please ensure this" + f" stream ({self.stream_id}) exists on this server and that you have" + " permission to send to it.", error, ) diff --git a/src/specklepy/transports/server/server.py b/src/specklepy/transports/server/server.py index 805799fd..7ea2e713 100644 --- a/src/specklepy/transports/server/server.py +++ b/src/specklepy/transports/server/server.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional from warnings import warn import requests @@ -14,10 +14,10 @@ class ServerTransport(AbstractTransport): """ - The `ServerTransport` is the vehicle through which you transport objects to and from a Speckle Server. Provide it to - `operations.send()` or `operations.receive()`. + The `ServerTransport` is the vehicle through which you transport objects to and + from a Speckle Server. Provide it to `operations.send()` or `operations.receive()`. - The `ServerTransport` can be authenticted two different ways: + The `ServerTransport` can be authenticated two different ways: 1. by providing a `SpeckleClient` 2. by providing an `Account` 3. by providing a `token` and `url` @@ -29,14 +29,15 @@ class ServerTransport(AbstractTransport): # here's the data you want to send block = Block(length=2, height=4) - # next create the server transport - this is the vehicle through which you will send and receive + # next create the server transport - this is the vehicle through which + # you will send and receive transport = ServerTransport(stream_id=new_stream_id, client=client) # this serialises the block and sends it to the transport hash = operations.send(base=block, transports=[transport]) # you can now create a commit on your stream with this object - commid_id = client.commit.create( + commit_id = client.commit.create( stream_id=new_stream_id, obj_id=hash, message="this is a block I made in speckle-py", @@ -45,25 +46,26 @@ class ServerTransport(AbstractTransport): """ _name = "RemoteTransport" - url: str = None - stream_id: str = None - account: Account = None + url: Optional[str] = None + stream_id: Optional[str] = None + account: Optional[Account] = None saved_obj_count: int = 0 - session: requests.Session = None + session: Optional[requests.Session] = None def __init__( self, stream_id: str, - client: SpeckleClient = None, - account: Account = None, - token: str = None, - url: str = None, + client: Optional[SpeckleClient] = None, + account: Optional[Account] = None, + token: Optional[str] = None, + url: Optional[str] = None, **data: Any, ) -> None: super().__init__(**data) if client is None and account is None and token is None and url is None: raise SpeckleException( - "You must provide either a client or a token and url to construct a ServerTransport." + "You must provide either a client or a token and url to construct a" + " ServerTransport." ) if account: @@ -74,7 +76,8 @@ def __init__( if not client.account.token: warn( SpeckleWarning( - f"Unauthenticated Speckle Client provided to Server Transport for {self.url}. Receiving from private streams will fail." + "Unauthenticated Speckle Client provided to Server Transport" + f" for {self.url}. Receiving from private streams will fail." ) ) else: @@ -118,8 +121,10 @@ def get_object(self, id: str) -> str: # return obj raise SpeckleException( - "Getting a single object using `ServerTransport.get_object()` is not implemented. To get an object from the server, please use the `SpeckleClient.object.get()` route", - NotImplementedError, + "Getting a single object using `ServerTransport.get_object()` is not" + " implemented. To get an object from the server, please use the" + " `SpeckleClient.object.get()` route", + NotImplementedError(), ) def has_objects(self, id_list: List[str]) -> Dict[str, bool]: @@ -134,7 +139,8 @@ def copy_object_and_children( if r.status_code != 200: raise SpeckleException( - f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})" + f"Can't get object {self.stream_id}/{id}: HTTP error" + f" {r.status_code} ({r.text[:1000]})" ) root_obj_serialized = r.text root_obj = json.loads(root_obj_serialized) diff --git a/src/specklepy/transports/sqlite.py b/src/specklepy/transports/sqlite.py index d79b526f..d0e67a07 100644 --- a/src/specklepy/transports/sqlite.py +++ b/src/specklepy/transports/sqlite.py @@ -10,21 +10,21 @@ class SQLiteTransport(AbstractTransport): _name = "SQLite" - _base_path: str = None - _root_path: str = None - __connection: sqlite3.Connection = None + _base_path: Optional[str] = None + _root_path: Optional[str] = None + __connection: Optional[sqlite3.Connection] = None app_name: str = "" scope: str = "" saved_obj_count: int = 0 - max_size: int = None - _current_batch: List[Tuple[str, str]] = None - _current_batch_size: int = None + max_size: Optional[int] = None + _current_batch: Optional[List[Tuple[str, str]]] = None + _current_batch_size: Optional[int] = None def __init__( self, base_path: Optional[str] = None, app_name: Optional[str] = None, - scope: str = None, + scope: Optional[str] = None, max_batch_size_mb: float = 10.0, **data: Any, ) -> None: @@ -45,7 +45,9 @@ def __init__( self.__initialise() except Exception as ex: raise SpeckleException( - f"SQLiteTransport could not initialise {self.scope}.db at {self._base_path}. Either provide a different `base_path` or use an alternative transport.", + f"SQLiteTransport could not initialise {self.scope}.db at" + f" {self._base_path}. Either provide a different `base_path` or use an" + " alternative transport.", ex, ) @@ -79,14 +81,16 @@ def save_object_from_transport( Arguments: id {str} -- the object id - source_transport {AbstractTransport) -- the transport through which the object can be found + source_transport {AbstractTransport) + -- the transport through which the object can be found """ serialized_object = source_transport.get_object(id) self.save_object(id, serialized_object) def save_object(self, id: str, serialized_object: str) -> None: """ - Adds an object to the current batch to be written to the db. If the current batch is full, + Adds an object to the current batch to be written to the db. + If the current batch is full, the batch is written to the db and the current batch is reset. Arguments: @@ -118,7 +122,8 @@ def save_current_batch(self) -> None: self.__connection.commit() except Exception as ex: raise SpeckleException( - f"Could not save the batch of objects to the local db. Inner exception: {ex}", + "Could not save the batch of objects to the local db. Inner exception:" + f" {ex}", ex, ) @@ -157,7 +162,10 @@ def copy_object_and_children( raise NotImplementedError def get_all_objects(self): - """Returns all the objects in the store. NOTE: do not use for large collections!""" + """ + Returns all the objects in the store. + NOTE: do not use for large collections! + """ self.__check_connection() with closing(self.__connection.cursor()) as c: rows = c.execute("SELECT * FROM objects").fetchall() diff --git a/tests/test_base.py b/tests/test_base.py index dd667a92..f653a3a4 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -13,11 +13,11 @@ @pytest.mark.parametrize( "invalid_prop_name", [ - (""), - ("@"), - ("@@wow"), - ("this.is.bad"), - ("super/bad"), + "", + "@", + "@@wow", + "this.is.bad", + "super/bad", ], ) def test_empty_prop_names(invalid_prop_name: str) -> None: @@ -95,7 +95,7 @@ def test_setting_units(): assert b.units == "ft" b.units = None # None should be a valid arg - assert b.units == None + assert b.units is None b.units = Units.none assert b.units == "none" diff --git a/tests/test_commit.py b/tests/test_commit.py index f9347e2e..9ce3c815 100644 --- a/tests/test_commit.py +++ b/tests/test_commit.py @@ -85,4 +85,4 @@ def test_commit_marked_as_received(self, client, stream, mesh) -> None: message="testing received", ) - assert commit_marked_received == True + assert commit_marked_received is True diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 9c63cfdd..6b3637b3 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -517,8 +517,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) + bad_mesh = Mesh.create(vertices=7, faces=faces) # noqa: F841 assert mesh.vertices == vertices assert mesh.textureCoordinates == [] diff --git a/tests/test_other_user.py b/tests/test_other_user.py index bd171477..106e9641 100644 --- a/tests/test_other_user.py +++ b/tests/test_other_user.py @@ -2,7 +2,6 @@ from specklepy.api.client import SpeckleClient from specklepy.api.models import Activity, ActivityCollection, LimitedUser -from specklepy.logging.exceptions import SpeckleException @pytest.mark.run(order=4) diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 474bc70b..6f444795 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -6,7 +6,6 @@ from specklepy.objects import Base from specklepy.objects.fakemesh import FakeMesh from specklepy.objects.geometry import Point -from specklepy.serialization.base_object_serializer import BaseObjectSerializer from specklepy.transports.memory import MemoryTransport from specklepy.transports.server import ServerTransport diff --git a/tests/test_stream.py b/tests/test_stream.py index 3111a6b7..b9e06153 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -1,5 +1,3 @@ -from datetime import datetime - import pytest from specklepy.api.client import SpeckleClient diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 2e8c8788..8a8fc20b 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -4,12 +4,7 @@ from specklepy.api import operations from specklepy.objects.geometry import Point, Vector -from specklepy.objects.other import ( - IDENTITY_TRANSFORM, - BlockDefinition, - BlockInstance, - Transform, -) +from specklepy.objects.other import Transform @pytest.fixture() @@ -81,7 +76,7 @@ def test_point_transform(point: Point, transform: Transform): def test_points_transform(points: List[Point], transform: Transform): new_points = transform.apply_to_points(points) - for (i, new_point) in enumerate(new_points): + for i, new_point in enumerate(new_points): assert new_point.x == points[i].x + 1 assert new_point.y == points[i].y + 2 assert new_point.z == points[i].z * 0.5 diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 5099110c..6129ea4d 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -1,11 +1,11 @@ import json from pathlib import Path +from typing import Generator, Iterable import pytest from specklepy.api.wrapper import StreamWrapper from specklepy.paths import accounts_path -from specklepy.transports.sqlite import SQLiteTransport def test_parse_stream(): @@ -60,7 +60,8 @@ def test_parse_globals_as_commit(): assert wrap.type == "commit" -#! NOTE: the following three tests may not pass locally if you have a `speckle.xyz` account in manager +#! NOTE: the following three tests may not pass locally +# if you have a `speckle.xyz` account in manager def test_get_client_without_auth(): wrap = StreamWrapper("https://speckle.xyz/streams/4c3ce1459c/commits/8b9b831792") client = wrap.get_client() @@ -88,16 +89,16 @@ def test_get_transport_with_token(): @pytest.fixture -def user_path() -> Path: +def user_path() -> Iterable[Path]: path = accounts_path().joinpath("test_acc.json") # hey, py37 doesn't support the missing_ok argument try: path.unlink() - except: + except Exception: pass try: path.unlink(missing_ok=True) - except: + except Exception: pass path.parent.absolute().mkdir(exist_ok=True) yield path