Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions babel/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,29 @@ class UnknownLocaleError(Exception):
is available.
"""

def __init__(self, identifier):
def __init__(self, identifier, message=None):
"""Create the exception.

:param identifier: the identifier string of the unsupported locale
:param message: an optional message string to override the default
"""
Exception.__init__(self, 'unknown locale %r' % identifier)
if not message:
message = 'unknown locale %r' % identifier
Exception.__init__(self, message)

#: The identifier of the locale that could not be found.
self.identifier = identifier


class InvalidLocaleSpecificationError(ValueError):
def __init__(self, identifier, part_name, value):
ValueError.__init__(
self,
"The %s (%r) of the locale identifier %r is invalid." % (part_name, value, identifier)
)
self.identifier = identifier


class Locale(object):
"""Representation of a specific locale.

Expand Down Expand Up @@ -156,16 +168,34 @@ def __init__(self, language, territory=None, script=None, variant=None):
requested locale
"""
#: the language code
self.language = language
self.language = str(language)
#: the territory (country or region) code
self.territory = territory
self.territory = (str(territory) if territory else None)
#: the script code
self.script = script
self.script = (str(script) if script else None)
#: the variant code
self.variant = variant
self.variant = (str(variant) if variant else None)
self.__data = None

self._validate()

def _validate(self):
"""
Validate that the locale parameters seem sane, and that the locale is known.
"""
identifier = str(self)
if not self.language.isalpha():
raise InvalidLocaleSpecificationError(identifier, "language", self.language)

if self.territory and not (self.territory.isdigit() or (self.territory.isalpha() and self.territory.isupper())):
raise InvalidLocaleSpecificationError(identifier, "territory", self.territory)

if self.script and not self.script.isalpha():
raise InvalidLocaleSpecificationError(identifier, "script", self.script)

if self.variant and not self.variant.isalpha():
raise InvalidLocaleSpecificationError(identifier, "variant", self.variant)

if not localedata.exists(identifier):
raise UnknownLocaleError(identifier)

Expand Down Expand Up @@ -808,7 +838,7 @@ def interval_formats(self):
How to format date intervals in Finnish when the day is the
smallest changing component:

>>> Locale('fi_FI').interval_formats['MEd']['d']
>>> Locale('fi', 'FI').interval_formats['MEd']['d']
[u'E d. \u2013 ', u'E d.M.']

.. seealso::
Expand Down Expand Up @@ -846,7 +876,7 @@ def list_patterns(self):
u'{0}, {1}'
>>> Locale('en').list_patterns['end']
u'{0}, and {1}'
>>> Locale('en_GB').list_patterns['end']
>>> Locale('en', 'GB').list_patterns['end']
u'{0} and {1}'
"""
return self._data['list_patterns']
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ def os_environ(monkeypatch):
mock_environ = dict(os.environ)
monkeypatch.setattr(os, 'environ', mock_environ)
return mock_environ


def pytest_generate_tests(metafunc):
if hasattr(metafunc.function, "all_locales"):
from babel.localedata import locale_identifiers
metafunc.parametrize("locale", list(locale_identifiers()))
2 changes: 1 addition & 1 deletion tests/messages/test_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_2_num_plurals_checkers(self):
num_plurals = PLURALS[_locale][0]
plural_expr = PLURALS[_locale][1]
try:
locale = Locale(_locale)
locale = Locale.parse(_locale)
date = format_datetime(datetime.now(LOCALTZ),
'yyyy-MM-dd HH:mmZ',
tzinfo=LOCALTZ, locale=_locale)
Expand Down
27 changes: 23 additions & 4 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import pytest

from babel import core, Locale
from babel.core import default_locale, Locale
from babel.core import default_locale, Locale, InvalidLocaleSpecificationError


def test_locale_provides_access_to_cldr_locale_data():
Expand All @@ -35,14 +35,11 @@ def test_locale_comparison():
en_US = Locale('en', 'US')
en_US_2 = Locale('en', 'US')
fi_FI = Locale('fi', 'FI')
bad_en_US = Locale('en_US')
assert en_US == en_US
assert en_US == en_US_2
assert en_US != fi_FI
assert not (en_US != en_US_2)
assert None != en_US
assert en_US != bad_en_US
assert fi_FI != bad_en_US


def test_can_return_default_locale(os_environ):
Expand Down Expand Up @@ -315,3 +312,25 @@ def find_class(self, module, name):

with open(filename, 'rb') as f:
return Unpickler(f).load()


@pytest.mark.parametrize("parts", [
("en", "US", "Hans", "BAHAMAS"),
("yi", "001"),
])
def test_locale_ctor_validation(parts):
part_names = ("language", "territory", "script", "variant")
n = len(parts)
for i in range(n):
mangled_parts = list(parts)
for x in range(i, n):
mangled_parts[x] = "~~"
assert len(mangled_parts) == n
with pytest.raises(InvalidLocaleSpecificationError) as ei:
Locale(*mangled_parts)
assert part_names[i] in str(ei.value) # assert the complaint was about the first mangled part


@pytest.mark.all_locales
def test_locale_load(locale):
assert str(Locale.parse(locale)) == locale