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:
@@ -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
|
- 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)
|
0.30.9 (2015-06-09)
|
||||||
|
|||||||
@@ -92,4 +92,4 @@ from .types import ( # noqa
|
|||||||
WeekDaysType
|
WeekDaysType
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = '0.30.9'
|
__version__ = '0.30.10'
|
||||||
|
|||||||
@@ -3,18 +3,10 @@ from sqlalchemy.ext.hybrid import hybrid_property
|
|||||||
|
|
||||||
from .exceptions import ImproperlyConfigured
|
from .exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from babel import Locale
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from babel.dates import get_day_names
|
import babel
|
||||||
except ImportError:
|
except ImportError:
|
||||||
def get_day_names():
|
babel = None
|
||||||
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.'
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from flask.ext.babel import get_locale
|
from flask.ext.babel import get_locale
|
||||||
@@ -29,6 +21,10 @@ except ImportError:
|
|||||||
|
|
||||||
class TranslationHybrid(object):
|
class TranslationHybrid(object):
|
||||||
def __init__(self, current_locale, default_locale, default_value=None):
|
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.current_locale = current_locale
|
||||||
self.default_locale = default_locale
|
self.default_locale = default_locale
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
@@ -43,8 +39,9 @@ class TranslationHybrid(object):
|
|||||||
locale = locale()
|
locale = locale()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
locale = locale(obj)
|
locale = locale(obj)
|
||||||
if isinstance(locale, Locale):
|
if isinstance(locale, babel.Locale):
|
||||||
return str(locale)
|
return str(locale)
|
||||||
|
|
||||||
return locale
|
return locale
|
||||||
|
|
||||||
def getter_factory(self, attr):
|
def getter_factory(self, attr):
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class Country(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def validate(self, code):
|
def validate(self, code):
|
||||||
try:
|
try:
|
||||||
i18n.get_locale().territories[code]
|
i18n.babel.Locale('en').territories[code]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Could not convert string to country code: {0}'.format(code)
|
'Could not convert string to country code: {0}'.format(code)
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
babel = None
|
|
||||||
try:
|
|
||||||
import babel
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from sqlalchemy_utils import i18n, ImproperlyConfigured
|
from sqlalchemy_utils import i18n, ImproperlyConfigured
|
||||||
@@ -58,7 +53,7 @@ class Currency(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, code):
|
def __init__(self, code):
|
||||||
if babel is None:
|
if i18n.babel is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"'babel' package is required in order to use Currency class."
|
"'babel' package is required in order to use Currency class."
|
||||||
)
|
)
|
||||||
@@ -77,13 +72,16 @@ class Currency(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def validate(self, code):
|
def validate(self, code):
|
||||||
try:
|
try:
|
||||||
i18n.get_locale().currencies[code]
|
i18n.babel.Locale('en').currencies[code]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError("{0}' is not valid currency code.")
|
raise ValueError("{0}' is not valid currency code.")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def symbol(self):
|
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
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class WeekDay(object):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_name(self, width='wide', context='format'):
|
def get_name(self, width='wide', context='format'):
|
||||||
names = i18n.get_day_names(
|
names = i18n.babel.dates.get_day_names(
|
||||||
width,
|
width,
|
||||||
context,
|
context,
|
||||||
i18n.get_locale()
|
i18n.get_locale()
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
babel = None
|
|
||||||
try:
|
|
||||||
import babel
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
import six
|
import six
|
||||||
from sqlalchemy import types
|
from sqlalchemy import types
|
||||||
|
|
||||||
from sqlalchemy_utils import ImproperlyConfigured
|
from sqlalchemy_utils import i18n, ImproperlyConfigured
|
||||||
from sqlalchemy_utils.primitives import Currency
|
from sqlalchemy_utils.primitives import Currency
|
||||||
|
|
||||||
from .scalar_coercible import ScalarCoercible
|
from .scalar_coercible import ScalarCoercible
|
||||||
@@ -57,7 +52,7 @@ class CurrencyType(types.TypeDecorator, ScalarCoercible):
|
|||||||
python_type = Currency
|
python_type = Currency
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if babel is None:
|
if i18n.babel is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"'babel' package is required in order to use CurrencyType."
|
"'babel' package is required in order to use CurrencyType."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
babel = None
|
|
||||||
try:
|
|
||||||
import babel
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
import six
|
import six
|
||||||
from sqlalchemy import types
|
from sqlalchemy import types
|
||||||
|
|
||||||
|
from sqlalchemy_utils import i18n
|
||||||
from sqlalchemy_utils.exceptions import ImproperlyConfigured
|
from sqlalchemy_utils.exceptions import ImproperlyConfigured
|
||||||
from sqlalchemy_utils.primitives import WeekDay, WeekDays
|
from sqlalchemy_utils.primitives import WeekDay, WeekDays
|
||||||
|
|
||||||
@@ -57,7 +53,7 @@ class WeekDaysType(types.TypeDecorator, ScalarCoercible):
|
|||||||
impl = BitType(WeekDay.NUM_WEEK_DAYS)
|
impl = BitType(WeekDay.NUM_WEEK_DAYS)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if babel is None:
|
if i18n.babel is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"'babel' package is required to use 'WeekDaysType'"
|
"'babel' package is required to use 'WeekDaysType'"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener)
|
|||||||
|
|
||||||
def get_locale():
|
def get_locale():
|
||||||
class Locale():
|
class Locale():
|
||||||
territories = {'fi': 'Finland'}
|
territories = {'FI': 'Finland'}
|
||||||
|
|
||||||
return Locale()
|
return Locale()
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import six
|
|||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
from sqlalchemy_utils import Country, i18n
|
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):
|
class TestCountry(object):
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
i18n.get_locale = lambda: babel.Locale('en')
|
i18n.get_locale = lambda: i18n.babel.Locale('en')
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
assert Country(u'FI') == Country(Country(u'FI'))
|
assert Country(u'FI') == Country(Country(u'FI'))
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ import six
|
|||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
from sqlalchemy_utils import Currency, i18n
|
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):
|
class TestCurrency(object):
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
i18n.get_locale = lambda: babel.Locale('en')
|
i18n.get_locale = lambda: i18n.babel.Locale('en')
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
assert Currency('USD') == Currency(Currency('USD'))
|
assert Currency('USD') == Currency(Currency('USD'))
|
||||||
|
|||||||
@@ -5,17 +5,11 @@ from flexmock import flexmock
|
|||||||
from sqlalchemy_utils import i18n
|
from sqlalchemy_utils import i18n
|
||||||
from sqlalchemy_utils.primitives import WeekDay, WeekDays
|
from sqlalchemy_utils.primitives import WeekDay, WeekDays
|
||||||
|
|
||||||
Locale = None
|
|
||||||
try:
|
|
||||||
from babel import Locale
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif('i18n.babel is None')
|
||||||
@pytest.mark.skipif('Locale is None')
|
|
||||||
class TestWeekDay(object):
|
class TestWeekDay(object):
|
||||||
def setup_method(self, method):
|
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):
|
def test_constructor_with_valid_index(self):
|
||||||
day = WeekDay(1)
|
day = WeekDay(1)
|
||||||
@@ -88,7 +82,7 @@ class TestWeekDay(object):
|
|||||||
assert str(day) == 'maanantaina'
|
assert str(day) == 'maanantaina'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif('Locale is None')
|
@pytest.mark.skipif('i18n.babel is None')
|
||||||
class TestWeekDays(object):
|
class TestWeekDays(object):
|
||||||
def test_constructor_with_valid_bit_string(self):
|
def test_constructor_with_valid_bit_string(self):
|
||||||
days = WeekDays('1000100')
|
days = WeekDays('1000100')
|
||||||
@@ -161,11 +155,11 @@ class TestWeekDays(object):
|
|||||||
assert indices == [1, 2, 3, 4, 5, 6, 0]
|
assert indices == [1, 2, 3, 4, 5, 6, 0]
|
||||||
|
|
||||||
def test_unicode(self):
|
def test_unicode(self):
|
||||||
i18n.get_locale = lambda: Locale('fi')
|
i18n.get_locale = lambda: i18n.babel.Locale('fi')
|
||||||
days = WeekDays('1000100')
|
days = WeekDays('1000100')
|
||||||
assert six.text_type(days) == u'maanantaina, perjantaina'
|
assert six.text_type(days) == u'maanantaina, perjantaina'
|
||||||
|
|
||||||
def test_str(self):
|
def test_str(self):
|
||||||
i18n.get_locale = lambda: Locale('fi')
|
i18n.get_locale = lambda: i18n.babel.Locale('fi')
|
||||||
days = WeekDays('1000100')
|
days = WeekDays('1000100')
|
||||||
assert str(days) == 'maanantaina, perjantaina'
|
assert str(days) == 'maanantaina, perjantaina'
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import sqlalchemy as sa
|
|||||||
from pytest import mark
|
from pytest import mark
|
||||||
from sqlalchemy.dialects.postgresql import HSTORE
|
from sqlalchemy.dialects.postgresql import HSTORE
|
||||||
|
|
||||||
from sqlalchemy_utils import TranslationHybrid
|
from sqlalchemy_utils import i18n, TranslationHybrid # noqa
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skipif('i18n.babel is None')
|
||||||
class TestTranslationHybrid(TestCase):
|
class TestTranslationHybrid(TestCase):
|
||||||
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ from sqlalchemy_utils import (
|
|||||||
remove_composite_listeners
|
remove_composite_listeners
|
||||||
)
|
)
|
||||||
from sqlalchemy_utils.types import pg_composite
|
from sqlalchemy_utils.types import pg_composite
|
||||||
from sqlalchemy_utils.types.currency import babel
|
|
||||||
from sqlalchemy_utils.types.range import intervals
|
from sqlalchemy_utils.types.range import intervals
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
|
|
||||||
@@ -52,13 +51,13 @@ class TestCompositeTypeWithRegularTypes(TestCase):
|
|||||||
assert account.balance.amount == 15
|
assert account.balance.amount == 15
|
||||||
|
|
||||||
|
|
||||||
@mark.skipif('babel is None')
|
@mark.skipif('i18n.babel is None')
|
||||||
class TestCompositeTypeWithTypeDecorators(TestCase):
|
class TestCompositeTypeWithTypeDecorators(TestCase):
|
||||||
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
||||||
|
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
TestCase.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):
|
def create_models(self):
|
||||||
class Account(self.Base):
|
class Account(self.Base):
|
||||||
@@ -101,7 +100,7 @@ class TestCompositeTypeWithTypeDecorators(TestCase):
|
|||||||
assert account.balance.amount == 15
|
assert account.balance.amount == 15
|
||||||
|
|
||||||
|
|
||||||
@mark.skipif('babel is None')
|
@mark.skipif('i18n.babel is None')
|
||||||
class TestCompositeTypeInsideArray(TestCase):
|
class TestCompositeTypeInsideArray(TestCase):
|
||||||
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
||||||
|
|
||||||
@@ -115,7 +114,7 @@ class TestCompositeTypeInsideArray(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
TestCase.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):
|
def create_models(self):
|
||||||
class Account(self.Base):
|
class Account(self.Base):
|
||||||
@@ -159,7 +158,6 @@ class TestCompositeTypeWithRangeTypeInsideArray(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
TestCase.setup_method(self, method)
|
TestCase.setup_method(self, method)
|
||||||
i18n.get_locale = lambda: babel.Locale('en')
|
|
||||||
|
|
||||||
def create_models(self):
|
def create_models(self):
|
||||||
class Account(self.Base):
|
class Account(self.Base):
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import sqlalchemy as sa
|
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
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skipif('i18n.babel is None')
|
||||||
class TestCountryType(TestCase):
|
class TestCountryType(TestCase):
|
||||||
def create_models(self):
|
def create_models(self):
|
||||||
class User(self.Base):
|
class User(self.Base):
|
||||||
@@ -18,7 +20,7 @@ class TestCountryType(TestCase):
|
|||||||
|
|
||||||
def test_parameter_processing(self):
|
def test_parameter_processing(self):
|
||||||
user = self.User(
|
user = self.User(
|
||||||
country=Country(u'fi')
|
country=Country(u'FI')
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(user)
|
self.session.add(user)
|
||||||
@@ -28,6 +30,5 @@ class TestCountryType(TestCase):
|
|||||||
assert user.country.name == u'Finland'
|
assert user.country.name == u'Finland'
|
||||||
|
|
||||||
def test_scalar_attributes_get_coerced_to_objects(self):
|
def test_scalar_attributes_get_coerced_to_objects(self):
|
||||||
user = self.User(country='fi')
|
user = self.User(country='FI')
|
||||||
|
|
||||||
assert isinstance(user.country, Country)
|
assert isinstance(user.country, Country)
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ import sqlalchemy as sa
|
|||||||
from pytest import mark
|
from pytest import mark
|
||||||
|
|
||||||
from sqlalchemy_utils import Currency, CurrencyType, i18n
|
from sqlalchemy_utils import Currency, CurrencyType, i18n
|
||||||
from sqlalchemy_utils.types.currency import babel
|
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
@mark.skipif('babel is None')
|
@mark.skipif('i18n.babel is None')
|
||||||
class TestCurrencyType(TestCase):
|
class TestCurrencyType(TestCase):
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
TestCase.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):
|
def create_models(self):
|
||||||
class User(self.Base):
|
class User(self.Base):
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ import sqlalchemy as sa
|
|||||||
from sqlalchemy_utils import i18n
|
from sqlalchemy_utils import i18n
|
||||||
from sqlalchemy_utils.primitives import WeekDays
|
from sqlalchemy_utils.primitives import WeekDays
|
||||||
from sqlalchemy_utils.types import WeekDaysType
|
from sqlalchemy_utils.types import WeekDaysType
|
||||||
from sqlalchemy_utils.types.weekdays import babel
|
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif('babel is None')
|
@pytest.mark.skipif('i18n.babel is None')
|
||||||
class WeekDaysTypeTestCase(TestCase):
|
class WeekDaysTypeTestCase(TestCase):
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
TestCase.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):
|
def create_models(self):
|
||||||
class Schedule(self.Base):
|
class Schedule(self.Base):
|
||||||
|
|||||||
Reference in New Issue
Block a user