0
# Numeric Types
1
2
Comprehensive numeric comparison types supporting integers, floats, decimals, and special float values with range checking, approximation, and sign validation. These types enable flexible validation of numeric data with various constraints and special value handling.
3
4
## Capabilities
5
6
### IsNumeric
7
8
Base class for all numeric comparisons providing range checking, approximate matching, and bound validation. Serves as the foundation for all specialized numeric types.
9
10
```python { .api }
11
class IsNumeric(DirtyEquals):
12
"""
13
Base class for numeric comparisons with configurable constraints.
14
15
Supports range checking, approximate matching, and bound validation
16
for numeric values including dates and datetimes.
17
"""
18
19
def __init__(
20
self,
21
*,
22
exactly: Optional[Union[int, float, Decimal]] = None,
23
approx: Optional[Union[int, float, Decimal]] = None,
24
delta: Optional[Union[int, float, Decimal]] = None,
25
gt: Optional[Union[int, float, Decimal]] = None,
26
lt: Optional[Union[int, float, Decimal]] = None,
27
ge: Optional[Union[int, float, Decimal]] = None,
28
le: Optional[Union[int, float, Decimal]] = None
29
):
30
"""
31
Initialize numeric comparison with constraints.
32
33
Args:
34
exactly: Exact value to match
35
approx: Approximate value for comparison
36
delta: Tolerance for approximate comparison
37
gt: Value must be greater than this
38
lt: Value must be less than this
39
ge: Value must be greater than or equal to this
40
le: Value must be less than or equal to this
41
"""
42
43
allowed_types: ClassVar[Tuple[type, ...]] = (int, float, Decimal, date, datetime)
44
45
def prepare(self) -> None:
46
"""Prepare and validate constraint parameters."""
47
48
def bounds_checks(self, other: Union[int, float, Decimal]) -> bool:
49
"""
50
Perform range bound checking.
51
52
Args:
53
other: Numeric value to check
54
55
Returns:
56
bool: True if value satisfies all bound constraints
57
"""
58
59
def approx_equals(
60
self,
61
other: Union[int, float, Decimal],
62
delta: Union[int, float, Decimal]
63
) -> bool:
64
"""
65
Check approximate equality within tolerance.
66
67
Args:
68
other: Value to compare
69
delta: Maximum allowed difference
70
71
Returns:
72
bool: True if values are within delta of each other
73
"""
74
75
def equals(self, other: Any) -> bool:
76
"""
77
Check if value matches numeric constraints.
78
79
Args:
80
other: Value to validate
81
82
Returns:
83
bool: True if value satisfies all conditions
84
"""
85
```
86
87
#### Usage Examples
88
89
```python
90
from dirty_equals import IsNumeric
91
from decimal import Decimal
92
93
# Exact value matching
94
assert 42 == IsNumeric(exactly=42)
95
assert 3.14 == IsNumeric(exactly=3.14)
96
97
# Approximate matching
98
assert 3.14159 == IsNumeric(approx=3.14, delta=0.01)
99
assert 100 == IsNumeric(approx=99, delta=2)
100
101
# Range constraints
102
assert 50 == IsNumeric(gt=0, lt=100)
103
assert 25 == IsNumeric(ge=25, le=75)
104
105
# Combining constraints
106
assert 42 == IsNumeric(gt=0, le=100, approx=40, delta=5)
107
108
# Works with Decimal for high precision
109
big_decimal = Decimal('123.456789')
110
assert big_decimal == IsNumeric(approx=Decimal('123.45'), delta=Decimal('0.01'))
111
112
# Date and datetime support
113
from datetime import date, datetime
114
today = date.today()
115
assert today == IsNumeric(ge=date(2023, 1, 1))
116
117
now = datetime.now()
118
assert now == IsNumeric(gt=datetime(2023, 1, 1))
119
```
120
121
### IsNumber
122
123
Base class specifically for numeric types (int, float, Decimal), excluding date/datetime objects that IsNumeric supports.
124
125
```python { .api }
126
class IsNumber(IsNumeric):
127
"""
128
Base class for pure number types (excludes dates).
129
130
Inherits all functionality from IsNumeric but restricts
131
allowed types to int, float, and Decimal only.
132
"""
133
134
allowed_types: ClassVar[Tuple[type, ...]] = (int, float, Decimal)
135
```
136
137
#### Usage Examples
138
139
```python
140
from dirty_equals import IsNumber
141
142
# Number validation (excludes dates)
143
assert 42 == IsNumber
144
assert 3.14 == IsNumber
145
assert Decimal('123.45') == IsNumber
146
147
# Range constraints for numbers only
148
assert 25 == IsNumber(gt=0, lt=100)
149
assert 50 == IsNumber(approx=48, delta=5)
150
151
# Dates/datetimes won't match
152
from datetime import date
153
# assert date.today() == IsNumber # Would fail - dates not allowed
154
```
155
156
### IsApprox
157
158
Specialized class for approximate number comparison with configurable tolerance.
159
160
```python { .api }
161
class IsApprox(IsNumber):
162
"""
163
Approximate number comparison with tolerance.
164
165
Validates that a number is within a specified delta
166
of the expected approximate value.
167
"""
168
169
def __init__(
170
self,
171
approx: Union[int, float, Decimal],
172
*,
173
delta: Optional[Union[int, float, Decimal]] = None
174
):
175
"""
176
Initialize approximate comparison.
177
178
Args:
179
approx: Expected approximate value
180
delta: Maximum allowed difference (default based on value magnitude)
181
"""
182
```
183
184
#### Usage Examples
185
186
```python
187
from dirty_equals import IsApprox
188
189
# Basic approximate matching
190
assert 3.14159 == IsApprox(3.14, delta=0.01)
191
assert 100 == IsApprox(99, delta=2)
192
193
# Default delta (implementation specific)
194
assert 42.001 == IsApprox(42) # Uses default tolerance
195
196
# Mathematical calculations with floating point precision
197
import math
198
calculated_pi = 22/7 # Approximation of pi
199
assert calculated_pi == IsApprox(math.pi, delta=0.01)
200
201
# API numeric responses
202
api_response = {
203
'cpu_usage': 85.7,
204
'memory_usage': 67.3,
205
'disk_usage': 23.8
206
}
207
208
expected_metrics = {
209
'cpu_usage': IsApprox(85, delta=5),
210
'memory_usage': IsApprox(70, delta=5),
211
'disk_usage': IsApprox(25, delta=5)
212
}
213
214
assert api_response == expected_metrics
215
```
216
217
### Sign-Based Validation Types
218
219
Types for validating the sign (positive/negative/zero) of numeric values.
220
221
```python { .api }
222
class IsPositive(IsNumber):
223
"""Checks value > 0."""
224
225
class IsNegative(IsNumber):
226
"""Checks value < 0."""
227
228
class IsNonNegative(IsNumber):
229
"""Checks value >= 0."""
230
231
class IsNonPositive(IsNumber):
232
"""Checks value <= 0."""
233
```
234
235
#### Usage Examples
236
237
```python
238
from dirty_equals import IsPositive, IsNegative, IsNonNegative, IsNonPositive
239
240
# Positive values (> 0)
241
assert 42 == IsPositive
242
assert 3.14 == IsPositive
243
assert 0.001 == IsPositive
244
245
# Negative values (< 0)
246
assert -42 == IsNegative
247
assert -3.14 == IsNegative
248
assert -0.001 == IsNegative
249
250
# Non-negative values (>= 0)
251
assert 42 == IsNonNegative
252
assert 0 == IsNonNegative # Zero is non-negative
253
254
# Non-positive values (<= 0)
255
assert -42 == IsNonPositive
256
assert 0 == IsNonPositive # Zero is non-positive
257
258
# In data validation
259
user_stats = {
260
'points': 150,
261
'balance': -25.50,
262
'login_count': 0,
263
'penalty_score': -10
264
}
265
266
assert user_stats == {
267
'points': IsPositive,
268
'balance': IsNegative,
269
'login_count': IsNonNegative, # Can be zero
270
'penalty_score': IsNonPositive # Zero or negative
271
}
272
```
273
274
### Integer Types
275
276
Specialized integer validation types with sign constraints.
277
278
```python { .api }
279
class IsInt(IsNumber):
280
"""Integer type checking."""
281
allowed_types: ClassVar[type] = int
282
283
class IsPositiveInt(IsInt):
284
"""Positive integer checking (> 0)."""
285
286
class IsNegativeInt(IsInt):
287
"""Negative integer checking (< 0)."""
288
```
289
290
#### Usage Examples
291
292
```python
293
from dirty_equals import IsInt, IsPositiveInt, IsNegativeInt
294
295
# Basic integer checking
296
assert 42 == IsInt
297
assert 0 == IsInt
298
assert -15 == IsInt
299
300
# Floats won't match
301
# assert 3.14 == IsInt # Would fail
302
303
# Positive integers
304
assert 42 == IsPositiveInt
305
assert 1 == IsPositiveInt
306
# assert 0 == IsPositiveInt # Would fail - zero is not positive
307
# assert -5 == IsPositiveInt # Would fail
308
309
# Negative integers
310
assert -42 == IsNegativeInt
311
assert -1 == IsNegativeInt
312
# assert 0 == IsNegativeInt # Would fail - zero is not negative
313
# assert 5 == IsNegativeInt # Would fail
314
315
# Database ID validation
316
user_record = {
317
'id': 123,
318
'parent_id': 456,
319
'sort_order': -1 # Negative for reverse order
320
}
321
322
assert user_record == {
323
'id': IsPositiveInt, # IDs should be positive
324
'parent_id': IsPositiveInt,
325
'sort_order': IsNegativeInt # Negative sort order
326
}
327
328
# API pagination parameters
329
pagination = {
330
'page': 1,
331
'per_page': 25,
332
'offset': 0,
333
'total_count': 150
334
}
335
336
assert pagination == {
337
'page': IsPositiveInt,
338
'per_page': IsPositiveInt,
339
'offset': IsInt, # Can be zero
340
'total_count': IsInt # Can be zero
341
}
342
```
343
344
### Float Types
345
346
Specialized float validation including special float values (infinity, NaN).
347
348
```python { .api }
349
class IsFloat(IsNumber):
350
"""Float type checking."""
351
allowed_types: ClassVar[type] = float
352
353
class IsPositiveFloat(IsFloat):
354
"""Positive float checking (> 0)."""
355
356
class IsNegativeFloat(IsFloat):
357
"""Negative float checking (< 0)."""
358
359
class IsFloatInf(IsFloat):
360
"""Infinite float checking (positive or negative)."""
361
362
class IsFloatInfPos(IsFloat):
363
"""Positive infinite float checking."""
364
365
class IsFloatInfNeg(IsFloat):
366
"""Negative infinite float checking."""
367
368
class IsFloatNan(IsFloat):
369
"""NaN (Not a Number) float checking."""
370
```
371
372
#### Usage Examples
373
374
```python
375
from dirty_equals import (
376
IsFloat, IsPositiveFloat, IsNegativeFloat,
377
IsFloatInf, IsFloatInfPos, IsFloatInfNeg, IsFloatNan
378
)
379
import math
380
381
# Basic float checking
382
assert 3.14 == IsFloat
383
assert 0.0 == IsFloat
384
assert -2.718 == IsFloat
385
386
# Integers won't match
387
# assert 42 == IsFloat # Would fail
388
389
# Positive floats
390
assert 3.14 == IsPositiveFloat
391
assert 0.001 == IsPositiveFloat
392
# assert 0.0 == IsPositiveFloat # Would fail - zero not positive
393
# assert -1.5 == IsPositiveFloat # Would fail
394
395
# Negative floats
396
assert -3.14 == IsNegativeFloat
397
assert -0.001 == IsNegativeFloat
398
# assert 0.0 == IsNegativeFloat # Would fail - zero not negative
399
# assert 1.5 == IsNegativeFloat # Would fail
400
401
# Special float values
402
assert float('inf') == IsFloatInf
403
assert float('-inf') == IsFloatInf
404
assert float('inf') == IsFloatInfPos
405
assert float('-inf') == IsFloatInfNeg
406
assert float('nan') == IsFloatNan
407
408
# Mathematical calculations with special values
409
calculation_results = {
410
'result1': 1.0 / 0.0, # Positive infinity
411
'result2': -1.0 / 0.0, # Negative infinity
412
'result3': 0.0 / 0.0, # NaN
413
'result4': math.sqrt(2.0) # Normal float
414
}
415
416
assert calculation_results == {
417
'result1': IsFloatInfPos,
418
'result2': IsFloatInfNeg,
419
'result3': IsFloatNan,
420
'result4': IsPositiveFloat
421
}
422
423
# Scientific computation validation
424
scientific_data = {
425
'temperature': 273.15,
426
'pressure': -1.5, # Negative relative pressure
427
'infinity_check': float('inf'),
428
'error_value': float('nan')
429
}
430
431
assert scientific_data == {
432
'temperature': IsPositiveFloat,
433
'pressure': IsNegativeFloat,
434
'infinity_check': IsFloatInf,
435
'error_value': IsFloatNan
436
}
437
438
# Numeric analysis results
439
analysis = {
440
'mean': 45.7,
441
'std_dev': 12.3,
442
'min_value': -infinity_placeholder, # Could be negative infinity
443
'max_value': infinity_placeholder, # Could be positive infinity
444
'invalid_calculations': [float('nan'), float('nan')]
445
}
446
447
# Validate statistical outputs
448
assert analysis == {
449
'mean': IsFloat,
450
'std_dev': IsPositiveFloat, # Standard deviation is always positive
451
'min_value': IsFloatInfNeg | IsFloat, # Could be normal float or -inf
452
'max_value': IsFloatInfPos | IsFloat, # Could be normal float or +inf
453
'invalid_calculations': [IsFloatNan, IsFloatNan]
454
}
455
```
456
457
## Type Definitions
458
459
```python { .api }
460
from typing import Any, ClassVar, Optional, Tuple, Union
461
from decimal import Decimal
462
from datetime import date, datetime
463
464
# All numeric types inherit from IsNumeric/IsNumber which inherit from DirtyEquals
465
# They work with Python's standard numeric types and the decimal module
466
```