Skip to content

Commit

Permalink
feature: Implement Overdraft truediv
Browse files Browse the repository at this point in the history
  • Loading branch information
antonagestam committed Nov 5, 2023
1 parent 2e2943a commit 534d51e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/immoney/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,17 @@ def from_money(
money.currency,
)

@classmethod
def from_overdraft(
cls,
overdraft: Overdraft[C_co],
denominator: int | Fraction = 1,
) -> SubunitFraction[C_co]:
return SubunitFraction(
Fraction(-overdraft.subunits, denominator),
overdraft.currency,
)

def _round_subunit(self, rounding: Round) -> int:
remainder = self.value % 1

Expand Down Expand Up @@ -753,6 +764,36 @@ def __rmul__(
) -> Money[C_co] | SubunitFraction[C_co] | Self:
return self.__mul__(other)

@overload
def __truediv__(self, other: int) -> SubunitFraction[C_co]:
...

@overload
def __truediv__(self, other: Fraction) -> SubunitFraction[C_co]:
...

def __truediv__(self, other: object) -> SubunitFraction[C_co]:
if not isinstance(other, (int, Fraction)):
return NotImplemented
if other == 0:
raise DivisionByZero
return SubunitFraction.from_overdraft(self, other)

@overload
def __rtruediv__(self, other: int) -> SubunitFraction[C_co]:
...

@overload
def __rtruediv__(self, other: Fraction) -> SubunitFraction[C_co]:
...

def __rtruediv__(self, other: object) -> SubunitFraction[C_co]:
if not isinstance(other, (int, Fraction)):
return NotImplemented
if other == 0:
return SubunitFraction(0, self.currency)
return 1 / SubunitFraction.from_overdraft(self, other)

@classmethod
# This needs HKT to allow typing to work properly for subclasses of Overdraft, that
# would also allow moving the implementation to the shared super-class.
Expand Down
65 changes: 65 additions & 0 deletions tests/test_overdraft.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from immoney.currencies import NOK
from immoney.currencies import SEK
from immoney.currencies import SEKType
from immoney.errors import DivisionByZero
from immoney.errors import FrozenInstanceError
from immoney.errors import InvalidOverdraftValue
from immoney.errors import ParseError
Expand Down Expand Up @@ -373,3 +374,67 @@ def test_raises_type_error_for_invalid_other(
match=(r"^unsupported operand type\(s\) for \*: '(\w+)' and 'Overdraft'$"),
):
b * a # type: ignore[operator]


class TestTruediv:
@pytest.mark.parametrize(
("a", "b", "expected"),
[
(SEK.overdraft("0.75"), 2, SEK.fraction(-75, 2)),
(SEK.overdraft("0.75"), Fraction(1, 3), SEK.fraction(-225)),
],
)
def test_can_truediv(
self,
a: SubunitFraction[SEKType],
b: int | Fraction,
expected: SubunitFraction[SEKType],
) -> None:
assert_type(a / b, SubunitFraction[SEKType])
assert a / b == expected

@pytest.mark.parametrize(
("a", "b", "expected"),
[
(2, SEK.overdraft("0.75"), SEK.fraction(-2, 75)),
(Fraction(1, 3), SEK.overdraft("0.75"), SEK.fraction(-1, 225)),
(0, SEK.overdraft("0.75"), SEK.fraction(0)),
],
)
def test_can_rtruediv(
self,
a: int | Fraction,
b: SubunitFraction[SEKType],
expected: SubunitFraction[SEKType],
) -> None:
assert_type(a / b, SubunitFraction[SEKType])
assert a / b == expected

@pytest.mark.parametrize(
("a", "b"),
(
(SEK.overdraft(3), SEK.overdraft(3)),
(SEK.overdraft(1), SEK(1)),
(SEK.overdraft(1), NOK.overdraft(1)),
(SEK.overdraft(1), object()),
),
)
def test_raises_type_error_for_invalid_other(
self,
a: Overdraft[Currency],
b: object,
) -> None:
with pytest.raises(
TypeError,
match=r"^unsupported operand type\(s\) for /: 'Overdraft' and",
):
a / b # type: ignore[operator]
with pytest.raises(
TypeError,
match=(r"^unsupported operand type\(s\) for /: '(\w+)' and 'Overdraft'$"),
):
b / a # type: ignore[operator]

def test_raises_division_by_zero(self) -> None:
with pytest.raises(DivisionByZero):
SEK.overdraft(1) / 0

0 comments on commit 534d51e

Please sign in to comment.