#!/usr/bin/env python3
-from decimal import Decimal
-import re
-from typing import Optional, TypeVar, Tuple
+# © Copyright 2021-2022, Scott Gasch
-import math_utils
+"""A class to represent money. See also centcount.py"""
+import re
+from decimal import Decimal
+from typing import Optional, Tuple
-T = TypeVar('T', bound='Money')
+import math_utils
class Money(object):
different currencies.
"""
- def __init__ (
- self,
- amount: Decimal = Decimal("0"),
- currency: str = 'USD',
- *,
- strict_mode = False
+ def __init__(
+ self,
+ amount: Decimal = Decimal("0"),
+ currency: str = 'USD',
+ *,
+ strict_mode=False,
):
self.strict_mode = strict_mode
if isinstance(amount, str):
if not currency:
self.currency: Optional[str] = None
else:
- self.currency: Optional[str] = currency
+ self.currency = currency
def __repr__(self):
a = float(self.amount)
a = round(a, 2)
s = f'{a:,.2f}'
if self.currency is not None:
- return '%s %s' % (s, self.currency)
+ return f'{s} {self.currency}'
else:
- return '$%s' % s
+ return f'${s}'
def __pos__(self):
return Money(amount=self.amount, currency=self.currency)
def __add__(self, other):
if isinstance(other, Money):
if self.currency == other.currency:
- return Money(
- amount = self.amount + other.amount,
- currency = self.currency
- )
+ return Money(amount=self.amount + other.amount, currency=self.currency)
else:
raise TypeError('Incompatible currencies in add expression')
else:
raise TypeError('In strict_mode only two moneys can be added')
else:
return Money(
- amount = self.amount + Decimal(float(other)),
- currency = self.currency
+ amount=self.amount + Decimal(float(other)),
+ currency=self.currency,
)
def __sub__(self, other):
if isinstance(other, Money):
if self.currency == other.currency:
- return Money(
- amount = self.amount - other.amount,
- currency = self.currency
- )
+ return Money(amount=self.amount - other.amount, currency=self.currency)
else:
raise TypeError('Incompatible currencies in add expression')
else:
raise TypeError('In strict_mode only two moneys can be added')
else:
return Money(
- amount = self.amount - Decimal(float(other)),
- currency = self.currency
+ amount=self.amount - Decimal(float(other)),
+ currency=self.currency,
)
def __mul__(self, other):
raise TypeError('can not multiply monetary quantities')
else:
return Money(
- amount = self.amount * Decimal(float(other)),
- currency = self.currency
+ amount=self.amount * Decimal(float(other)),
+ currency=self.currency,
)
def __truediv__(self, other):
raise TypeError('can not divide monetary quantities')
else:
return Money(
- amount = self.amount / Decimal(float(other)),
- currency = self.currency
+ amount=self.amount / Decimal(float(other)),
+ currency=self.currency,
)
def __float__(self):
def __rsub__(self, other):
if isinstance(other, Money):
if self.currency == other.currency:
- return Money(
- amount = other.amount - self.amount,
- currency = self.currency
- )
+ return Money(amount=other.amount - self.amount, currency=self.currency)
else:
raise TypeError('Incompatible currencies in sub expression')
else:
raise TypeError('In strict_mode only two moneys can be added')
else:
return Money(
- amount = Decimal(float(other)) - self.amount,
- currency = self.currency
+ amount=Decimal(float(other)) - self.amount,
+ currency=self.currency,
)
__rmul__ = __mul__
if other is None:
return False
if isinstance(other, Money):
- return (
- self.amount == other.amount and
- self.currency == other.currency
- )
+ return self.amount == other.amount and self.currency == other.currency
if self.strict_mode:
raise TypeError("In strict mode only two Moneys can be compared")
else:
def __ge__(self, other):
return self > other or self == other
- def __hash__(self):
- return self.__repr__
+ def __hash__(self) -> int:
+ return hash(self.__repr__)
- AMOUNT_RE = re.compile("^([+|-]?)(\d+)(\.\d+)$")
- CURRENCY_RE = re.compile("^[A-Z][A-Z][A-Z]$")
+ AMOUNT_RE = re.compile(r"^([+|-]?)(\d+)(\.\d+)$")
+ CURRENCY_RE = re.compile(r"^[A-Z][A-Z][A-Z]$")
@classmethod
def _parse(cls, s: str) -> Optional[Tuple[Decimal, str]]:
return None
@classmethod
- def parse(cls, s: str) -> T:
+ def parse(cls, s: str) -> 'Money':
chunks = Money._parse(s)
if chunks is not None:
return Money(chunks[0], chunks[1])