Skip to content

Commit 0100700

Browse files
committed
Improves Type Hints
1 parent b5db560 commit 0100700

20 files changed

+167
-107
lines changed

.flake8

Lines changed: 0 additions & 3 deletions
This file was deleted.

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ I will try to keep up with the latest version of `TinyDB`.
1818

1919
* **`ujson`:** Using `ujson` instead of `json`. Some arguments aren't compatible with `json`[^1]
2020

21-
* **Storage `closed` property**: Original `TinyDB` won't raise exceptions when operating on a closed file. Now the property `closed` of `Storage` classes is required to be implemented. An `IOError` should be raised.
21+
* **Storage `closed` property**: Original `TinyDB` won't raise exceptions when operating on a closed file. Now the property `closed` of `Storage` classes is required to be implemented[^why-closed][^operating-on-closed].
2222

23-
* **[Miscellaneous](#misc)**: Differences only matter in edge cases.
23+
* **[Miscellaneous](#misc)**: Differences that only matter in edge cases.
2424

2525
# New Features
2626

27-
* **Event Hooks**: You can now use event hooks to do something before or after an operation. See [Event Hooks](#event-hooks) for more details.
27+
* **Event Hooks**: You can now use event hooks to hook into an operation. See [Event Hooks](#event-hooks) for more details.
2828

2929
* **Redesigned ID & Doc Class**: You can [replace](#replacing-id-&-document-class) and [customise them](#customise-id-class) more pleasingly.
3030

@@ -266,7 +266,7 @@ Make sure you have implemented all the methods required by `BaseDocument` class
266266
* `search` accepts optional `cond`, returns all docs if no arguments are provided
267267
* `get` and `contains` raises `ValueError` instead of `RuntimeError` when `cond` and `doc_id` are both `None`
268268
* `LRUCache` stores `tuple`s of ids instead of `list`s of docs
269-
* `search` and `get` treat `doc_id` and `doc_ids` as extra conditions instead of ignoring conditions when they are provided. That is to say, when `cond` and `doc_id(s)` are passed, they return docs satisfies both conditions.
269+
* `search` and `get` treat `doc_id` and `doc_ids` as extra conditions instead of ignoring conditions when they are provided. That is to say, when `cond` and `doc_id(s)` are passed, they return docs satisfies both `cond` and `doc_id(s)`.
270270

271271

272272

@@ -275,3 +275,6 @@ Make sure you have implemented all the methods required by `BaseDocument` class
275275
[^UUID-version]:Currently using UUID4
276276
[^disable-db-level]: See [DB-level caching](#db-level-caching) to learn how to disable this feature if it causes dirty reads.
277277
[^isolevel]: See [isolevel](#isolation-level)
278+
[^why-closed]: This is for `Middileware` classes to reliably determine whether the `Storage` is closed, so they can raise `IOError`
279+
[^operating-on-closed]: An `IOError` should be raised when operating on a closed storage.
280+

asynctinydb/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from .queries import Query, where
2828
from .storages import Storage, JSONStorage, EncryptedJSONStorage, MemoryStorage
29+
from .middlewares import CachingMiddleware
2930
from .modifier import Modifier
3031
from .database import TinyDB
3132
from .version import __version__ # noqa
@@ -34,6 +35,7 @@
3435
__all__ = (
3536
"TinyDB", "Storage", "JSONStorage",
3637
"EncryptedJSONStorage", "MemoryStorage",
38+
"CachingMiddleware",
3739
"Query", "where", "BaseID", "BaseDocument",
3840
"Modifier", "IncreID", "Document", "UUID"
3941
)

asynctinydb/async_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def get_create_loop():
184184
try:
185185
return asyncio.get_running_loop()
186186
except RuntimeError:
187-
loop: asyncio.AbstractEventLoop = getattr(_LOCAL, "loop", None)
187+
loop: asyncio.AbstractEventLoop | None = getattr(_LOCAL, "loop", None)
188188
if loop is None or loop.is_closed():
189189
loop = asyncio.new_event_loop()
190190
asyncio.set_event_loop(loop)

asynctinydb/database.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
"""
44

55
from __future__ import annotations
6-
from typing import AsyncGenerator, Type, overload, TypeVar, Generic
6+
from typing import AsyncGenerator, Type, overload, TypeVar, Generic, Any
77

88
from .storages import Storage, JSONStorage
9+
from .middlewares import Middleware
910
from .table import Table, Document, IncreID, IDVar, DocVar, BaseDocument
1011
from .utils import with_typehint, sync_await
1112

1213
# The table's base class. This is used to add type hinting from the Table
1314
# class to TinyDB. Currently, this supports PyCharm, Pyright/VS Code and MyPy.
1415
TableBase: Type[Table] = with_typehint(Table)
15-
_S = TypeVar("_S", bound=Storage)
16+
S = TypeVar("S", bound=Storage, covariant=True)
1617

1718

18-
class TinyDB(Generic[_S], TableBase):
19+
class TinyDB(Generic[S], TableBase):
1920
"""
2021
The main class of TinyDB.
2122
@@ -86,16 +87,14 @@ class TinyDB(Generic[_S], TableBase):
8687
default_storage_class = JSONStorage
8788

8889
@overload
89-
def __init__(self: TinyDB[_S], *args, storage: Type[_S], **kw) -> None:
90-
...
91-
90+
def __init__(self: TinyDB[S], *args, storage: Type[S], **kw) -> None:
91+
"""For `Storage` classes passed to `storage`"""
9292
@overload
93-
def __init__(self: TinyDB[Storage], *args, storage, **kw) -> None:
94-
...
95-
93+
def __init__(self: TinyDB[S], *args, storage: Middleware[S], **kw) -> None:
94+
"""For `Middleware` passed to `storage`"""
9695
@overload
9796
def __init__(self: TinyDB[JSONStorage], *args, **kw) -> None:
98-
...
97+
"""For default `JSONStorage`"""
9998

10099
def __init__(self, *args, **kw) -> None:
101100
"""
@@ -109,7 +108,7 @@ def __init__(self, *args, **kw) -> None:
109108
self._no_dbcache: bool = kw.pop("no_dbcache", False)
110109

111110
# Prepare the storage
112-
self._storage: _S = kw.pop(
111+
self._storage: S = kw.pop(
113112
"storage", type(self).default_storage_class)(*args, **kw)
114113

115114
self._opened = True
@@ -271,7 +270,7 @@ async def drop_table(self, name: str) -> None:
271270
await self.storage.write(data)
272271

273272
@property
274-
def storage(self) -> _S:
273+
def storage(self) -> S:
275274
"""
276275
Get the storage instance used for this TinyDB instance.
277276

asynctinydb/event_hooks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, actions: Iterable[ActionVar] | None = None,
2727
* `limit` is the maximum number of actions to add to the chain.
2828
Set to 0 for unlimited.
2929
"""
30-
self._seq = list[ActionVar](actions or [])
30+
self._seq: list[ActionVar] = list(actions or [])
3131
self.limit = limit
3232

3333
def append(self, action: ActionVar) -> None:
@@ -232,6 +232,7 @@ class EventHook(dict[str, BaseActionChain]):
232232
# Event Hook Class
233233
Binds events to action chains.
234234
"""
235+
235236
def __init__(self, chain: Mapping[str, Iterable[ActionVar]] | None = None):
236237
dict.__init__(self)
237238
if chain is not None:

asynctinydb/middlewares.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
middlewares and implementations.
44
"""
55

6+
from __future__ import annotations
7+
from typing import TypeVar, Type, Generic
68
from .storages import Storage
79

10+
S = TypeVar('S', bound=Storage, covariant=True)
811

9-
class Middleware:
12+
13+
class Middleware(Generic[S]):
1014
"""
1115
The base class for all Middlewares.
1216
@@ -17,9 +21,9 @@ class Middleware:
1721
constructor so the middleware chain can be configured properly.
1822
"""
1923

20-
def __init__(self, storage_cls):
24+
def __init__(self, storage_cls: Type[S]):
2125
self._storage_cls = storage_cls
22-
self.storage: Storage = None
26+
self.storage: Storage = None # type: ignore
2327

2428
@property
2529
def on(self):
@@ -80,7 +84,7 @@ def __getattr__(self, name):
8084
return getattr(self.__dict__['storage'], name)
8185

8286

83-
class CachingMiddleware(Middleware):
87+
class CachingMiddleware(Middleware[S]):
8488
"""
8589
Add some caching to TinyDB.
8690
@@ -89,7 +93,7 @@ class CachingMiddleware(Middleware):
8993
from cache.
9094
"""
9195

92-
def __init__(self, storage_cls, cache_size=1000):
96+
def __init__(self, storage_cls: Type[S], cache_size=1000):
9397
# Initialize the parent constructor
9498
super().__init__(storage_cls)
9599

asynctinydb/modifier.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
from typing import Any, Callable, TypeVar
55
from warnings import warn
66
from functools import partial
7-
from .storages import Storage
7+
from .storages import Storage, StorageWithWriteReadPrePostHooks
88
from .utils import async_run
99
from .database import TinyDB
1010

1111

12-
_S = TypeVar('_S', bound=Storage)
12+
S = TypeVar('S', bound=Storage)
13+
SWRPH = TypeVar("SWRPH", bound=StorageWithWriteReadPrePostHooks)
1314

1415

15-
def _get_storage(item: _S | TinyDB[_S]) -> _S:
16+
def _get_storage(item: S | TinyDB[S]) -> S:
1617
"""Get the storage from a TinyDB or Storage object."""
1718
if isinstance(item, TinyDB):
1819
return item.storage
@@ -27,7 +28,7 @@ class Encryption:
2728
"""
2829

2930
@staticmethod
30-
def AES_GCM(s: _S | TinyDB[_S], key: str | bytes, **kw) -> _S:
31+
def AES_GCM(s: SWRPH | TinyDB[SWRPH], key: str | bytes, **kw) -> SWRPH:
3132
"""
3233
### Add AES-GCM Encryption to TinyDB Storage
3334
Hooks to `write.post` and `read.pre` to encrypt/decrypt data.
@@ -71,8 +72,8 @@ async def encrypt_aes_gcm(_: str, s: Storage, data: str | bytes):
7172
async def decrypt_aes_gcm(_: str, s: Storage, data: bytes):
7273
d_len = data[0] # digest length
7374
digest = data[1: d_len + 1]
74-
cipher: GcmMode = AES.new(
75-
key, nonce=data[d_len + 1:d_len + 17], **kw) # type: ignore
75+
cipher: GcmMode = AES.new(key, # type: ignore
76+
nonce=data[d_len + 1:d_len + 17], **kw)
7677
data = data[d_len + 17:]
7778
task = async_run(cipher.decrypt_and_verify, data, digest)
7879
ret = await task
@@ -84,8 +85,8 @@ async def decrypt_aes_gcm(_: str, s: Storage, data: bytes):
8485
return s
8586

8687
@classmethod
87-
def add_encryption(cls, s: _S | TinyDB[_S], key: str | bytes,
88-
encoding=None, **kw) -> _S:
88+
def add_encryption(cls, s: SWRPH | TinyDB[SWRPH], key: str | bytes,
89+
encoding: str = None, **kw) -> SWRPH:
8990
"""
9091
### Add AES-GCM Encryption to TinyDB Storage
9192
**Deprecated, consider using Modifier.Encryption.AES_GCM**
@@ -113,7 +114,7 @@ class Compression:
113114
"""
114115

115116
@staticmethod
116-
def brotli(s: _S | TinyDB[_S], quality=11, **kw) -> _S:
117+
def brotli(s: SWRPH | TinyDB[SWRPH], quality=11, **kw) -> SWRPH:
117118
"""
118119
### Add Brotli Compression to TinyDB Storage
119120
Hooks to `write.post` and `read.pre` to compress/decompress data.
@@ -153,7 +154,7 @@ async def decompress_brotli(ev: str, s: Storage, data: bytes):
153154
return s
154155

155156
@staticmethod
156-
def blosc2(s: _S | TinyDB[_S], clevel=9, **kw) -> _S:
157+
def blosc2(s: SWRPH | TinyDB[SWRPH], clevel=9, **kw) -> SWRPH:
157158
"""
158159
### Add Blosc2 Compression to TinyDB Storage
159160
Hooks to `write.post` and `read.pre` to compress/decompress data.
@@ -198,13 +199,13 @@ class Conversion:
198199
"""
199200

200201
@staticmethod
201-
def ExtendedJSON(s: _S | TinyDB[_S],
202+
def ExtendedJSON(s: SWRPH | TinyDB[SWRPH],
202203
type_hooks: dict[type, None | Callable[[
203204
Any, Callable[[Any], Any]],
204205
dict[str, Any]]] = None,
205206
marker_hooks: dict[str, None | Callable[[
206207
dict[str, Any], Callable[[Any], Any]],
207-
Any]] = None) -> _S:
208+
Any]] = None) -> SWRPH:
208209
"""
209210
### Extend JSON Data Types
210211
@@ -325,7 +326,7 @@ def convert(obj, memo: set = None):
325326
ret = hook(obj, _convert)
326327
return ret
327328

328-
def recover(obj):
329+
def recover(obj) -> Any:
329330
"""
330331
### Recursively recovery object from extended JSON
331332
**No loop reference check**

0 commit comments

Comments
 (0)