Make currency values hashable.
[fulbank.git] / fulbank / currency.py
CommitLineData
0c362f2c
FT
1class currency(object):
2 def __init__(self, symbol):
3 self.symbol = symbol
4
5 def sformat(self, amount):
6 return "%s %s" % (self.symbol, self.format(amount))
7
8 def __repr__(self):
9 return "#<currency %s>" % self.symbol
10
11 @property
12 def zero(self):
13 return value(0, self)
14
15 _known = {}
16 @classmethod
17 def define(cls, *args, **kwargs):
18 self = cls(*args, **kwargs)
19 cls._known[self.symbol] = self
20 @classmethod
21 def get(cls, symbol):
22 return cls._known[symbol]
23
24 def __reduce__(self):
25 return _currency_restore, (type(self), self.symbol,)
26def _currency_restore(cls, symbol):
27 return cls.get(symbol)
28
29class integral(currency):
30 def __init__(self, symbol):
31 super().__init__(symbol)
32
33 def format(self, amount):
34 return "%i" % amount
35
36 def parse(self, text):
37 return value(int(text), self)
38
39class decimal(currency):
40 def __init__(self, symbol, separator, thseparator, decimals=2):
41 super().__init__(symbol)
42 self.separator = separator
43 self.thseparator = thseparator
44 self.decimals = decimals
45
46 def format(self, amount):
47 if amount < 0:
48 return "-" + self.format(-amount)
49 bias = 10 ** self.decimals
50 ip = amount // bias
51 fp = amount - (ip * bias)
52 return "%i.%0*i" % (ip, self.decimals, fp)
53
54 def parse(self, text):
55 def parse2(text):
56 p = text.find(self.separator)
57 bias = 10 ** self.decimals
58 if p < 0:
59 text = text.replace(self.thseparator)
60 if not text.isdigit():
61 raise ValueError(text)
62 return int(text) * bias
63 else:
64 if p != len(text) - 3:
65 raise ValueError(text)
66 ip = text[:p].replace(self.thseparator, "")
67 fp = text[p + 1:]
68 if not (ip.isdigit() and fp.isdigit()):
69 raise ValueError(text)
70 ip = int(ip)
71 fp = int(fp)
72 if fp >= bias:
73 raise ValueError(text)
74 return (ip * bias) + fp
75 if text[0:1] == "-":
76 return value(-parse2(text[1:]), self)
77 else:
78 return value(parse2(text), self)
79
80decimal.define("SEK", ",", " ")
81decimal.define("USD", ".", ",")
82integral.define("JPY")
83
84class value(object):
85 __slots__ = ["amount", "currency"]
86 def __init__(self, amount, currency):
87 self.amount = int(amount)
88 self.currency = currency
89
90 def __repr__(self):
91 return "%s %s" % (self.currency.symbol, self.currency.format(self.amount))
92
93 def __add__(self, other):
94 if self.currency != other.currency:
95 raise ValueError("cannot add %s to %s" % (other.currency.symbol, self.currency.symbol))
96 return value(int(self.amount + other.amount), self.currency)
97 def __sub__(self, other):
98 if self.currency != other.currency:
99 raise ValueError("cannot subtract %s from %s" % (other.currency.symbol, self.currency.symbol))
100 return value(int(self.amount - other.amount), self.currency)
101 def __mul__(self, other):
102 return value(int(self.amount * other), self.currency)
103 def __truediv__(self, other):
104 if self.currency != other.currency:
105 raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
106 return self.amount / other.amount
107 def __floordiv__(self, other):
108 if self.currency != other.currency:
109 raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
110 return self.amount // other.amount
111 def __mod__(self, other):
112 if self.currency != other.currency:
113 raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
114 return value(int(self.amount % other.amount), self.currency)
115 def __divmod__(self, other):
116 if self.currency != other.currency:
117 raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
118 return (self.amount // other.amount, value(int(self.amount % other.amount), self.currency))
119 def __neg__(self):
120 return value(-self.amount, self.currency)
121
122 def __eq__(self, other):
123 if self.currency != other.currency:
124 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
125 return self.amount == other.amount
126 def __ne__(self, other):
127 if self.currency != other.currency:
128 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
129 return self.amount != other.amount
130 def __lt__(self, other):
131 if self.currency != other.currency:
132 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
133 return self.amount < other.amount
134 def __le__(self, other):
135 if self.currency != other.currency:
136 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
137 return self.amount <= other.amount
138 def __gt__(self, other):
139 if self.currency != other.currency:
140 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
141 return self.amount > other.amount
142 def __ge__(self, other):
143 if self.currency != other.currency:
144 raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
145 return self.amount >= other.amount
619e377b
FT
146
147 def __hash__(self):
148 return hash(self.amount) + hash(self.currency)