From 5980c48e265d65aebf7aa56f6339185d59aa7056 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Sun, 16 Oct 2016 12:25:39 +0100 Subject: [PATCH 1/2] Add a temporary_locale context manager. Addresses #1347. --- pelican/tests/test_utils.py | 115 +++++++++++++++++++++--------------- pelican/utils.py | 32 ++++++---- 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 8dfc0b9b..0621dc0c 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -370,33 +370,30 @@ class TestUtils(LoggedTestCase): locale_available('Turkish'), 'Turkish locale needed') def test_strftime_locale_dependent_turkish(self): - # store current locale - old_locale = locale.setlocale(locale.LC_ALL) - if platform == 'win32': - locale.setlocale(locale.LC_ALL, str('Turkish')) + new_locale = 'Turkish' else: - locale.setlocale(locale.LC_ALL, str('tr_TR.UTF-8')) + new_locale = 'tr_TR.UTF-8' - d = utils.SafeDatetime(2012, 8, 29) + with utils.temporary_locale(new_locale): - # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') - self.assertEqual(utils.strftime(d, '%A, %d %B %Y'), - 'Çarşamba, 29 Ağustos 2012') + d = utils.SafeDatetime(2012, 8, 29) - # with text - self.assertEqual( - utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), - 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') + # simple + self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') + self.assertEqual(utils.strftime(d, '%A, %d %B %Y'), + 'Çarşamba, 29 Ağustos 2012') - # non-ascii format candidate (someone might pass it… for some reason) - self.assertEqual( - utils.strftime(d, '%Y yılında %üretim artışı'), - '2012 yılında %üretim artışı') + # with text + self.assertEqual( + utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), + 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') - # restore locale back - locale.setlocale(locale.LC_ALL, old_locale) + # non-ascii format candidate (someone might pass it… for + # some reason) + self.assertEqual( + utils.strftime(d, '%Y yılında %üretim artışı'), + '2012 yılında %üretim artışı') # test the output of utils.strftime in a different locale # French locale @@ -404,34 +401,31 @@ class TestUtils(LoggedTestCase): locale_available('French'), 'French locale needed') def test_strftime_locale_dependent_french(self): - # store current locale - old_locale = locale.setlocale(locale.LC_ALL) - if platform == 'win32': - locale.setlocale(locale.LC_ALL, str('French')) + new_locale = 'French' else: - locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8')) + new_locale = 'fr_FR.UTF-8' - d = utils.SafeDatetime(2012, 8, 29) + with utils.temporary_locale(new_locale): - # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') + d = utils.SafeDatetime(2012, 8, 29) - # depending on OS, the first letter is m or M - self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) + # simple + self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') - # with text - self.assertEqual( - utils.strftime(d, 'Écrit le %d %B %Y'), - 'Écrit le 29 août 2012') + # depending on OS, the first letter is m or M + self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) - # non-ascii format candidate (someone might pass it… for some reason) - self.assertEqual( - utils.strftime(d, '%écrits en %Y'), - '%écrits en 2012') + # with text + self.assertEqual( + utils.strftime(d, 'Écrit le %d %B %Y'), + 'Écrit le 29 août 2012') - # restore locale back - locale.setlocale(locale.LC_ALL, old_locale) + # non-ascii format candidate (someone might pass it… for + # some reason) + self.assertEqual( + utils.strftime(d, '%écrits en %Y'), + '%écrits en 2012') def test_maybe_pluralize(self): self.assertEqual( @@ -573,13 +567,13 @@ class TestDateFormatter(unittest.TestCase): self.assertEqual( u'jeudi, 14 août 2014', df(date, date_format="%A, %d %B %Y").lower()) - # Let us now set the global locale to C: - locale.setlocale(locale.LC_ALL, str('C')) - # DateFormatter should still work as expected - # since it is the whole point of DateFormatter - # (This is where pre-2014/4/15 code fails on macos10) - df_date = df(date, date_format="%A, %d %B %Y").lower() - self.assertEqual(u'jeudi, 14 août 2014', df_date) + + with utils.temporary_locale(str('C')): + # DateFormatter should still work as expected + # since it is the whole point of DateFormatter + # (This is where pre-2014/4/15 code fails on macos10) + df_date = df(date, date_format="%A, %d %B %Y").lower() + self.assertEqual(u'jeudi, 14 août 2014', df_date) @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), @@ -650,3 +644,30 @@ class TestDateFormatter(unittest.TestCase): with utils.pelican_open(output_path) as output_file: self.assertEqual(output_file, utils.strftime(self.date, 'date = %A, %d %B %Y')) + + @unittest.skipUnless( + (locale_available('tr_TR.UTF-8') or locale_available('Turkish')) and + (locale_available('fr_FR.UTF-8') or locale_available('French')), + 'French and Turkish locales needed' + ) + def test_temporary_locale(self): + ''' + Test the temporary_locale context manager. Use it to temporarily + set the locale to Turkish (or French, if we are already using the + Turkish locale). + ''' + if platform == 'win32': + new_locale = 'Turkish' + alt_locale = 'French' + else: + new_locale = 'tr_TR.UTF-8' + alt_locale = 'fr_FR.UTF-8' + + # If our locale is already Turkish, we will use French instead + if locale.setlocale(locale.LC_ALL) == new_locale: + new_locale = alt_locale + + self.assertNotEqual(locale.setlocale(locale.LC_ALL), new_locale) + with utils.temporary_locale(new_locale): + self.assertEqual(locale.setlocale(locale.LC_ALL), new_locale) + self.assertNotEqual(locale.setlocale(locale.LC_ALL), new_locale) diff --git a/pelican/utils.py b/pelican/utils.py index f1558fda..b6d466de 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -120,19 +120,8 @@ class DateFormatter(object): self.locale = locale.setlocale(locale.LC_TIME) def __call__(self, date, date_format): - old_lc_time = locale.setlocale(locale.LC_TIME) - old_lc_ctype = locale.setlocale(locale.LC_CTYPE) - - locale.setlocale(locale.LC_TIME, self.locale) - # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 - # make sure it's same as LC_TIME - locale.setlocale(locale.LC_CTYPE, self.locale) - - formatted = strftime(date, date_format) - - locale.setlocale(locale.LC_TIME, old_lc_time) - locale.setlocale(locale.LC_CTYPE, old_lc_ctype) - return formatted + with temporary_locale(self.locale): + return strftime(date, date_format) def python_2_unicode_compatible(klass): @@ -824,3 +813,20 @@ def maybe_pluralize(count, singular, plural): if count == 1: selection = singular return '{} {}'.format(count, selection) + + +@contextmanager +def temporary_locale(temp_locale=None): + ''' + Enable code to run in a context with a temporary locale. + + Resets the locale back when context exits. Can set a temporary + locale if provided. + ''' + orig_locale = locale.setlocale(locale.LC_ALL) + + if temp_locale is not None: + locale.setlocale(locale.LC_ALL, temp_locale) + + yield + locale.setlocale(locale.LC_ALL, orig_locale) From b084a22dc88963ec8ead2ec0148675bbf9391909 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Sun, 16 Oct 2016 12:28:54 +0100 Subject: [PATCH 2/2] Improve assert helper in test_strftime test --- pelican/tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 0621dc0c..deb7785e 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -414,7 +414,7 @@ class TestUtils(LoggedTestCase): self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') # depending on OS, the first letter is m or M - self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) + self.assertIn(utils.strftime(d, '%A'), ('mercredi', 'Mercredi')) # with text self.assertEqual(