Skip to content

Commit 5f2ae2a

Browse files
committed
Begin resolving type annotation issues
1 parent 474873a commit 5f2ae2a

File tree

9 files changed

+81
-56
lines changed

9 files changed

+81
-56
lines changed

.github/workflows/integration.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,21 @@ jobs:
206206
PIP_DISABLE_PIP_VERSION_CHECK: "1"
207207
run: |
208208
pip install --quiet git+${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git@${GITHUB_SHA}
209+
210+
211+
test-type-annotations:
212+
runs-on: ubuntu-latest
213+
timeout-minutes: 5
214+
name: Test type annotations
215+
steps:
216+
- uses: actions/checkout@v6
217+
- uses: actions/setup-python@v6
218+
with:
219+
python-version: '3.14'
220+
cache: 'pip'
221+
222+
- name: Test type annotations
223+
run: |
224+
pip install --group type-check
225+
pip install .
226+
mypy

.mypy.ini

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

pyproject.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ dev = [
5656
'uvloop; implementation_name != "pypy"',
5757
'vulture>=2.3.0',
5858
]
59+
type-check = [
60+
'mypy',
61+
'types-requests',
62+
'cryptography',
63+
'libvalkey',
64+
]
5965
docs = [
6066
'sphinx>=5.0,<7.0',
6167
'docutils<0.18',
@@ -103,3 +109,21 @@ markers = [
103109
]
104110
asyncio_mode = "auto"
105111
timeout = 30
112+
113+
[tool.mypy]
114+
packages = ["valkey"]
115+
sqlite_cache = true
116+
117+
[[tool.mypy.overrides]]
118+
# The goal is to eliminate the `tool.mypy.overrides` section.
119+
# This will be accomplished by resolving type annotation issues
120+
# in the modules listed below.
121+
ignore_errors = true
122+
module = [
123+
"valkey.client",
124+
"valkey.cluster",
125+
"valkey.connection",
126+
"valkey.commands.*",
127+
"valkey.asyncio.*",
128+
"valkey._parsers.*",
129+
]

valkey/_cache.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from abc import ABC, abstractmethod
55
from collections import OrderedDict, defaultdict
66
from enum import Enum
7-
from typing import List, Sequence, Union
7+
from typing import List, Sequence, Union, Any
88

99
from valkey.typing import KeyT, ResponseT
1010

@@ -223,9 +223,9 @@ def __init__(
223223
self.max_size = max_size
224224
self.ttl = ttl
225225
self.eviction_policy = eviction_policy
226-
self.cache = OrderedDict()
227-
self.key_commands_map = defaultdict(set)
228-
self.commands_ttl_list = []
226+
self.cache: OrderedDict[str | Sequence[str], dict[str, Any]] = OrderedDict()
227+
self.key_commands_map: defaultdict[KeyT, set] = defaultdict(set)
228+
self.commands_ttl_list: list[str | Sequence[str]] = []
229229

230230
def set(
231231
self,
@@ -252,7 +252,7 @@ def set(
252252
self._update_key_commands_map(keys_in_command, command)
253253
self.commands_ttl_list.append(command)
254254

255-
def get(self, command: Union[str, Sequence[str]]) -> ResponseT:
255+
def get(self, command: Union[str, Sequence[str]]) -> ResponseT | None:
256256
"""
257257
Get the response for a valkey command from the cache.
258258
@@ -262,12 +262,14 @@ def get(self, command: Union[str, Sequence[str]]) -> ResponseT:
262262
Returns:
263263
ResponseT: The response associated with the command, or None if the command is not in the cache. # noqa
264264
"""
265-
if command in self.cache:
266-
if self._is_expired(command):
267-
self.delete_command(command)
268-
return
269-
self._update_access(command)
270-
return copy.deepcopy(self.cache[command]["response"])
265+
if command not in self.cache:
266+
return None
267+
268+
if self._is_expired(command):
269+
self.delete_command(command)
270+
return None
271+
self._update_access(command)
272+
return copy.deepcopy(self.cache[command]["response"])
271273

272274
def delete_command(self, command: Union[str, Sequence[str]]):
273275
"""
@@ -278,7 +280,7 @@ def delete_command(self, command: Union[str, Sequence[str]]):
278280
"""
279281
if command in self.cache:
280282
keys_in_command = self.cache[command].get("keys")
281-
self._del_key_commands_map(keys_in_command, command)
283+
self._del_key_commands_map(keys_in_command, command) # type: ignore[arg-type]
282284
self.commands_ttl_list.remove(command)
283285
del self.cache[command]
284286

valkey/backoff.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE) -> None
9898
"""
9999
self._cap = cap
100100
self._base = base
101-
self._previous_backoff = 0
101+
self._previous_backoff = 0.0
102102

103103
def reset(self) -> None:
104-
self._previous_backoff = 0
104+
self._previous_backoff = 0.0
105105

106106
def compute(self, failures: int) -> float:
107107
max_backoff = max(self._base, self._previous_backoff * 3)

valkey/crc.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
from binascii import crc_hqx
22

3-
from valkey.typing import EncodedT
4-
53
# Valkey Cluster's key space is divided into 16384 slots.
64
# For more information see: https://github.com/redis/redis/issues/2576
75
VALKEY_CLUSTER_HASH_SLOTS = 16384
86

97
__all__ = ["key_slot", "VALKEY_CLUSTER_HASH_SLOTS"]
108

119

12-
def key_slot(key: EncodedT, bucket: int = VALKEY_CLUSTER_HASH_SLOTS) -> int:
10+
def key_slot(key: bytes, bucket: int = VALKEY_CLUSTER_HASH_SLOTS) -> int:
1311
"""Calculate key slot for a given key.
1412
See Keys distribution model in https://redis.io/topics/cluster-spec
1513
:param key - bytes

valkey/lock.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ class Lock:
1717
multiple clients play nicely together.
1818
"""
1919

20-
lua_release = None
21-
lua_extend = None
22-
lua_reacquire = None
20+
# This function is used to flag that the class `lua_*` functions are not set yet.
21+
# Using a function, rather than `None`, prevents type annotation issues.
22+
def __undefined(*_, **__) -> None: ...
23+
24+
lua_release = __undefined
25+
lua_extend = __undefined
26+
lua_reacquire = __undefined
2327

2428
# KEYS[1] - lock name
2529
# ARGV[1] - token
@@ -147,11 +151,11 @@ def __init__(
147151
def register_scripts(self) -> None:
148152
cls = self.__class__
149153
client = self.valkey
150-
if cls.lua_release is None:
154+
if cls.lua_release is cls.__undefined:
151155
cls.lua_release = client.register_script(cls.LUA_RELEASE_SCRIPT)
152-
if cls.lua_extend is None:
156+
if cls.lua_extend is cls.__undefined:
153157
cls.lua_extend = client.register_script(cls.LUA_EXTEND_SCRIPT)
154-
if cls.lua_reacquire is None:
158+
if cls.lua_reacquire is cls.__undefined:
155159
cls.lua_reacquire = client.register_script(cls.LUA_REACQUIRE_SCRIPT)
156160

157161
def __enter__(self) -> "Lock":
@@ -175,7 +179,7 @@ def acquire(
175179
sleep: Optional[Number] = None,
176180
blocking: Optional[bool] = None,
177181
blocking_timeout: Optional[Number] = None,
178-
token: Optional[str] = None,
182+
token: str| bytes | None = None,
179183
):
180184
"""
181185
Use Valkey to hold a shared, distributed lock named ``name``.
@@ -195,10 +199,10 @@ def acquire(
195199
if sleep is None:
196200
sleep = self.sleep
197201
if token is None:
198-
token = uuid.uuid1().hex.encode()
202+
encoded_token = uuid.uuid1().hex.encode()
199203
else:
200204
encoder = self.valkey.get_encoder()
201-
token = encoder.encode(token)
205+
encoded_token = encoder.encode(token)
202206
if blocking is None:
203207
blocking = self.blocking
204208
if blocking_timeout is None:
@@ -207,8 +211,8 @@ def acquire(
207211
if blocking_timeout is not None:
208212
stop_trying_at = mod_time.monotonic() + blocking_timeout
209213
while True:
210-
if self.do_acquire(token):
211-
self.local.token = token
214+
if self.do_acquire(encoded_token):
215+
self.local.token = encoded_token
212216
return True
213217
if not blocking:
214218
return False
@@ -217,7 +221,7 @@ def acquire(
217221
return False
218222
mod_time.sleep(sleep)
219223

220-
def do_acquire(self, token: str) -> bool:
224+
def do_acquire(self, token: bytes) -> bool:
221225
if self.timeout:
222226
# convert to milliseconds
223227
timeout = int(self.timeout * 1000)
@@ -312,6 +316,10 @@ def reacquire(self) -> bool:
312316
return self.do_reacquire()
313317

314318
def do_reacquire(self) -> bool:
319+
# `do_reacquire()` will only be called if `self.timeout` is not `None`.
320+
# However, this `assert` is needed so that mypy understands the type.
321+
assert self.timeout is not None
322+
315323
timeout = int(self.timeout * 1000)
316324
if not bool(
317325
self.lua_reacquire(

valkey/sentinel.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ def owns_connection(self, connection):
189189
check = not self.is_master or (
190190
self.is_master and self.master_address == (connection.host, connection.port)
191191
)
192-
parent = super()
193-
return check and parent.owns_connection(connection)
192+
return check and super().owns_connection(connection)
194193

195194
def get_master_address(self):
196195
return self.proxy.get_master_address()

valkey/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def dict_merge(*dicts: Mapping[str, Any]) -> Dict[str, Any]:
6363
*dicts : `dict`
6464
dictionaries to merge
6565
"""
66-
merged = {}
66+
merged: dict[str, Any] = {}
6767

6868
for d in dicts:
6969
merged.update(d)

0 commit comments

Comments
 (0)