country.py 2.7 KB

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