Skip to content

Commit 42050a5

Browse files
committed
Add mypy typing
This was mostly straightforward except for a few places where there were conflicting types documented (usually int instead of float or vice versa) There are a few type ignores too since some of the current class hierarchies cannot be typed. For example, with Store the merge method accepts a store of the same type which when typed in both the parent and child classes results in a violation of the Liskov substitution principle
1 parent fd43f17 commit 42050a5

File tree

10 files changed

+153
-17
lines changed

10 files changed

+153
-17
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
fetch-depth: 0
1717
- run: pip install riot==0.8
1818
- run: riot -v run check_fmt
19+
- run: riot -v run -s mypy
20+
1921
test:
2022
strategy:
2123
matrix:

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,12 @@ Format code with
9494
riot run fmt
9595

9696

97+
### Type-checking
98+
99+
Type checking is done with [mypy](http://mypy-lang.org/):
100+
101+
riot run mypy
102+
103+
97104
## References
98105
[1] Charles Masson and Jee E Rim and Homin K. Lee. DDSketch: A fast and fully-mergeable quantile sketch with relative-error guarantees. PVLDB, 12(12): 2195-2205, 2019. (The code referenced in the paper, including our implementation of the the Greenwald-Khanna (GK) algorithm, can be found at: https://github.com/DataDog/sketches-py/releases/tag/v0.1 )

ddsketch/ddsketch.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<a href="https://github.com/DataDog/sketches-py/">Python</a>
3535
<a href="https://github.com/DataDog/sketches-js/">JavaScript</a>
3636
"""
37+
import typing
3738

3839
from .exception import IllegalArgumentException
3940
from .exception import UnequalSketchParametersException
@@ -43,6 +44,13 @@
4344
from .store import DenseStore
4445

4546

47+
if typing.TYPE_CHECKING:
48+
from typing import Optional
49+
50+
from .mapping import KeyMapping
51+
from .store import Store
52+
53+
4654
DEFAULT_REL_ACC = 0.01 # "alpha" in the paper
4755
DEFAULT_BIN_LIMIT = 2048
4856

@@ -73,6 +81,7 @@ def __init__(
7381
negative_store,
7482
zero_count,
7583
):
84+
# type: (KeyMapping, Store, Store, float) -> None
7685
self.mapping = mapping
7786
self.store = store
7887
self.negative_store = negative_store
@@ -85,6 +94,7 @@ def __init__(
8594
self._sum = 0.0
8695

8796
def __repr__(self):
97+
# type: () -> str
8898
return (
8999
f"store: {self.store}, negative_store: {self.negative_store}, "
90100
f"zero_count: {self.zero_count}, count: {self.count}, "
@@ -93,25 +103,30 @@ def __repr__(self):
93103

94104
@property
95105
def name(self):
106+
# type: () -> str
96107
"""str: name of the sketch"""
97108
return "DDSketch"
98109

99110
@property
100111
def num_values(self):
112+
# type: () -> float
101113
"""float: number of values in the sketch"""
102114
return self.count
103115

104116
@property
105117
def avg(self):
118+
# type: () -> float
106119
"""float: exact avg of the values added to the sketch"""
107120
return self.sum / self.count
108121

109122
@property
110123
def sum(self):
124+
# type: () -> float
111125
"""float: exact sum of the values added to the sketch"""
112126
return self._sum
113127

114128
def add(self, val, weight=1.0):
129+
# type: (float, float) -> None
115130
"""Add a value to the sketch."""
116131
if weight <= 0.0:
117132
raise IllegalArgumentException("weight must be a positive float")
@@ -132,6 +147,7 @@ def add(self, val, weight=1.0):
132147
self.max = val
133148

134149
def get_quantile_value(self, quantile):
150+
# type: (float) -> Optional[float]
135151
"""the approximate value at the specified quantile
136152
137153
Args:
@@ -158,6 +174,7 @@ def get_quantile_value(self, quantile):
158174
return quantile_value
159175

160176
def merge(self, sketch):
177+
# type: (BaseDDSketch) -> None
161178
"""Merges the other sketch into this one. After this operation, this sketch
162179
encodes the values that were added to both this and the input sketch.
163180
"""
@@ -187,10 +204,12 @@ def merge(self, sketch):
187204
self.max = sketch.max
188205

189206
def mergeable(self, other):
207+
# type: (BaseDDSketch) -> bool
190208
"""Two sketches can be merged only if their gammas are equal."""
191209
return self.mapping.gamma == other.mapping.gamma
192210

193211
def copy(self, sketch):
212+
# type: (BaseDDSketch) -> None
194213
"""copy the input sketch into this one"""
195214
self.store.copy(sketch.store)
196215
self.negative_store.copy(sketch.negative_store)
@@ -210,7 +229,7 @@ class DDSketch(BaseDDSketch):
210229
"""
211230

212231
def __init__(self, relative_accuracy=None):
213-
232+
# type: (Optional[float]) -> None
214233
# Make sure the parameters are valid
215234
if relative_accuracy is None:
216235
relative_accuracy = DEFAULT_REL_ACC
@@ -234,7 +253,7 @@ class LogCollapsingLowestDenseDDSketch(BaseDDSketch):
234253
"""
235254

236255
def __init__(self, relative_accuracy=None, bin_limit=None):
237-
256+
# type: (Optional[float], Optional[int]) -> None
238257
# Make sure the parameters are valid
239258
if relative_accuracy is None:
240259
relative_accuracy = DEFAULT_REL_ACC
@@ -264,7 +283,7 @@ class LogCollapsingHighestDenseDDSketch(BaseDDSketch):
264283
"""
265284

266285
def __init__(self, relative_accuracy=None, bin_limit=None):
267-
286+
# type: (Optional[float], Optional[int]) -> None
268287
# Make sure the parameters are valid
269288
if relative_accuracy is None:
270289
relative_accuracy = DEFAULT_REL_ACC

ddsketch/mapping.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class KeyMapping(ABC):
4040
"""
4141

4242
def __init__(self, relative_accuracy, offset=0.0):
43+
# type: (float, float) -> None
4344
if relative_accuracy <= 0 or relative_accuracy >= 1:
4445
raise IllegalArgumentException("Relative accuracy must be between 0 and 1.")
4546
self.relative_accuracy = relative_accuracy
@@ -53,19 +54,23 @@ def __init__(self, relative_accuracy, offset=0.0):
5354

5455
@classmethod
5556
def from_gamma_offset(cls, gamma, offset):
57+
# type: (float, float) -> KeyMapping
5658
"""Constructor used by pb.proto"""
5759
relative_accuracy = (gamma - 1.0) / (gamma + 1.0)
5860
return cls(relative_accuracy, offset=offset)
5961

6062
@abstractmethod
6163
def _log_gamma(self, value):
64+
# type: (float) -> float
6265
"""Return (an approximation of) the logarithm of the value base gamma"""
6366

6467
@abstractmethod
6568
def _pow_gamma(self, value):
69+
# type: (float) -> float
6670
"""Return (an approximation of) gamma to the power value"""
6771

6872
def key(self, value):
73+
# type: (float) -> int
6974
"""
7075
Args:
7176
value (float)
@@ -75,6 +80,7 @@ def key(self, value):
7580
return int(math.ceil(self._log_gamma(value)) + self._offset)
7681

7782
def value(self, key):
83+
# type: (int) -> float
7884
"""
7985
Args:
8086
key (int)
@@ -91,13 +97,16 @@ class LogarithmicMapping(KeyMapping):
9197
"""
9298

9399
def __init__(self, relative_accuracy, offset=0.0):
100+
# type: (float, float) -> None
94101
super().__init__(relative_accuracy, offset=offset)
95102
self._multiplier *= math.log(2)
96103

97104
def _log_gamma(self, value):
105+
# type: (float) -> float
98106
return math.log2(value) * self._multiplier
99107

100108
def _pow_gamma(self, value):
109+
# type: (float) -> float
101110
return 2 ** (value / self._multiplier)
102111

103112

@@ -116,6 +125,7 @@ class LinearlyInterpolatedMapping(KeyMapping):
116125
linearly interpolating the logarithm in-between."""
117126

118127
def _log2_approx(self, value):
128+
# type: (float) -> float
119129
"""approximates log2 by s + f
120130
where v = (s+1) * 2 ** f for s in [0, 1)
121131
@@ -128,15 +138,18 @@ def _log2_approx(self, value):
128138
return significand + (exponent - 1)
129139

130140
def _exp2_approx(self, value):
141+
# type: (float) -> float
131142
"""inverse of _log2_approx"""
132143
exponent = math.floor(value) + 1
133144
mantissa = (value - exponent + 2) / 2.0
134145
return math.ldexp(mantissa, exponent)
135146

136147
def _log_gamma(self, value):
148+
# type: (float) -> float
137149
return self._log2_approx(value) * self._multiplier
138150

139151
def _pow_gamma(self, value):
152+
# type: (float) -> float
140153
return self._exp2_approx(value / self._multiplier)
141154

142155

@@ -155,10 +168,12 @@ class CubicallyInterpolatedMapping(KeyMapping):
155168
C = 10 / 7
156169

157170
def __init__(self, relative_accuracy, offset=0.0):
171+
# type: (float, float) -> None
158172
super().__init__(relative_accuracy, offset=offset)
159173
self._multiplier /= self.C
160174

161175
def _cubic_log2_approx(self, value):
176+
# type: (float) -> float
162177
"""approximates log2 using a cubic polynomial"""
163178
mantissa, exponent = math.frexp(value)
164179
significand = 2 * mantissa - 1
@@ -167,6 +182,7 @@ def _cubic_log2_approx(self, value):
167182
) * significand + (exponent - 1)
168183

169184
def _cubic_exp2_approx(self, value):
185+
# type: (float) -> float
170186
"""Derived from Cardano's formula"""
171187

172188
exponent = math.floor(value)
@@ -187,7 +203,9 @@ def _cubic_exp2_approx(self, value):
187203
return math.ldexp(mantissa, exponent + 1)
188204

189205
def _log_gamma(self, value):
206+
# type: (float) -> float
190207
return self._cubic_log2_approx(value) * self._multiplier
191208

192209
def _pow_gamma(self, value):
210+
# type: (float) -> float
193211
return self._cubic_exp2_approx(value / self._multiplier)

ddsketch/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)