Skip to content

Commit 49c354f

Browse files
authored
[RSDK-878] Typechecking (#512)
1 parent 2e91644 commit 49c354f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1642
-1031
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
push:
1010
branches: [main]
1111
pull_request_target:
12-
branches: [main, 'rc-*']
12+
branches: [main, "rc-*"]
1313

1414
jobs:
1515
test:
@@ -44,6 +44,9 @@ jobs:
4444
run: poetry run pip install -r requirements-test.txt
4545
if: ${{ matrix.requirements-version == 'min' }}
4646

47+
- name: Type Check
48+
run: make typecheck
49+
4750
- name: Lint
4851
run: make lint
4952

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ _format:
1414
format:
1515
poetry run $(MAKE) _format
1616

17+
_typecheck:
18+
pyright
19+
20+
typecheck:
21+
poetry run $(MAKE) _typecheck
22+
1723
_buf: clean
1824
rm -rf src/viam/gen
1925
buf generate buf.build/viamrobotics/api

poetry.lock

Lines changed: 888 additions & 773 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,34 @@ include = ["LICENSE", "src/viam/rpc/libviam_rust_utils.*"]
2323
numpy = { version = ">=1.21,<2", optional = true }
2424

2525
[tool.poetry.group.dev.dependencies]
26-
pytest = "^7.4.2"
27-
pytest-asyncio = "^0.21.1"
26+
pytest = "^7.4.3"
27+
pytest-asyncio = "^0.23.2"
2828
coverage = "^7.3.2"
2929
protoletariat = "^3.2.19"
3030
jupyter = "^1.0.0"
3131
flake8 = "^6.1.0"
32-
myst-nb = "^0.17.2"
33-
sphinx-autoapi = "^2.1.1"
34-
sphinx-rtd-theme = "^1.3.0"
32+
myst-nb = [
33+
{version = "<1.0.0", python = "<3.9"},
34+
{version = ">=1.0.0", python = ">=3.9"},
35+
]
36+
sphinx-autoapi = [
37+
{version = "<3.0.0", python = "<3.9"},
38+
{version = ">=3.0.0", python = ">=3.9"},
39+
]
40+
sphinx-rtd-theme = "^2.0.0"
3541
autopep8 = "^2.0.4"
3642
black = {extras = ["jupyter"], version = "^23.12.0"}
37-
nbmake = "^1.4.5"
38-
types-pillow = "^10.0.0.3"
43+
nbmake = "^1.4.6"
44+
types-pillow = "^10.1.0.2"
3945
mypy-protobuf = "^3.5.0"
40-
tox = "^4.11.3"
46+
tox = "^4.11.4"
4147
isort = "^5.12.0"
4248
pytest-watcher = "^0.3.4"
43-
# TODO(RSDK-5450) - Update myst-nb so this can be unpinned.
44-
astroid = "<=2.15.6"
4549
numpy = [
4650
{version = "<1.25.0", python = "<3.9"},
47-
{version = ">=1.25.0", python = ">=3.9"},
51+
{version = ">=1.26.2", python = ">=3.9"},
4852
]
53+
pyright = "^1.1.339"
4954

5055
[tool.pytest.ini_options]
5156
addopts = "-ra"
@@ -72,3 +77,7 @@ build-backend = "poetry.core.masonry.api"
7277

7378
[tool.poetry.extras]
7479
mlmodel = ["numpy"]
80+
81+
[tool.pyright]
82+
include = [ "src" ]
83+
exclude = [ "**/gen", "**/proto" ]

src/viam/__init__.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ def _log_exceptions(exctype, value, traceback):
4141
##################
4242
# MONKEY PATCHES #
4343
##################
44-
_ResourceName.__str__ = lambda self: f"{self.namespace}:{self.type}:{self.subtype}/{self.name}"
45-
_ResourceName.__repr__ = lambda self: f"<viam.proto.common.ResourceName {str(self)} at {hex(id(self))}>"
46-
_ResourceName.__hash__ = lambda self: hash(str(self))
44+
def _rname_str(self: _ResourceName) -> str:
45+
return f"{self.namespace}:{self.type}:{self.subtype}/{self.name}"
46+
47+
48+
_ResourceName.__str__ = _rname_str
49+
50+
51+
def _rname_repr(self: _ResourceName) -> str:
52+
return f"<viam.proto.common.ResourceName {str(self)} at {hex(id(self))}>"
53+
54+
55+
_ResourceName.__repr__ = _rname_repr
56+
57+
58+
def _rname_hash(self: _ResourceName) -> int:
59+
return hash(str(self))
60+
61+
62+
_ResourceName.__hash__ = _rname_hash # type: ignore

src/viam/app/_logs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ async def next(self) -> LogsType:
1717
async def close(self):
1818
...
1919

20-
def __aiter__(self):
21-
return self
20+
def __aiter__(self) -> AsyncIterator:
21+
...
2222

2323
async def __anext__(self) -> LogsType:
24-
return await self.next()
24+
...
2525

2626

2727
class _LogsStreamWithIterator(_LogsStream[LogsType]):

src/viam/app/app_client.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,8 @@ async def _create_authorization_for_new_api_key(self, auth: APIKeyAuthorization)
434434
return await self._create_authorization(
435435
identity_id="", # setting `identity_id` when creating an API key results in an error
436436
identity_type="api-key",
437-
role=auth._role,
438-
resource_type=auth._resource_type,
437+
role=auth._role, # type: ignore -- Ignoring because this is technically a `string`
438+
resource_type=auth._resource_type, # type: ignore -- Ignoring because this is technically a `string`
439439
resource_id=auth._resource_id,
440440
)
441441

@@ -1282,7 +1282,7 @@ async def list_authorizations(self, resource_ids: Optional[List[str]] = None) ->
12821282
response: ListAuthorizationsResponse = await self._app_client.ListAuthorizations(request, metadata=self._metadata)
12831283
return list(response.authorizations)
12841284

1285-
async def check_permissions(self, permissions: [List[AuthorizedPermissions]]) -> List[AuthorizedPermissions]:
1285+
async def check_permissions(self, permissions: List[AuthorizedPermissions]) -> List[AuthorizedPermissions]:
12861286
"""Checks validity of a list of permissions.
12871287
12881288
Args:
@@ -1369,9 +1369,10 @@ async def upload_module_file(self, module_file_info: Optional[ModuleFileInfo], f
13691369
async with self._app_client.UploadModuleFile.open(metadata=self._metadata) as stream:
13701370
await stream.send_message(request_module_file_info)
13711371
await stream.send_message(request_file, end=True)
1372-
response = await stream.recv_message()
1372+
response: Union[UploadModuleFileRequest, None] = await stream.recv_message()
13731373
if not response:
13741374
await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error.
1375+
raise TypeError("Response cannot be empty") # we should never get here, but for typechecking
13751376
return response.url
13761377

13771378
async def get_module(self, module_id: str) -> Module:
@@ -1418,8 +1419,8 @@ async def create_key(self, authorizations: List[APIKeyAuthorization], name: Opti
14181419
Tuple[str, str]: The api key and api key ID.
14191420
"""
14201421
name = name if name is not None else str(datetime.now())
1421-
authorizationspb = [await self._create_authorization_for_new_api_key(auth) for auth in authorizations]
1422-
request = CreateKeyRequest(authorizations=authorizationspb, name=name)
1422+
authorizations_pb = [await self._create_authorization_for_new_api_key(auth) for auth in authorizations]
1423+
request = CreateKeyRequest(authorizations=authorizations_pb, name=name)
14231424
response: CreateKeyResponse = await self._app_client.CreateKey(request, metadata=self._metadata)
14241425
return (response.key, response.id)
14251426

@@ -1447,4 +1448,4 @@ async def list_keys(self) -> List[APIKeyWithAuthorizations]:
14471448
org_id = await self._get_organization_id()
14481449
request = ListKeysRequest(org_id=org_id)
14491450
response: ListKeysResponse = await self._app_client.ListKeys(request, metadata=self._metadata)
1450-
return [key for key in response.api_keys]
1451+
return list(response.api_keys)

src/viam/app/billing_client.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Mapping, Optional
22

3-
from grpclib.client import Channel
3+
from grpclib.client import Channel, Stream
44

55
from viam import logging
66
from viam.proto.app.billing import (
@@ -60,11 +60,12 @@ async def get_invoice_pdf(self, invoice_id: str, org_id: str, dest: str, timeout
6060
org_id (str): the ID of the org to request data from
6161
dest (str): filepath to save the invoice to
6262
"""
63-
request = GetInvoicePdfRequest(id=invoice_id, org_id=org_id)
64-
response: GetInvoicePdfResponse = await self._billing_client.GetInvoicePdf(request, metadata=self._metadata, timeout=timeout)
65-
data: bytes = response[0].chunk
66-
with open(dest, "wb") as file:
67-
file.write(data)
63+
stream: Stream[GetInvoicePdfRequest, GetInvoicePdfResponse]
64+
async with self._billing_client.GetInvoicePdf.open(timeout=timeout, metadata=self._metadata) as stream:
65+
await stream.send_message(GetInvoicePdfRequest(id=invoice_id, org_id=org_id), end=True)
66+
with open(dest, "wb") as file:
67+
async for response in stream:
68+
file.write(response.chunk)
6869

6970
async def get_invoices_summary(self, org_id: str, timeout: Optional[float] = None) -> GetInvoicesSummaryResponse:
7071
"""Access total outstanding balance plus invoice summaries for a given org.

src/viam/app/data_client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ def __init__(self, data: Mapping[str, Any], metadata: CaptureMetadata, time_requ
9393
def __str__(self) -> str:
9494
return f"{self.data}\n{self.metadata}Time requested: {self.time_requested}\nTime received: {self.time_received}\n"
9595

96-
def __eq__(self, other: "DataClient.TabularData") -> bool:
97-
return str(self) == str(other)
96+
def __eq__(self, other: object) -> bool:
97+
if isinstance(other, DataClient.TabularData):
98+
return str(self) == str(other)
99+
return False
98100

99101
class BinaryData:
100102
"""Class representing a piece of binary data and associated metadata.
@@ -114,8 +116,10 @@ def __init__(self, data: bytes, metadata: BinaryMetadata) -> None:
114116
def __str__(self) -> str:
115117
return f"{self.data}\n{self.metadata}"
116118

117-
def __eq__(self, other: "DataClient.BinaryData") -> bool:
118-
return str(self) == str(other)
119+
def __eq__(self, other: object) -> bool:
120+
if isinstance(other, DataClient.BinaryData):
121+
return str(self) == str(other)
122+
return False
119123

120124
def __init__(self, channel: Channel, metadata: Mapping[str, str]):
121125
"""Create a `DataClient` that maintains a connection to app.

src/viam/components/arm/arm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Arm(ComponentBase):
1616
overridden, it must call the ``super().__init__()`` function.
1717
"""
1818

19-
SUBTYPE: Final = Subtype(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm")
19+
SUBTYPE: Final = Subtype(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm") # pyright: ignore [reportIncompatibleVariableOverride]
2020

2121
@abc.abstractmethod
2222
async def get_end_position(

0 commit comments

Comments
 (0)