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 e8ab7fa commit 088bca1
Show file tree
Hide file tree
Showing 2 changed files with 100 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:
raise DivisionByZero
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
59 changes: 59 additions & 0 deletions tests/test_overdraft.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,62 @@ 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)),
],
)
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]

0 comments on commit 088bca1

Please sign in to comment.