1from .. import i18n, ImproperlyConfigured
2from ..utils import str_coercible
3
4
5@str_coercible
6class Currency:
7 """
8 Currency class wraps a 3-letter currency code. It provides various
9 convenience properties and methods.
10
11 ::
12
13 from babel import Locale
14 from sqlalchemy_utils import Currency, i18n
15
16
17 # First lets add a locale getter for testing purposes
18 i18n.get_locale = lambda: Locale('en')
19
20
21 Currency('USD').name # US Dollar
22 Currency('USD').symbol # $
23
24 Currency(Currency('USD')).code # 'USD'
25
26 Currency always validates the given code if you use at least the optional
27 dependency list 'babel', otherwise no validation are performed.
28
29 ::
30
31 Currency(None) # raises TypeError
32
33 Currency('UnknownCode') # raises ValueError
34
35
36 Currency supports equality operators.
37
38 ::
39
40 Currency('USD') == Currency('USD')
41 Currency('USD') != Currency('EUR')
42
43
44 Currencies are hashable.
45
46
47 ::
48
49 len(set([Currency('USD'), Currency('USD')])) # 1
50
51
52 """
53
54 def __init__(self, code):
55 if i18n.babel is None:
56 raise ImproperlyConfigured(
57 "'babel' package is required in order to use Currency class."
58 )
59 if isinstance(code, Currency):
60 self.code = code
61 elif isinstance(code, str):
62 self.validate(code)
63 self.code = code
64 else:
65 raise TypeError(
66 'First argument given to Currency constructor should be '
67 'either an instance of Currency or valid three letter '
68 'currency code.'
69 )
70
71 @classmethod
72 def validate(self, code):
73 try:
74 i18n.babel.Locale('en').currencies[code]
75 except KeyError:
76 raise ValueError(f"'{code}' is not valid currency code.")
77 except AttributeError:
78 # As babel is optional, we may raise an AttributeError accessing it
79 pass
80
81 @property
82 def symbol(self):
83 return i18n.babel.numbers.get_currency_symbol(self.code, i18n.get_locale())
84
85 @property
86 def name(self):
87 return i18n.get_locale().currencies[self.code]
88
89 def __eq__(self, other):
90 if isinstance(other, Currency):
91 return self.code == other.code
92 elif isinstance(other, str):
93 return self.code == other
94 else:
95 return NotImplemented
96
97 def __ne__(self, other):
98 return not (self == other)
99
100 def __hash__(self):
101 return hash(self.code)
102
103 def __repr__(self):
104 return f'{self.__class__.__name__}({self.code!r})'
105
106 def __unicode__(self):
107 return self.code