Fix babel primitive types

- Make babel dependent primitive types to use Locale('en') for data
validation instead of current locale. Using current locale leads to
infinite recursion in cases where the loaded data has dependency to
the loaded object's locale.
This commit is contained in:
Konsta Vesterinen
2015-06-17 16:45:51 +03:00
parent a6973658d3
commit 204aba376d
17 changed files with 47 additions and 70 deletions

View File

@@ -8,6 +8,7 @@ Here you can see the full list of changes between each SQLAlchemy-Utils release.
^^^^^^^^^^^^^^^^^^^^
- Added better support for dynamic locales in translation_hybrid
- Make babel dependent primitive types to use Locale('en') for data validation instead of current locale. Using current locale leads to infinite recursion in cases where the loaded data has dependency to the loaded object's locale.
0.30.9 (2015-06-09)

View File

@@ -92,4 +92,4 @@ from .types import ( # noqa
WeekDaysType
)
__version__ = '0.30.9'
__version__ = '0.30.10'

View File

@@ -3,18 +3,10 @@ from sqlalchemy.ext.hybrid import hybrid_property
from .exceptions import ImproperlyConfigured
from babel import Locale
try:
from babel.dates import get_day_names
import babel
except ImportError:
def get_day_names():
raise ImproperlyConfigured(
'Could not load get_day_names function from babel. Either install '
' babel or make a similar function and override it in this '
'module.'
)
babel = None
try:
from flask.ext.babel import get_locale
@@ -29,6 +21,10 @@ except ImportError:
class TranslationHybrid(object):
def __init__(self, current_locale, default_locale, default_value=None):
if babel is None:
raise ImproperlyConfigured(
'You need to install babel in order to use TranslationHybrid.'
)
self.current_locale = current_locale
self.default_locale = default_locale
self.default_value = default_value
@@ -43,8 +39,9 @@ class TranslationHybrid(object):
locale = locale()
except TypeError:
locale = locale(obj)
if isinstance(locale, Locale):
if isinstance(locale, babel.Locale):
return str(locale)
return locale
def getter_factory(self, attr):

View File

@@ -71,7 +71,7 @@ class Country(object):
@classmethod
def validate(self, code):
try:
i18n.get_locale().territories[code]
i18n.babel.Locale('en').territories[code]
except KeyError:
raise ValueError(
'Could not convert string to country code: {0}'.format(code)

View File

@@ -1,9 +1,4 @@
# -*- coding: utf-8 -*-
babel = None
try:
import babel
except ImportError:
pass
import six
from sqlalchemy_utils import i18n, ImproperlyConfigured
@@ -58,7 +53,7 @@ class Currency(object):
"""
def __init__(self, code):
if babel is None:
if i18n.babel is None:
raise ImproperlyConfigured(
"'babel' package is required in order to use Currency class."
)
@@ -77,13 +72,16 @@ class Currency(object):
@classmethod
def validate(self, code):
try:
i18n.get_locale().currencies[code]
i18n.babel.Locale('en').currencies[code]
except KeyError:
raise ValueError("{0}' is not valid currency code.")
@property
def symbol(self):
return babel.numbers.get_currency_symbol(self.code, i18n.get_locale())
return i18n.babel.numbers.get_currency_symbol(
self.code,
i18n.get_locale()
)
@property
def name(self):

View File

@@ -39,7 +39,7 @@ class WeekDay(object):
return self.name
def get_name(self, width='wide', context='format'):
names = i18n.get_day_names(
names = i18n.babel.dates.get_day_names(
width,
context,
i18n.get_locale()

View File

@@ -1,12 +1,7 @@
babel = None
try:
import babel
except ImportError:
pass
import six
from sqlalchemy import types
from sqlalchemy_utils import ImproperlyConfigured
from sqlalchemy_utils import i18n, ImproperlyConfigured
from sqlalchemy_utils.primitives import Currency
from .scalar_coercible import ScalarCoercible
@@ -57,7 +52,7 @@ class CurrencyType(types.TypeDecorator, ScalarCoercible):
python_type = Currency
def __init__(self, *args, **kwargs):
if babel is None:
if i18n.babel is None:
raise ImproperlyConfigured(
"'babel' package is required in order to use CurrencyType."
)

View File

@@ -1,11 +1,7 @@
babel = None
try:
import babel
except ImportError:
pass
import six
from sqlalchemy import types
from sqlalchemy_utils import i18n
from sqlalchemy_utils.exceptions import ImproperlyConfigured
from sqlalchemy_utils.primitives import WeekDay, WeekDays
@@ -57,7 +53,7 @@ class WeekDaysType(types.TypeDecorator, ScalarCoercible):
impl = BitType(WeekDay.NUM_WEEK_DAYS)
def __init__(self, *args, **kwargs):
if babel is None:
if i18n.babel is None:
raise ImproperlyConfigured(
"'babel' package is required to use 'WeekDaysType'"
)

View File

@@ -31,7 +31,7 @@ sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener)
def get_locale():
class Locale():
territories = {'fi': 'Finland'}
territories = {'FI': 'Finland'}
return Locale()

View File

@@ -2,13 +2,12 @@ import six
from pytest import mark, raises
from sqlalchemy_utils import Country, i18n
from sqlalchemy_utils.primitives.currency import babel # noqa
@mark.skipif('babel is None')
@mark.skipif('i18n.babel is None')
class TestCountry(object):
def setup_method(self, method):
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def test_init(self):
assert Country(u'FI') == Country(Country(u'FI'))

View File

@@ -3,13 +3,12 @@ import six
from pytest import mark, raises
from sqlalchemy_utils import Currency, i18n
from sqlalchemy_utils.primitives.currency import babel # noqa
@mark.skipif('babel is None')
@mark.skipif('i18n.babel is None')
class TestCurrency(object):
def setup_method(self, method):
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def test_init(self):
assert Currency('USD') == Currency(Currency('USD'))

View File

@@ -5,17 +5,11 @@ from flexmock import flexmock
from sqlalchemy_utils import i18n
from sqlalchemy_utils.primitives import WeekDay, WeekDays
Locale = None
try:
from babel import Locale
except ImportError:
pass
@pytest.mark.skipif('Locale is None')
@pytest.mark.skipif('i18n.babel is None')
class TestWeekDay(object):
def setup_method(self, method):
i18n.get_locale = lambda: Locale('fi')
i18n.get_locale = lambda: i18n.babel.Locale('fi')
def test_constructor_with_valid_index(self):
day = WeekDay(1)
@@ -88,7 +82,7 @@ class TestWeekDay(object):
assert str(day) == 'maanantaina'
@pytest.mark.skipif('Locale is None')
@pytest.mark.skipif('i18n.babel is None')
class TestWeekDays(object):
def test_constructor_with_valid_bit_string(self):
days = WeekDays('1000100')
@@ -161,11 +155,11 @@ class TestWeekDays(object):
assert indices == [1, 2, 3, 4, 5, 6, 0]
def test_unicode(self):
i18n.get_locale = lambda: Locale('fi')
i18n.get_locale = lambda: i18n.babel.Locale('fi')
days = WeekDays('1000100')
assert six.text_type(days) == u'maanantaina, perjantaina'
def test_str(self):
i18n.get_locale = lambda: Locale('fi')
i18n.get_locale = lambda: i18n.babel.Locale('fi')
days = WeekDays('1000100')
assert str(days) == 'maanantaina, perjantaina'

View File

@@ -2,10 +2,11 @@ import sqlalchemy as sa
from pytest import mark
from sqlalchemy.dialects.postgresql import HSTORE
from sqlalchemy_utils import TranslationHybrid
from sqlalchemy_utils import i18n, TranslationHybrid # noqa
from tests import TestCase
@mark.skipif('i18n.babel is None')
class TestTranslationHybrid(TestCase):
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'

View File

@@ -15,7 +15,6 @@ from sqlalchemy_utils import (
remove_composite_listeners
)
from sqlalchemy_utils.types import pg_composite
from sqlalchemy_utils.types.currency import babel
from sqlalchemy_utils.types.range import intervals
from tests import TestCase
@@ -52,13 +51,13 @@ class TestCompositeTypeWithRegularTypes(TestCase):
assert account.balance.amount == 15
@mark.skipif('babel is None')
@mark.skipif('i18n.babel is None')
class TestCompositeTypeWithTypeDecorators(TestCase):
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
def setup_method(self, method):
TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def create_models(self):
class Account(self.Base):
@@ -101,7 +100,7 @@ class TestCompositeTypeWithTypeDecorators(TestCase):
assert account.balance.amount == 15
@mark.skipif('babel is None')
@mark.skipif('i18n.babel is None')
class TestCompositeTypeInsideArray(TestCase):
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
@@ -115,7 +114,7 @@ class TestCompositeTypeInsideArray(TestCase):
)
TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def create_models(self):
class Account(self.Base):
@@ -159,7 +158,6 @@ class TestCompositeTypeWithRangeTypeInsideArray(TestCase):
)
TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')
def create_models(self):
class Account(self.Base):

View File

@@ -1,9 +1,11 @@
import sqlalchemy as sa
from pytest import mark
from sqlalchemy_utils import Country, CountryType
from sqlalchemy_utils import Country, CountryType, i18n # noqa
from tests import TestCase
@mark.skipif('i18n.babel is None')
class TestCountryType(TestCase):
def create_models(self):
class User(self.Base):
@@ -18,7 +20,7 @@ class TestCountryType(TestCase):
def test_parameter_processing(self):
user = self.User(
country=Country(u'fi')
country=Country(u'FI')
)
self.session.add(user)
@@ -28,6 +30,5 @@ class TestCountryType(TestCase):
assert user.country.name == u'Finland'
def test_scalar_attributes_get_coerced_to_objects(self):
user = self.User(country='fi')
user = self.User(country='FI')
assert isinstance(user.country, Country)

View File

@@ -3,15 +3,14 @@ import sqlalchemy as sa
from pytest import mark
from sqlalchemy_utils import Currency, CurrencyType, i18n
from sqlalchemy_utils.types.currency import babel
from tests import TestCase
@mark.skipif('babel is None')
@mark.skipif('i18n.babel is None')
class TestCurrencyType(TestCase):
def setup_method(self, method):
TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def create_models(self):
class User(self.Base):

View File

@@ -4,15 +4,14 @@ import sqlalchemy as sa
from sqlalchemy_utils import i18n
from sqlalchemy_utils.primitives import WeekDays
from sqlalchemy_utils.types import WeekDaysType
from sqlalchemy_utils.types.weekdays import babel
from tests import TestCase
@pytest.mark.skipif('babel is None')
@pytest.mark.skipif('i18n.babel is None')
class WeekDaysTypeTestCase(TestCase):
def setup_method(self, method):
TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')
i18n.get_locale = lambda: i18n.babel.Locale('en')
def create_models(self):
class Schedule(self.Base):