Skip to content

Commit

Permalink
Fix babel primitive types
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
kvesteri committed Jun 17, 2015
1 parent a697365 commit 204aba3
Show file tree
Hide file tree
Showing 17 changed files with 47 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@
WeekDaysType
)

__version__ = '0.30.9'
__version__ = '0.30.10'
19 changes: 8 additions & 11 deletions sqlalchemy_utils/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@

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
Expand All @@ -29,6 +21,10 @@ def get_locale():

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
Expand All @@ -43,8 +39,9 @@ def cast_locale(self, obj, locale):
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):
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_utils/primitives/country.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def name(self):
@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)
Expand Down
14 changes: 6 additions & 8 deletions sqlalchemy_utils/primitives/currency.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
# -*- coding: utf-8 -*-
babel = None
try:
import babel
except ImportError:
pass
import six

from sqlalchemy_utils import i18n, ImproperlyConfigured
Expand Down Expand Up @@ -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."
)
Expand All @@ -77,13 +72,16 @@ def __init__(self, code):
@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):
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_utils/primitives/weekday.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __unicode__(self):
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()
Expand Down
9 changes: 2 additions & 7 deletions sqlalchemy_utils/types/currency.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -57,7 +52,7 @@ class User(Base):
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."
)
Expand Down
8 changes: 2 additions & 6 deletions sqlalchemy_utils/types/weekdays.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -57,7 +53,7 @@ class Schedule(Base):
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'"
)
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def count_sql_calls(conn, cursor, statement, parameters, context, executemany):

def get_locale():
class Locale():
territories = {'fi': 'Finland'}
territories = {'FI': 'Finland'}

return Locale()

Expand Down
5 changes: 2 additions & 3 deletions tests/primitives/test_country.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
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'))
Expand Down
5 changes: 2 additions & 3 deletions tests/primitives/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
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'))
Expand Down
16 changes: 5 additions & 11 deletions tests/primitives/test_weekdays.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@
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)
Expand Down Expand Up @@ -88,7 +82,7 @@ def test_str(self):
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')
Expand Down Expand Up @@ -161,11 +155,11 @@ def test_iterator_starts_from_locales_first_week_day(self):
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'
3 changes: 2 additions & 1 deletion tests/test_translation_hybrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
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'

Expand Down
10 changes: 4 additions & 6 deletions tests/types/test_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
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

Expand Down Expand Up @@ -52,13 +51,13 @@ def test_parameter_processing(self):
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):
Expand Down Expand Up @@ -101,7 +100,7 @@ def test_parameter_processing(self):
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'

Expand All @@ -115,7 +114,7 @@ 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):
Expand Down Expand Up @@ -159,7 +158,6 @@ def setup_method(self, method):
)

TestCase.setup_method(self, method)
i18n.get_locale = lambda: babel.Locale('en')

def create_models(self):
class Account(self.Base):
Expand Down
9 changes: 5 additions & 4 deletions tests/types/test_country.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -18,7 +20,7 @@ def __repr__(self):

def test_parameter_processing(self):
user = self.User(
country=Country(u'fi')
country=Country(u'FI')
)

self.session.add(user)
Expand All @@ -28,6 +30,5 @@ def test_parameter_processing(self):
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)
5 changes: 2 additions & 3 deletions tests/types/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
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):
Expand Down
5 changes: 2 additions & 3 deletions tests/types/test_weekdays.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
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):
Expand Down

0 comments on commit 204aba3

Please sign in to comment.