Skip to content

Commit 19984fe

Browse files
gh-53203: Improve tests for strptime() (GH-125090)
Run them with different locales and different date and time. Add the @run_with_locales() decorator to run the test with multiple locales. Improve the run_with_locale() context manager/decorator -- it now catches only expected exceptions and reports the test as skipped if no appropriate locale is available.
1 parent 7c4b6a6 commit 19984fe

10 files changed

+200
-85
lines changed

Lib/test/pickletester.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from test import support
2727
from test.support import os_helper
2828
from test.support import (
29-
TestFailed, run_with_locale, no_tracing,
29+
TestFailed, run_with_locales, no_tracing,
3030
_2G, _4G, bigmemtest
3131
)
3232
from test.support.import_helper import forget
@@ -2895,7 +2895,7 @@ def test_float(self):
28952895
got = self.loads(pickle)
28962896
self.assert_is_copy(value, got)
28972897

2898-
@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
2898+
@run_with_locales('LC_ALL', 'de_DE', 'fr_FR', '')
28992899
def test_float_format(self):
29002900
# make sure that floats are formatted locale independent with proto 0
29012901
self.assertEqual(self.dumps(1.2, 0)[0:3], b'F1.')

Lib/test/support/__init__.py

+49-4
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,8 @@ def check_sizeof(test, o, size):
930930
test.assertEqual(result, size, msg)
931931

932932
#=======================================================================
933-
# Decorator for running a function in a different locale, correctly resetting
934-
# it afterwards.
933+
# Decorator/context manager for running a code in a different locale,
934+
# correctly resetting it afterwards.
935935

936936
@contextlib.contextmanager
937937
def run_with_locale(catstr, *locales):
@@ -942,23 +942,68 @@ def run_with_locale(catstr, *locales):
942942
except AttributeError:
943943
# if the test author gives us an invalid category string
944944
raise
945-
except:
945+
except Exception:
946946
# cannot retrieve original locale, so do nothing
947947
locale = orig_locale = None
948+
if '' not in locales:
949+
raise unittest.SkipTest('no locales')
948950
else:
949951
for loc in locales:
950952
try:
951953
locale.setlocale(category, loc)
952954
break
953-
except:
955+
except locale.Error:
954956
pass
957+
else:
958+
if '' not in locales:
959+
raise unittest.SkipTest(f'no locales {locales}')
955960

956961
try:
957962
yield
958963
finally:
959964
if locale and orig_locale:
960965
locale.setlocale(category, orig_locale)
961966

967+
#=======================================================================
968+
# Decorator for running a function in multiple locales (if they are
969+
# availasble) and resetting the original locale afterwards.
970+
971+
def run_with_locales(catstr, *locales):
972+
def deco(func):
973+
@functools.wraps(func)
974+
def wrapper(self, /, *args, **kwargs):
975+
dry_run = '' in locales
976+
try:
977+
import locale
978+
category = getattr(locale, catstr)
979+
orig_locale = locale.setlocale(category)
980+
except AttributeError:
981+
# if the test author gives us an invalid category string
982+
raise
983+
except Exception:
984+
# cannot retrieve original locale, so do nothing
985+
pass
986+
else:
987+
try:
988+
for loc in locales:
989+
with self.subTest(locale=loc):
990+
try:
991+
locale.setlocale(category, loc)
992+
except locale.Error:
993+
self.skipTest(f'no locale {loc!r}')
994+
else:
995+
dry_run = False
996+
func(self, *args, **kwargs)
997+
finally:
998+
locale.setlocale(category, orig_locale)
999+
if dry_run:
1000+
# no locales available, so just run the test
1001+
# with the current locale
1002+
with self.subTest(locale=None):
1003+
func(self, *args, **kwargs)
1004+
return wrapper
1005+
return deco
1006+
9621007
#=======================================================================
9631008
# Decorator for running a function in a specific timezone, correctly
9641009
# resetting it afterwards.

Lib/test/test_codecs.py

+1-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import contextlib
33
import copy
44
import io
5-
import locale
65
import pickle
76
import sys
87
import unittest
@@ -1812,16 +1811,10 @@ def test_getwriter(self):
18121811
self.assertRaises(TypeError, codecs.getwriter)
18131812
self.assertRaises(LookupError, codecs.getwriter, "__spam__")
18141813

1814+
@support.run_with_locale('LC_CTYPE', 'tr_TR')
18151815
def test_lookup_issue1813(self):
18161816
# Issue #1813: under Turkish locales, lookup of some codecs failed
18171817
# because 'I' is lowercased as "ı" (dotless i)
1818-
oldlocale = locale.setlocale(locale.LC_CTYPE)
1819-
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
1820-
try:
1821-
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
1822-
except locale.Error:
1823-
# Unsupported locale on this system
1824-
self.skipTest('test needs Turkish locale')
18251818
c = codecs.lookup('ASCII')
18261819
self.assertEqual(c.name, 'ascii')
18271820

Lib/test/test_decimal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ def test_deprecated_N_format(self):
12531253
self.assertRaises(ValueError, format, h, '10Nf')
12541254
self.assertRaises(ValueError, format, h, 'Nx')
12551255

1256-
@run_with_locale('LC_ALL', 'ps_AF')
1256+
@run_with_locale('LC_ALL', 'ps_AF', '')
12571257
def test_wide_char_separator_decimal_point(self):
12581258
# locale with wide char separator and decimal point
12591259
Decimal = self.decimal.Decimal

Lib/test/test_float.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def check(s):
175175
# non-UTF-8 byte string
176176
check(b'123\xa0')
177177

178-
@support.run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE')
178+
@support.run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE', '')
179179
def test_float_with_comma(self):
180180
# set locale to something that doesn't use '.' for the decimal point
181181
# float must not accept the locale specific decimal point but

Lib/test/test_imaplib.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def timevalues(self):
5757
timezone(timedelta(0, 2 * 60 * 60))),
5858
'"18-May-2033 05:33:20 +0200"']
5959

60-
@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
60+
@run_with_locale('LC_ALL', 'de_DE', 'fr_FR', '')
6161
# DST rules included to work around quirk where the Gnu C library may not
6262
# otherwise restore the previous time zone
6363
@run_with_tz('STD-1DST,M3.2.0,M11.1.0')

Lib/test/test_str.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1665,7 +1665,7 @@ def test_startswith_endswith_errors(self):
16651665
self.assertIn('str', exc)
16661666
self.assertIn('tuple', exc)
16671667

1668-
@support.run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
1668+
@support.run_with_locale('LC_ALL', 'de_DE', 'fr_FR', '')
16691669
def test_format_float(self):
16701670
# should not format with a comma, but always with C locale
16711671
self.assertEqual('1.0', '%.1f' % 1.0)

0 commit comments

Comments
 (0)