currency.py 2.8 KB

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