Skip to content

Commit 8225737

Browse files
[3.12] gh-53203: Improve tests for strptime() (GH-125090) (GH-125093)
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. (cherry picked from commit 19984fe)
1 parent bc237ed commit 8225737

File tree

10 files changed

+201
-86
lines changed

10 files changed

+201
-86
lines changed

Lib/test/pickletester.py

Lines changed: 2 additions & 2 deletions
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
@@ -2591,7 +2591,7 @@ def test_float(self):
25912591
got = self.loads(pickle)
25922592
self.assert_is_copy(value, got)
25932593

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

Lib/test/support/__init__.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -854,8 +854,8 @@ def check_sizeof(test, o, size):
854854
test.assertEqual(result, size, msg)
855855

856856
#=======================================================================
857-
# Decorator for running a function in a different locale, correctly resetting
858-
# it afterwards.
857+
# Decorator/context manager for running a code in a different locale,
858+
# correctly resetting it afterwards.
859859

860860
@contextlib.contextmanager
861861
def run_with_locale(catstr, *locales):
@@ -866,23 +866,68 @@ def run_with_locale(catstr, *locales):
866866
except AttributeError:
867867
# if the test author gives us an invalid category string
868868
raise
869-
except:
869+
except Exception:
870870
# cannot retrieve original locale, so do nothing
871871
locale = orig_locale = None
872+
if '' not in locales:
873+
raise unittest.SkipTest('no locales')
872874
else:
873875
for loc in locales:
874876
try:
875877
locale.setlocale(category, loc)
876878
break
877-
except:
879+
except locale.Error:
878880
pass
881+
else:
882+
if '' not in locales:
883+
raise unittest.SkipTest(f'no locales {locales}')
879884

880885
try:
881886
yield
882887
finally:
883888
if locale and orig_locale:
884889
locale.setlocale(category, orig_locale)
885890

891+
#=======================================================================
892+
# Decorator for running a function in multiple locales (if they are
893+
# availasble) and resetting the original locale afterwards.
894+
895+
def run_with_locales(catstr, *locales):
896+
def deco(func):
897+
@functools.wraps(func)
898+
def wrapper(self, /, *args, **kwargs):
899+
dry_run = '' in locales
900+
try:
901+
import locale
902+
category = getattr(locale, catstr)
903+
orig_locale = locale.setlocale(category)
904+
except AttributeError:
905+
# if the test author gives us an invalid category string
906+
raise
907+
except Exception:
908+
# cannot retrieve original locale, so do nothing
909+
pass
910+
else:
911+
try:
912+
for loc in locales:
913+
with self.subTest(locale=loc):
914+
try:
915+
locale.setlocale(category, loc)
916+
except locale.Error:
917+
self.skipTest(f'no locale {loc!r}')
918+
else:
919+
dry_run = False
920+
func(self, *args, **kwargs)
921+
finally:
922+
locale.setlocale(category, orig_locale)
923+
if dry_run:
924+
# no locales available, so just run the test
925+
# with the current locale
926+
with self.subTest(locale=None):
927+
func(self, *args, **kwargs)
928+
return wrapper
929+
return deco
930+
886931
#=======================================================================
887932
# Decorator for running a function in a specific timezone, correctly
888933
# resetting it afterwards.

Lib/test/test_codecs.py

Lines changed: 1 addition & 8 deletions
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
@@ -1700,16 +1699,10 @@ def test_getwriter(self):
17001699
self.assertRaises(TypeError, codecs.getwriter)
17011700
self.assertRaises(LookupError, codecs.getwriter, "__spam__")
17021701

1702+
@support.run_with_locale('LC_CTYPE', 'tr_TR')
17031703
def test_lookup_issue1813(self):
17041704
# Issue #1813: under Turkish locales, lookup of some codecs failed
17051705
# because 'I' is lowercased as "ı" (dotless i)
1706-
oldlocale = locale.setlocale(locale.LC_CTYPE)
1707-
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
1708-
try:
1709-
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
1710-
except locale.Error:
1711-
# Unsupported locale on this system
1712-
self.skipTest('test needs Turkish locale')
17131706
c = codecs.lookup('ASCII')
17141707
self.assertEqual(c.name, 'ascii')
17151708

Lib/test/test_decimal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1242,7 +1242,7 @@ def get_fmt(x, override=None, fmt='n'):
12421242
self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'),
12431243
'-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5')
12441244

1245-
@run_with_locale('LC_ALL', 'ps_AF')
1245+
@run_with_locale('LC_ALL', 'ps_AF', '')
12461246
def test_wide_char_separator_decimal_point(self):
12471247
# locale with wide char separator and decimal point
12481248
Decimal = self.decimal.Decimal

Lib/test/test_float.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def check(s):
153153
# non-UTF-8 byte string
154154
check(b'123\xa0')
155155

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

Lib/test/test_imaplib.py

Lines changed: 1 addition & 1 deletion
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')

0 commit comments

Comments
 (0)