currency.py 2.7 KB

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