Skip to content

Commit 728917f

Browse files
committed
QDB-16709 - Add Python type hints (#102)
1 parent 09a93b8 commit 728917f

File tree

24 files changed

+752
-401
lines changed

24 files changed

+752
-401
lines changed

.github/workflows/lint.yml

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ jobs:
2424
- name: Setup Python version
2525
uses: actions/setup-python@v5
2626
with:
27-
python-version: '3.13'
27+
python-version: '3.12'
2828

2929
- name: Install dependencies
3030
run: |
31-
pip install -r scripts/github_actions/requirements.txt
31+
pip install black==24.10.0
3232
3333
- name: Run black on Python files
3434
run: |
@@ -38,22 +38,21 @@ jobs:
3838
run: |
3939
black --include '\.pyi$' --check --verbose .
4040
41-
# typing:
42-
# runs-on: ubuntu-latest
43-
# steps:
44-
# - name: Check out code
45-
# uses: actions/checkout@v4
46-
47-
# - name: Setup Python version
48-
# uses: actions/setup-python@v5
49-
# with:
50-
# python-version: '3.13'
51-
52-
# - name: Install dependencies
53-
# run: |
54-
# pip install -r scripts/github_actions/requirements.txt
55-
# pip install -r dev-requirements.txt
56-
57-
# - name: Run mypy
58-
# run: |
59-
# mypy --check quasardb
41+
typing:
42+
runs-on: ubuntu-22.04
43+
steps:
44+
- name: Check out code
45+
uses: actions/checkout@v4
46+
47+
- name: Setup Python version
48+
uses: actions/setup-python@v5
49+
with:
50+
python-version: '3.7'
51+
52+
- name: Install dependencies
53+
run: |
54+
pip install -r dev-requirements.txt
55+
56+
- name: Run mypy
57+
run: |
58+
mypy --check quasardb

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ celerybeat-schedule
110110
*.sage.py
111111

112112
# Environments
113-
.env
114-
.venv
115-
venv
113+
.env*/
114+
.venv*/
115+
venv*/
116116

117117
# Spyder project settings
118118
.spyderproject

dev-requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ setuptools-git == 1.2
3737

3838
# Linting
3939
black==24.10.0; python_version >= '3.9'
40-
black == 23.3.0; python_version < '3.9'
40+
black==23.3.0; python_version < '3.9'
4141

4242
# Stubs
4343
mypy
4444
pybind11-stubgen
45+
pandas-stubs

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ xfail_strict = true
2525
filterwarnings = []
2626
testpaths = ["tests"]
2727

28-
# [tool.mypy]
29-
# python_version = "3.9"
28+
[tool.mypy]
29+
python_version = "3.7"
30+
disallow_untyped_defs = true

quasardb/extensions/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from .writer import extend_writer
1+
from typing import Any, List
22

3+
from .writer import extend_writer
34

4-
__all__ = []
5+
__all__: List[Any] = []
56

67

7-
def extend_module(m):
8-
m.Writer = extend_writer(m.Writer)
8+
def extend_module(m: Any) -> None:
9+
extend_writer(m.Writer)

quasardb/extensions/writer.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import copy
2-
import quasardb
2+
from typing import Any, Callable, Dict, List, Optional
3+
34
import numpy as np
45
import numpy.ma as ma
56

6-
__all__ = []
7+
import quasardb
8+
9+
__all__: List[Any] = []
710

811

9-
def _ensure_ctype(self, idx, ctype):
12+
def _ensure_ctype(self: Any, idx: int, ctype: quasardb.ColumnType) -> None:
1013
assert "table" in self._legacy_state
1114
infos = self._legacy_state["table"].list_columns()
1215
cinfo = infos[idx]
@@ -24,7 +27,7 @@ def _ensure_ctype(self, idx, ctype):
2427
raise quasardb.IncompatibleTypeError()
2528

2629

27-
def _legacy_next_row(self, table):
30+
def _legacy_next_row(self: Any, table: Any) -> Dict[str, Any]:
2831
if "pending" not in self._legacy_state:
2932
self._legacy_state["pending"] = []
3033

@@ -37,56 +40,56 @@ def _legacy_next_row(self, table):
3740
return self._legacy_state["pending"][-1]
3841

3942

40-
def _legacy_current_row(self):
43+
def _legacy_current_row(self: Any) -> Dict[str, Any]:
4144
return self._legacy_state["pending"][-1]
4245

4346

44-
def _legacy_start_row(self, table, x):
47+
def _legacy_start_row(self: Any, table: Any, x: np.datetime64) -> None:
4548
row = _legacy_next_row(self, table)
4649
assert "$timestamp" not in row
4750
row["$timestamp"] = x
4851

4952

50-
def _legacy_set_double(self, idx, x):
53+
def _legacy_set_double(self: Any, idx: int, x: float) -> None:
5154
_ensure_ctype(self, idx, quasardb.ColumnType.Double)
5255
assert isinstance(x, float)
5356
assert idx not in _legacy_current_row(self)["by_index"]
5457
_legacy_current_row(self)["by_index"][idx] = x
5558

5659

57-
def _legacy_set_int64(self, idx, x):
60+
def _legacy_set_int64(self: Any, idx: int, x: int) -> None:
5861
_ensure_ctype(self, idx, quasardb.ColumnType.Int64)
5962
assert isinstance(x, int)
6063
assert idx not in _legacy_current_row(self)["by_index"]
6164
_legacy_current_row(self)["by_index"][idx] = x
6265

6366

64-
def _legacy_set_timestamp(self, idx, x):
67+
def _legacy_set_timestamp(self: Any, idx: int, x: np.datetime64) -> None:
6568
_ensure_ctype(self, idx, quasardb.ColumnType.Timestamp)
6669
assert idx not in _legacy_current_row(self)["by_index"]
6770
_legacy_current_row(self)["by_index"][idx] = x
6871

6972

70-
def _legacy_set_string(self, idx, x):
73+
def _legacy_set_string(self: Any, idx: int, x: str) -> None:
7174
_ensure_ctype(self, idx, quasardb.ColumnType.String)
7275
assert isinstance(x, str)
7376
assert idx not in _legacy_current_row(self)["by_index"]
7477

7578
_legacy_current_row(self)["by_index"][idx] = x
7679

7780

78-
def _legacy_set_blob(self, idx, x):
81+
def _legacy_set_blob(self: Any, idx: int, x: bytes) -> None:
7982
_ensure_ctype(self, idx, quasardb.ColumnType.Blob)
8083
assert isinstance(x, bytes)
8184
assert idx not in _legacy_current_row(self)["by_index"]
8285

8386
_legacy_current_row(self)["by_index"][idx] = x
8487

8588

86-
def _legacy_push(self):
89+
def _legacy_push(self: Any) -> Optional[quasardb.WriterData]:
8790
if "pending" not in self._legacy_state:
8891
# Extremely likely default case, no "old" rows
89-
return
92+
return None
9093

9194
assert "table" in self._legacy_state
9295
table = self._legacy_state["table"]
@@ -109,7 +112,7 @@ def _legacy_push(self):
109112
all_idx = set(ctype_by_idx.keys())
110113

111114
# Prepare data structure
112-
pivoted = {"$timestamp": [], "by_index": {}}
115+
pivoted: Dict[str, Any] = {"$timestamp": [], "by_index": {}}
113116
for i in all_idx:
114117
pivoted["by_index"][i] = []
115118

@@ -140,7 +143,6 @@ def _legacy_push(self):
140143

141144
mask = [x is None for x in xs]
142145

143-
xs_ = []
144146
if all(mask):
145147
xs_ = ma.masked_all(len(xs), dtype=dtype)
146148
else:
@@ -159,9 +161,11 @@ def _legacy_push(self):
159161
return push_data
160162

161163

162-
def _wrap_fn(old_fn, replace_fn):
164+
def _wrap_fn(
165+
old_fn: Callable[..., Any], replace_fn: Callable[..., Optional[quasardb.WriterData]]
166+
) -> Callable[..., Any]:
163167

164-
def wrapped(self, *args, **kwargs):
168+
def wrapped(self: Any, *args: Any, **kwargs: Any) -> Any:
165169
data = replace_fn(self)
166170
if data:
167171
return old_fn(self, data, *args, **kwargs)
@@ -171,7 +175,7 @@ def wrapped(self, *args, **kwargs):
171175
return wrapped
172176

173177

174-
def extend_writer(x):
178+
def extend_writer(x: Any) -> None:
175179
"""
176180
Extends the writer with the "old", batch inserter API. This is purely
177181
a backwards compatibility layer, and we want to avoid having to maintain that

quasardb/firehose.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
import time
2-
import quasardb
31
import logging
2+
import time
3+
from typing import Any, Dict, Iterator, List, Optional, Tuple
4+
45
import numpy as np
56

7+
from quasardb import Cluster
8+
69
FIREHOSE_TABLE = "$qdb.firehose"
710
POLL_INTERVAL = 0.1
811

912
logger = logging.getLogger("quasardb.firehose")
1013

1114

12-
def _init():
15+
def _init() -> Dict[str, Any]:
1316
"""
1417
Initialize our internal state.
1518
"""
1619
return {"last": None, "seen": set()}
1720

1821

19-
def _get_transactions_since(conn, table_name, last):
22+
def _get_transactions_since(
23+
conn: Cluster, table_name: str, last: Optional[Dict[str, Any]]
24+
) -> List[Dict[str, Any]]:
2025
"""
2126
Retrieve all transactions since a certain timestamp. `last` is expected to be a dict
2227
firehose row with at least a $timestamp attached.
@@ -33,15 +38,19 @@ def _get_transactions_since(conn, table_name, last):
3338
return conn.query(q)
3439

3540

36-
def _get_transaction_data(conn, table_name, begin, end):
41+
def _get_transaction_data(
42+
conn: Cluster, table_name: str, begin: str, end: str
43+
) -> List[Dict[str, Any]]:
3744
"""
3845
Gets all data from a certain table.
3946
"""
4047
q = 'SELECT * FROM "{}" IN RANGE ({}, {}) '.format(table_name, begin, end)
4148
return conn.query(q)
4249

4350

44-
def _get_next(conn, table_name, state):
51+
def _get_next(
52+
conn: Cluster, table_name: str, state: Dict[str, Any]
53+
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
4554

4655
# Our flow to retrieve new data is as follows:
4756
# 1. Based on the state's last processed transaction, retrieve all transactions
@@ -52,7 +61,7 @@ def _get_next(conn, table_name, state):
5261

5362
txs = _get_transactions_since(conn, table_name, state["last"])
5463

55-
xs = list()
64+
xs: List[Dict[str, Any]] = []
5665
for tx in txs:
5766
txid = tx["transaction_id"]
5867

@@ -83,7 +92,7 @@ def _get_next(conn, table_name, state):
8392
return (state, xs)
8493

8594

86-
def subscribe(conn, table_name):
95+
def subscribe(conn: Cluster, table_name: str) -> Iterator[Dict[str, Any]]:
8796
state = _init()
8897

8998
while True:

0 commit comments

Comments
 (0)