Skip to content

Commit 1b8abf7

Browse files
committed
pyright strict
1 parent a3e66cc commit 1b8abf7

File tree

20 files changed

+118
-65
lines changed

20 files changed

+118
-65
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Install dependencies
2525
run: |
2626
python -m pip install --upgrade pip
27-
pip install pytest pycodestyle pyright websockets
27+
pip install pytest pycodestyle pyright aiohttp
2828
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
2929
- name: Run tests with pytest
3030
run: |

examples/bookstore/webserver.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
2727
"""
2828
import asyncio
2929
from sys import argv
30+
from typing import TypedDict
3031
from functools import partial
31-
from aiohttp import web # type: ignore
32+
from aiohttp import web
3233
from thingsdb.client import Client
3334
from thingsdb.room import Room, event
3435

@@ -37,11 +38,14 @@
3738

3839
bookstore = None
3940

41+
class Book(TypedDict):
42+
title: str
43+
4044

4145
class BookStore(Room):
4246

4347
def on_init(self):
44-
self.books = []
48+
self.books: list[Book] = []
4549
self.add_book = partial(self.client.run, 'add_book')
4650

4751
async def on_join(self):
@@ -50,7 +54,7 @@ async def on_join(self):
5054
""")
5155

5256
@event('add-book')
53-
def on_add_book(self, book):
57+
def on_add_book(self, book: Book):
5458
self.books.append(book)
5559

5660

@@ -59,7 +63,7 @@ def on_cleanup():
5963
return client.wait_closed()
6064

6165

62-
async def add_book(request):
66+
async def add_book(request: web.Request):
6367
book = await request.json()
6468
# Use the procedure to add the book
6569
assert bookstore
@@ -68,17 +72,17 @@ async def add_book(request):
6872

6973

7074
# We have the books in memory, no need for a query
71-
async def get_books(request):
75+
async def get_books(request: web.Request):
7276
assert bookstore
7377
return web.json_response({
7478
"book_titles": [book['title'] for book in bookstore.books]
7579
})
7680

7781

78-
async def setup(client):
82+
async def setup(client: Client):
7983
global bookstore
8084

81-
await client.connect('playground.thingsdb.net', '9400')
85+
await client.connect('playground.thingsdb.net', 9400)
8286
await client.authenticate(THINGSDB_AUTH_TOKEN)
8387

8488
bookstore = BookStore("""//ti

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[tool.pyright]
2-
typeCheckingMode = "basic"
2+
typeCheckingMode = "strict"
3+
reportPrivateUsage = "none"

requirements.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
msgpack>=0.6.2
2-
deprecation
3-
# Optional package:
4-
# websockets
1+
msgpack
2+
msgpack-types # optional
3+
websockets # optional

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
],
5151
install_requires=[
5252
'msgpack',
53-
'deprecation'
5453
],
5554
keywords='database connector',
5655

thingsdb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .version import __version__
1+
from .version import __version__ # type: ignore # noqa: F401

thingsdb/client/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
from .client import Client
2-
from .package import set_package_fail_file
1+
from .client import Client # type: ignore # noqa: F401
2+
from .package import set_package_fail_file # type: ignore # noqa: F401

thingsdb/client/baseprotocol.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ class Err(enum.IntEnum):
108108
Err.EX_INTERNAL: InternalError,
109109
}
110110

111-
_PROTO_RESPONSE_MAP = {
111+
_PROTO_RESPONSE_MAP: dict[
112+
Proto,
113+
Callable[[asyncio.Future[None], Any], None]] = {
112114
Proto.RES_PING: lambda f, d: f.set_result(None),
113115
Proto.RES_OK: lambda f, d: f.set_result(None),
114116
Proto.RES_DATA: lambda f, d: f.set_result(d),
@@ -127,24 +129,28 @@ class Err(enum.IntEnum):
127129
)
128130

129131

130-
def proto_unknown(f, d):
131-
f.set_exception(TypeError('unknown package type received ({})'.format(d)))
132+
def proto_unknown(f: asyncio.Future[None], d: Any):
133+
f.set_exception(TypeError(f'unknown package type received ({d})'))
132134

133135

134136
class BaseProtocol:
135137
def __init__(
136138
self,
137139
on_connection_lost: Callable[[asyncio.Protocol, Exception], None],
138140
on_event: Callable[[Package], None],):
139-
self._requests = {}
141+
self._requests: dict[
142+
int,
143+
tuple[
144+
asyncio.Future[Any],
145+
asyncio.Task[None] | None]] = {}
140146
self._pid = 0
141147
self._on_connection_lost = on_connection_lost
142148
self._on_event = on_event
143149

144150
async def _timer(self, pid: int, timeout: int) -> None:
145151
await asyncio.sleep(timeout)
146152
try:
147-
future, task = self._requests.pop(pid)
153+
future, _task = self._requests.pop(pid)
148154
except KeyError:
149155
logging.error(f'Timed out package Id not found: {pid}')
150156
return None
@@ -194,9 +200,12 @@ def write(
194200
self._pid += 1
195201
self._pid %= 0x10000 # pid is handled as uint16_t
196202

197-
data = data if is_bin else b'' if data is None else \
203+
data = ( # type: ignore
204+
data if is_bin else b'' if data is None else \
198205
msgpack.packb(data, use_bin_type=True)
206+
)
199207

208+
assert isinstance(data, bytes)
200209
header = Package.st_package.pack(
201210
len(data),
202211
self._pid,
@@ -208,7 +217,7 @@ def write(
208217
task = asyncio.ensure_future(
209218
self._timer(self._pid, timeout)) if timeout else None
210219

211-
future = asyncio.Future()
220+
future: asyncio.Future[None] = asyncio.Future()
212221
self._requests[self._pid] = (future, task)
213222
return future
214223

thingsdb/client/client.py

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1+
from __future__ import annotations
12
import asyncio
23
import logging
34
import random
45
import ssl
56
import time
67
from collections import defaultdict
78
from ssl import SSLContext, PROTOCOL_TLS
8-
from typing import Any
9+
from typing import Any, TYPE_CHECKING
910
from concurrent.futures import CancelledError
1011
from .buildin import Buildin
1112
from .protocol import Proto, Protocol, ProtocolWS
1213
from ..exceptions import NodeError, AuthError
1314
from ..util import strip_code
15+
if TYPE_CHECKING:
16+
from ..room.roombase import RoomBase
17+
from ..client.package import Package
1418

1519

1620
class Client(Buildin):
@@ -54,7 +58,7 @@ def __init__(
5458
self._scope = '@t' # default to thingsdb scope
5559
self._pool_idx = 0
5660
self._reconnecting = False
57-
self._rooms = dict()
61+
self._rooms: dict[int, RoomBase] = dict()
5862
self._rooms_lock = asyncio.Lock()
5963

6064
if ssl is True:
@@ -64,7 +68,7 @@ def __init__(
6468
else:
6569
self._ssl = ssl
6670

67-
def get_rooms(self):
71+
def get_rooms(self) -> tuple[RoomBase, ...]:
6872
"""Can be used to get the rooms which are joined.
6973
7074
Returns:
@@ -185,12 +189,18 @@ def connect_pool(
185189
assert self._reconnecting is False
186190
assert len(pool), 'pool must contain at least one node'
187191
if len(auth) == 1:
188-
auth = auth[0] # type: ignore
192+
_auth = auth[0] # token or tuple[str, str]
193+
elif len(auth) == 2 and \
194+
isinstance(auth[0], str) and \
195+
isinstance(auth[1], str):
196+
_auth = (auth[0], auth[1]) # username/password
197+
else:
198+
raise TypeError('wrong or missing authentication arguments')
189199

190200
self._pool = tuple((
191201
(address, 9200) if isinstance(address, str) else address
192202
for address in pool))
193-
self._auth = self._auth_check(auth)
203+
self._auth = self._auth_check(_auth)
194204
self._pool_idx = random.randint(0, len(pool) - 1)
195205
fut = self.reconnect()
196206
if fut is None:
@@ -291,8 +301,15 @@ async def authenticate(
291301
wait forever on a response. Defaults to 5.
292302
"""
293303
if len(auth) == 1:
294-
auth = auth[0] # type: ignore
295-
self._auth = self._auth_check(auth)
304+
_auth = auth[0] # token or tuple[str, str]
305+
elif len(auth) == 2 and \
306+
isinstance(auth[0], str) and \
307+
isinstance(auth[1], str):
308+
_auth = (auth[0], auth[1]) # username/password
309+
else:
310+
raise TypeError('wrong or missing authentication arguments')
311+
312+
self._auth = self._auth_check(_auth)
296313
await self._authenticate(timeout)
297314

298315
def query(
@@ -568,7 +585,7 @@ def _leave(self, *ids: int | str,
568585
return self._write_pkg(Proto.REQ_LEAVE, [scope, *ids]) # type: ignore
569586

570587
@staticmethod
571-
def _auth_check(auth):
588+
def _auth_check(auth: str | tuple[str, str]) -> str | tuple[str, str]:
572589
assert ((
573590
isinstance(auth, (list, tuple)) and
574591
len(auth) == 2 and
@@ -583,7 +600,7 @@ def _auth_check(auth):
583600
return auth
584601

585602
@staticmethod
586-
def _is_websocket_host(host):
603+
def _is_websocket_host(host: str) -> bool:
587604
return host.startswith('ws://') or host.startswith('wss://')
588605

589606
async def _connect(self, timeout: int | None = 5):
@@ -615,7 +632,7 @@ async def _connect(self, timeout: int | None = 5):
615632
self._pool_idx += 1
616633
self._pool_idx %= len(self._pool)
617634

618-
async def _on_room(self, room_id, pkg):
635+
async def _on_room(self, room_id: int, pkg: Package):
619636
async with self._rooms_lock:
620637
try:
621638
room = self._rooms[room_id]
@@ -628,8 +645,9 @@ async def _on_room(self, room_id, pkg):
628645
if isinstance(task, asyncio.Task):
629646
await task
630647

631-
def _on_event(self, pkg):
648+
def _on_event(self, pkg: Package):
632649
if pkg.tp == Proto.ON_NODE_STATUS:
650+
assert pkg.data is not None
633651
status, node_id = pkg.data['status'], pkg.data['id']
634652

635653
if self._reconnect and status == 'SHUTTING_DOWN':
@@ -654,7 +672,7 @@ def _on_event(self, pkg):
654672
asyncio.ensure_future(self._on_room(room_id, pkg),
655673
loop=self.get_event_loop())
656674

657-
def _on_connection_lost(self, protocol, exc):
675+
def _on_connection_lost(self, protocol: asyncio.Protocol, exc: Exception):
658676
if self._protocol is not protocol:
659677
return
660678
self._protocol = None
@@ -695,20 +713,23 @@ async def _reconnect_loop(self):
695713
finally:
696714
self._reconnecting = False
697715

698-
def _ping(self, timeout):
699-
return self._write(Proto.REQ_PING, timeout=timeout)
716+
async def _ping(self, timeout: int | None):
717+
return await self._write(Proto.REQ_PING, timeout=timeout)
700718

701-
def _authenticate(self, timeout):
702-
return self._write(Proto.REQ_AUTH, data=self._auth, timeout=timeout)
719+
async def _authenticate(self, timeout: int | None) -> asyncio.Future[Any]:
720+
return await self._write(
721+
Proto.REQ_AUTH,
722+
data=self._auth,
723+
timeout=timeout)
703724

704725
async def _rejoin(self):
705726
if not self._rooms:
706727
return # do nothig if no rooms are used
707728

708729
# re-arrange the rooms per scope to combine joins in a less requests
709-
scopes = defaultdict(list)
730+
scopes: dict[str, list[int]] = defaultdict(list)
710731
for room in self._rooms.values():
711-
if room.id:
732+
if room.id and room.scope:
712733
scopes[room.scope].append(room.id)
713734

714735
# join request per scope, each for one or more rooms

thingsdb/client/package.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import struct
22
import msgpack
33
import logging
4+
from typing import Any
45

56

67
_fail_file = ''
@@ -30,7 +31,7 @@ def __init__(self, barray: bytearray | bytes) -> None:
3031
self.length, self.pid, self.tp, self.checkbit = \
3132
self.__class__.st_package.unpack_from(barray, offset=0)
3233
self.total = self.__class__.st_package.size + self.length
33-
self.data = None
34+
self.data: Any = None
3435

3536
def _handle_fail_file(self, message: bytes):
3637
if _fail_file:

0 commit comments

Comments
 (0)