From 3d90045d2d1ce1df89f75937f415e3982e111063 Mon Sep 17 00:00:00 2001 From: James Carey Date: Thu, 3 Jul 2014 20:40:44 +0000 Subject: Backport code for i18n to check lazy at runtime Currently in the oslo-incubator version of i18n support the TranslationFactory generates a direct translation method based on the passed lazy parameter. This means that when the default is assigned to _ during its intial import, lazy isn't specified, which defaults to False, so the TranslationFactory returns a method that does not support lazy translation. The _ method is not changed until enable_lazy is called, thus any import of _ prior to that will cache the initial method that does not support lazy translation even when it has been enabled. This problem was exposed by the cinder test case: cinder/tests/test_wsgi.py test test_cinder_exception_with_localized_explanation when the file: cinder/api/middleware/fault.py had the line: from cinder.openstack.common.gettextutils import _ added to it. This behavior is corrected in the i18n library such that the generated method checks the global _lazy.USE_LAZY to determine if lazy translation should be used in commit: Iae22668 Check the lazy flag at runtime Note that this removes the lazy parameter from the signature of install and the TranslationFactory to match the library. Change-Id: If1832b012d4b72708d0850367a1ec55cb0cd63e8 --- openstack/common/gettextutils.py | 63 ++++++++++++++-------------------------- tests/unit/test_gettext.py | 47 +++++++++++++++--------------- 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/openstack/common/gettextutils.py b/openstack/common/gettextutils.py index 87229a93..59545c49 100644 --- a/openstack/common/gettextutils.py +++ b/openstack/common/gettextutils.py @@ -23,7 +23,6 @@ Usual usage in an openstack.common module: """ import copy -import functools import gettext import locale from logging import handlers @@ -42,7 +41,7 @@ class TranslatorFactory(object): """Create translator functions """ - def __init__(self, domain, lazy=False, localedir=None): + def __init__(self, domain, localedir=None): """Establish a set of translation functions for the domain. :param domain: Name of translation domain, @@ -55,7 +54,6 @@ class TranslatorFactory(object): :type localedir: str """ self.domain = domain - self.lazy = lazy if localedir is None: localedir = os.environ.get(domain.upper() + '_LOCALEDIR') self.localedir = localedir @@ -75,16 +73,19 @@ class TranslatorFactory(object): """ if domain is None: domain = self.domain - if self.lazy: - return functools.partial(Message, domain=domain) - t = gettext.translation( - domain, - localedir=self.localedir, - fallback=True, - ) - if six.PY3: - return t.gettext - return t.ugettext + t = gettext.translation(domain, + localedir=self.localedir, + fallback=True) + # Use the appropriate method of the translation object based + # on the python version. + m = t.gettext if six.PY3 else t.ugettext + + def f(msg): + """oslo.i18n.gettextutils translation function.""" + if USE_LAZY: + return Message(msg, domain=domain) + return m(msg) + return f @property def primary(self): @@ -147,19 +148,11 @@ def enable_lazy(): your project is importing _ directly instead of using the gettextutils.install() way of importing the _ function. """ - # FIXME(dhellmann): This function will be removed in oslo.i18n, - # because the TranslatorFactory makes it superfluous. - global _, _LI, _LW, _LE, _LC, USE_LAZY - tf = TranslatorFactory('oslo', lazy=True) - _ = tf.primary - _LI = tf.log_info - _LW = tf.log_warning - _LE = tf.log_error - _LC = tf.log_critical + global USE_LAZY USE_LAZY = True -def install(domain, lazy=False): +def install(domain): """Install a _() function using the given translation domain. Given a translation domain, install a _() function using gettext's @@ -170,26 +163,14 @@ def install(domain, lazy=False): a translation-domain-specific environment variable (e.g. NOVA_LOCALEDIR). + Note that to enable lazy translation, enable_lazy must be + called. + :param domain: the translation domain - :param lazy: indicates whether or not to install the lazy _() function. - The lazy _() introduces a way to do deferred translation - of messages by installing a _ that builds Message objects, - instead of strings, which can then be lazily translated into - any available locale. """ - if lazy: - from six import moves - tf = TranslatorFactory(domain, lazy=True) - moves.builtins.__dict__['_'] = tf.primary - else: - localedir = '%s_LOCALEDIR' % domain.upper() - if six.PY3: - gettext.install(domain, - localedir=os.environ.get(localedir)) - else: - gettext.install(domain, - localedir=os.environ.get(localedir), - unicode=True) + from six import moves + tf = TranslatorFactory(domain) + moves.builtins.__dict__['_'] = tf.primary class Message(six.text_type): diff --git a/tests/unit/test_gettext.py b/tests/unit/test_gettext.py index 47490534..1d9d6ad2 100644 --- a/tests/unit/test_gettext.py +++ b/tests/unit/test_gettext.py @@ -39,6 +39,7 @@ class GettextTest(test_base.BaseTestCase): self.mox = moxfixture.mox # remember so we can reset to it later self._USE_LAZY = gettextutils.USE_LAZY + gettextutils.USE_LAZY = False def tearDown(self): # reset to value before test @@ -59,28 +60,14 @@ class GettextTest(test_base.BaseTestCase): gettextutils.install('blaa') self.assertTrue(isinstance(_('A String'), six.text_type)) # noqa - gettextutils.install('blaa', lazy=True) + def test_gettextutils_install_with_enable(self): + gettextutils.install('blaa') + self.assertTrue(isinstance(_('A String'), six.text_type)) # noqa + + gettextutils.enable_lazy() self.assertTrue(isinstance(_('A Message'), # noqa gettextutils.Message)) - def test_gettext_install_looks_up_localedir(self): - with mock.patch('os.environ.get') as environ_get: - with mock.patch('gettext.install') as gettext_install: - environ_get.return_value = '/foo/bar' - - gettextutils.install('blaa') - - environ_get.assert_called_once_with('BLAA_LOCALEDIR') - if six.PY3: - gettext_install.assert_called_once_with( - 'blaa', - localedir='/foo/bar') - else: - gettext_install.assert_called_once_with( - 'blaa', - localedir='/foo/bar', - unicode=True) - def test_get_available_languages(self): # All the available languages for which locale data is available def _mock_locale_identifiers(): @@ -718,9 +705,21 @@ class TranslationHandlerTestCase(test_base.BaseTestCase): class TranslatorFactoryTest(test_base.BaseTestCase): + def setUp(self): + super(TranslatorFactoryTest, self).setUp() + # remember so we can reset to it later + self._USE_LAZY = gettextutils.USE_LAZY + gettextutils.USE_LAZY = False + + def tearDown(self): + # reset to value before test + gettextutils.USE_LAZY = self._USE_LAZY + super(TranslatorFactoryTest, self).tearDown() + def test_lazy(self): with mock.patch.object(gettextutils, 'Message') as msg: - tf = gettextutils.TranslatorFactory('domain', lazy=True) + tf = gettextutils.TranslatorFactory('domain') + gettextutils.enable_lazy() tf.primary('some text') msg.assert_called_with('some text', domain='domain') @@ -731,7 +730,7 @@ class TranslatorFactoryTest(test_base.BaseTestCase): translation.return_value = trans trans.gettext.side_effect = AssertionError( 'should have called ugettext') - tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf = gettextutils.TranslatorFactory('domain') tf.primary('some text') trans.ugettext.assert_called_with('some text') @@ -742,14 +741,14 @@ class TranslatorFactoryTest(test_base.BaseTestCase): translation.return_value = trans trans.ugettext.side_effect = AssertionError( 'should have called gettext') - tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf = gettextutils.TranslatorFactory('domain') tf.primary('some text') trans.gettext.assert_called_with('some text') def test_log_level_domain_name(self): with mock.patch.object(gettextutils.TranslatorFactory, '_make_translation_func') as mtf: - tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf = gettextutils.TranslatorFactory('domain') tf._make_log_translation_func('mylevel') mtf.assert_called_with('domain-log-mylevel') @@ -765,7 +764,7 @@ class LogLevelTranslationsTest(test_base.BaseTestCase): def test(self): with mock.patch.object(gettextutils.TranslatorFactory, '_make_translation_func') as mtf: - tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf = gettextutils.TranslatorFactory('domain') getattr(tf, 'log_%s' % self.level) mtf.assert_called_with('domain-log-%s' % self.level) -- cgit v1.2.1