Skip to content

Commit 9e61aa9

Browse files
[3.13] gh-53203: Improve tests for strptime() (GH-125090) (GH-125091)
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) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent ea2ccb3 commit 9e61aa9

File tree

10 files changed

+200
-85
lines changed

10 files changed

+200
-85
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
@@ -2589,7 +2589,7 @@ def test_float(self):
25892589
got = self.loads(pickle)
25902590
self.assert_is_copy(value, got)
25912591

2592-
@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
2592+
@run_with_locales('LC_ALL', 'de_DE', 'fr_FR', '')
25932593
def test_float_format(self):
25942594
# make sure that floats are formatted locale independent with proto 0
25952595
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
@@ -921,8 +921,8 @@ def check_sizeof(test, o, size):
921921
test.assertEqual(result, size, msg)
922922

923923
#=======================================================================
924-
# Decorator for running a function in a different locale, correctly resetting
925-
# it afterwards.
924+
# Decorator/context manager for running a code in a different locale,
925+
# correctly resetting it afterwards.
926926

927927
@contextlib.contextmanager
928928
def run_with_locale(catstr, *locales):
@@ -933,23 +933,68 @@ def run_with_locale(catstr, *locales):
933933
except AttributeError:
934934
# if the test author gives us an invalid category string
935935
raise
936-
except:
936+
except Exception:
937937
# cannot retrieve original locale, so do nothing
938938
locale = orig_locale = None
939+
if '' not in locales:
940+
raise unittest.SkipTest('no locales')
939941
else:
940942
for loc in locales:
941943
try:
942944
locale.setlocale(category, loc)
943945
break
944-
except:
946+
except locale.Error:
945947
pass
948+
else:
949+
if '' not in locales:
950+
raise unittest.SkipTest(f'no locales {locales}')
946951

947952
try:
948953
yield
949954
finally:
950955
if locale and orig_locale:
951956
locale.setlocale(category, orig_locale)
952957

958+
#=======================================================================
959+
# Decorator for running a function in multiple locales (if they are
960+
# availasble) and resetting the original locale afterwards.
961+
962+
def run_with_locales(catstr, *locales):
963+
def deco(func):
964+
@functools.wraps(func)
965+
def wrapper(self, /, *args, **kwargs):
966+
dry_run = '' in locales
967+
try:
968+
import locale
969+
category = getattr(locale, catstr)
970+
orig_locale = locale.setlocale(category)
971+
except AttributeError:
972+
# if the test author gives us an invalid category string
973+
raise
974+
except Exception:
975+
# cannot retrieve original locale, so do nothing
976+
pass
977+
else:
978+
try:
979+
for loc in locales:
980+
with self.subTest(locale=loc):
981+
try:
982+
locale.setlocale(category, loc)
983+
except locale.Error:
984+
self.skipTest(f'no locale {loc!r}')
985+
else:
986+
dry_run = False
987+
func(self, *args, **kwargs)
988+
finally:
989+
locale.setlocale(category, orig_locale)
990+
if dry_run:
991+
# no locales available, so just run the test
992+
# with the current locale
993+
with self.subTest(locale=None):
994+
func(self, *args, **kwargs)
995+
return wrapper
996+
return deco
997+
953998
#=======================================================================
954999
# Decorator for running a function in a specific timezone, correctly
9551000
# 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
@@ -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

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

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')

Lib/test/test_str.py

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