Skip to content

Commit

Permalink
numeric exact (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Apr 27, 2023
1 parent 6ca308e commit b8909ec
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
21 changes: 18 additions & 3 deletions dirty_equals/_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class IsNumeric(DirtyEquals[N]):
def __init__(
self,
*,
exactly: Optional[N] = None,
approx: Optional[N] = None,
delta: Optional[N] = None,
gt: Optional[N] = None,
Expand All @@ -50,6 +51,8 @@ def __init__(
):
"""
Args:
exactly: A value to exactly compare to - useful when you want to make sure a value is an `int` or `float`,
while also checking its value.
approx: A value to approximately compare to.
delta: The allowable different when comparing to the value to `approx`,
if omitted `value / 100` is used except for datetimes where 2 seconds is used.
Expand All @@ -74,16 +77,22 @@ def __init__(
assert d == IsNumeric(approx=datetime(2020, 1, 1, 12, 0, 1))
```
"""
self.exactly: Optional[N] = exactly
if self.exactly is not None and (gt, lt, ge, le) != (None, None, None, None):
raise TypeError('"exactly" cannot be combined with "gt", "lt", "ge", or "le"')
if self.exactly is not None and approx is not None:
raise TypeError('"exactly" cannot be combined with "approx"')
self.approx: Optional[N] = approx
self.delta: Optional[N] = delta
if self.approx is not None and (gt, lt, ge, le) != (None, None, None, None):
raise TypeError('"approx" cannot be combined with "gt", "lt", "ge", or "le"')
self.delta: Optional[N] = delta
self.gt: Optional[N] = gt
self.lt: Optional[N] = lt
self.ge: Optional[N] = ge
self.le: Optional[N] = le
self.has_bounds_checks = not all(f is None for f in (approx, delta, gt, lt, ge, le))
self.has_bounds_checks = not all(f is None for f in (exactly, approx, delta, gt, lt, ge, le))
kwargs = {
'exactly': Omit if exactly is None else exactly,
'approx': Omit if approx is None else approx,
'delta': Omit if delta is None else delta,
'gt': Omit if gt is None else gt,
Expand All @@ -110,7 +119,9 @@ def equals(self, other: Any) -> bool:
return True

def bounds_checks(self, other: N) -> bool:
if self.approx is not None:
if self.exactly is not None:
return self.exactly == other
elif self.approx is not None:
if self.delta is None:
if isinstance(other, date):
delta: Any = timedelta(seconds=2)
Expand Down Expand Up @@ -278,6 +289,8 @@ class IsInt(IsNumeric[int]):
assert 1.0 != IsInt
assert 'foobar' != IsInt
assert True != IsInt
assert 1 == IsInt(exactly=1)
assert -2 != IsInt(exactly=1)
```
"""

Expand Down Expand Up @@ -341,6 +354,8 @@ class IsFloat(IsNumeric[float]):
assert 1.0 == IsFloat
assert 1 != IsFloat
assert 1.0 == IsFloat(exactly=1.0)
assert 1.001 != IsFloat(exactly=1.0)
```
"""

Expand Down
18 changes: 17 additions & 1 deletion tests/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
[
(1, IsInt),
(1, IsInt()),
(1, IsInt(exactly=1)),
(1, IsPositiveInt),
(-1, IsNegativeInt),
(-1.0, IsFloat),
(-1.0, IsFloat(exactly=-1.0)),
(1.0, IsPositiveFloat),
(-1.0, IsNegativeFloat),
(1, IsPositive),
Expand Down Expand Up @@ -72,6 +74,7 @@ def test_dirty_equals(other, dirty):
[
(1.0, IsInt),
(1.2, IsInt),
(1, IsInt(exactly=2)),
(True, IsInt),
(False, IsInt),
(1.0, IsInt()),
Expand All @@ -80,6 +83,8 @@ def test_dirty_equals(other, dirty):
(1, IsNegativeInt),
(0, IsNegativeInt),
(1, IsFloat),
(1, IsFloat(exactly=1.0)),
(1.1234, IsFloat(exactly=1.0)),
(-1.0, IsPositiveFloat),
(0.0, IsPositiveFloat),
(1.0, IsNegativeFloat),
Expand All @@ -105,16 +110,27 @@ def test_dirty_equals(other, dirty):
(-float('-inf'), IsFloatInfNeg),
(-float('-inf'), IsFloatInfNeg),
],
ids=repr,
)
def test_dirty_not_equals(other, dirty):
assert other != dirty


def test_invalid():
def test_invalid_approx_gt():
with pytest.raises(TypeError, match='"approx" cannot be combined with "gt", "lt", "ge", or "le"'):
IsInt(approx=1, gt=1)


def test_invalid_exactly_approx():
with pytest.raises(TypeError, match='"exactly" cannot be combined with "approx"'):
IsInt(exactly=1, approx=1)


def test_invalid_exactly_gt():
with pytest.raises(TypeError, match='"exactly" cannot be combined with "gt", "lt", "ge", or "le"'):
IsInt(exactly=1, gt=1)


def test_not_int():
d = IsInt()
with pytest.raises(AssertionError):
Expand Down

0 comments on commit b8909ec

Please sign in to comment.