Skip to content

Commit 943d129

Browse files
committed
Add new tests
1 parent e4d8bc9 commit 943d129

4 files changed

Lines changed: 178 additions & 29 deletions

File tree

Lib/test/test_format.py

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,8 @@ def test_g_format_has_no_trailing_zeros(self):
661661
self.assertEqual(format(12300050.0, "#.6g"), "1.23000e+07")
662662

663663
def test_with_two_commas_in_format_specifier(self):
664-
error_msg = re.escape("Cannot specify grouping ',' more than once")
664+
error_msg = re.escape(
665+
"Cannot specify grouping character ',' more than once")
665666
with self.assertRaisesRegex(ValueError, error_msg):
666667
'{:,,}'.format(1)
667668
with self.assertRaisesRegex(ValueError, error_msg):
@@ -670,7 +671,8 @@ def test_with_two_commas_in_format_specifier(self):
670671
'{:.,,f}'.format(1.1)
671672

672673
def test_with_two_underscore_in_format_specifier(self):
673-
error_msg = re.escape("Cannot specify grouping '_' more than once")
674+
error_msg = re.escape(
675+
"Cannot specify grouping character '_' more than once")
674676
with self.assertRaisesRegex(ValueError, error_msg):
675677
'{:__}'.format(1)
676678
with self.assertRaisesRegex(ValueError, error_msg):
@@ -696,32 +698,153 @@ def test_with_an_underscore_and_a_comma_in_format_specifier(self):
696698
with self.assertRaisesRegex(ValueError, error_msg):
697699
'{:._,f}'.format(1.1)
698700

699-
def test_better_error_message_format(self):
701+
def test_invalid_format_specifier_error_message(self):
700702
# https://bugs.python.org/issue20524
701703
for value in [12j, 12, 12.0, "12"]:
702-
with self.subTest(value=value):
704+
for bad_spec in ["%M", "ЫйXЯЧ", "\n'\\"]:
703705
# The format spec must be invalid for all types we're testing.
704-
# '%M' will suffice.
705-
bad_format_spec = '%M'
706-
err = re.escape("Invalid format specifier "
707-
f"'{bad_format_spec}' for object of type "
708-
f"'{type(value).__name__}'")
709-
with self.assertRaisesRegex(ValueError, err):
710-
f"xx{{value:{bad_format_spec}}}yy".format(value=value)
711-
712-
# Also test the builtin format() function.
713-
with self.assertRaisesRegex(ValueError, err):
714-
format(value, bad_format_spec)
715-
716-
# Also test f-strings.
717-
with self.assertRaisesRegex(ValueError, err):
718-
eval("f'xx{value:{bad_format_spec}}yy'")
719-
720-
def test_unicode_in_error_message(self):
721-
str_err = re.escape(
722-
"Invalid format specifier '%ЫйЯЧ' for object of type 'str'")
723-
with self.assertRaisesRegex(ValueError, str_err):
724-
"{a:%ЫйЯЧ}".format(a='a')
706+
with self.subTest(value=value, bad_spec=bad_spec):
707+
err = re.escape("Invalid format specifier "
708+
f"{bad_spec!r} for object of type "
709+
f"'{type(value).__name__}'")
710+
with self.assertRaisesRegex(ValueError, err):
711+
f"xx{{value:{bad_spec}}}yy".format(value=value)
712+
713+
# Also test the builtin format() function.
714+
with self.assertRaisesRegex(ValueError, err):
715+
format(value, bad_spec)
716+
717+
# Also test f-strings.
718+
with self.assertRaisesRegex(ValueError, err):
719+
eval("f'xx{value:{bad_spec}}yy'")
720+
721+
def test_invalid_specifier_type_error_message(self):
722+
for value in [12j, 12, 12.0, "12"]:
723+
for bad_spec, repr in [
724+
("M", "'M'"),
725+
("10$", "'$'"),
726+
("\t", "U+0009"),
727+
(",\x7f", "U+007F"),
728+
("о", "'о' (U+043E)"),
729+
("+#020,🐍", "'🐍' (U+1F40D)")
730+
]:
731+
with self.subTest(value=value, bad_spec=bad_spec):
732+
err = re.escape("Unknown format code "
733+
f"{repr} for object of type "
734+
f"'{type(value).__name__}'")
735+
with self.assertRaisesRegex(ValueError, err):
736+
f"xx{{value:{bad_spec}}}yy".format(value=value)
737+
738+
# Also test the builtin format() function.
739+
with self.assertRaisesRegex(ValueError, err):
740+
format(value, bad_spec)
741+
742+
# Also test f-strings.
743+
with self.assertRaisesRegex(ValueError, err):
744+
eval("f'xx{value:{bad_spec}}yy'")
745+
746+
def test_specifier_grouping_with_types(self):
747+
def assertEqualGroup(spec, value, expected):
748+
with self.subTest(spec=spec, value=value):
749+
self.assertEqual(("{:%s}" % spec).format(value), expected)
750+
self.assertEqual(format(value, spec), expected)
751+
self.assertEqual(f"{value:{spec}}", expected)
752+
753+
def assertRaisesGroup(spec, value, error_msg):
754+
with self.subTest(spec=spec, value=value):
755+
error_msg = re.escape(error_msg)
756+
with self.assertRaisesRegex(ValueError, error_msg):
757+
("{:%s}" % spec).format(value)
758+
with self.assertRaisesRegex(ValueError, error_msg):
759+
format(value, spec)
760+
with self.assertRaisesRegex(ValueError, error_msg):
761+
f"{value:{spec}}"
762+
763+
value = 1234567
764+
assertEqualGroup(",", value, "1,234,567")
765+
assertRaisesGroup("._", value,
766+
"Cannot specify '_' in fractional part with 'd'")
767+
assertEqualGroup(",d", value, "1,234,567")
768+
assertRaisesGroup("._d", value,
769+
"Cannot specify '_' in fractional part with 'd'")
770+
assertEqualGroup(",e", value, "1.234567e+06")
771+
assertEqualGroup("._e", value, "1.234_567e+06")
772+
assertRaisesGroup(",b", value, "Cannot specify ',' with 'b'")
773+
assertEqualGroup("_b", 1234, "100_1101_0010")
774+
assertRaisesGroup("._b", value,
775+
"Cannot specify '_' in fractional part with 'b'")
776+
assertRaisesGroup(",s", value, "Cannot specify ',' with 's'")
777+
assertRaisesGroup("._s", value,
778+
"Cannot specify '_' in fractional part with 's'")
779+
assertRaisesGroup(",n", value, "Cannot specify ',' with 'n'")
780+
assertRaisesGroup("._n", value,
781+
"Cannot specify '_' in fractional part with 'n'")
782+
783+
value = 1234567.1234567
784+
assertEqualGroup(",", value, "1,234,567.1234567")
785+
assertEqualGroup("._", value, "1234567.123_456_7")
786+
assertRaisesGroup(",d", value,
787+
"Unknown format code 'd' for object of type 'float'")
788+
assertRaisesGroup("._d", value,
789+
"Cannot specify '_' in fractional part with 'd'")
790+
assertEqualGroup(",e", value, "1.234567e+06")
791+
assertEqualGroup("._e", value, "1.234_567e+06")
792+
assertRaisesGroup(",b", value, "Cannot specify ',' with 'b'")
793+
assertRaisesGroup("_b", value,
794+
"Unknown format code 'b' for object of type 'float'")
795+
assertRaisesGroup("._b", value,
796+
"Cannot specify '_' in fractional part with 'b'")
797+
assertRaisesGroup(",s", value, "Cannot specify ',' with 's'")
798+
assertRaisesGroup("._s", value,
799+
"Cannot specify '_' in fractional part with 's'")
800+
assertRaisesGroup(",n", value, "Cannot specify ',' with 'n'")
801+
assertRaisesGroup("._n", value,
802+
"Cannot specify '_' in fractional part with 'n'")
803+
804+
value = 1234567.1234567+1234567.1234567j
805+
assertEqualGroup(",", value, "(1,234,567.1234567+1,234,567.1234567j)")
806+
assertEqualGroup("._", value, "(1234567.123_456_7+1234567.123_456_7j)")
807+
assertRaisesGroup(",d", value,
808+
"Unknown format code 'd' for object of type 'complex'")
809+
assertRaisesGroup("._d", value,
810+
"Cannot specify '_' in fractional part with 'd'")
811+
assertEqualGroup(",e", value, "1.234567e+06+1.234567e+06j")
812+
assertEqualGroup("._e", value, "1.234_567e+06+1.234_567e+06j")
813+
assertRaisesGroup(",b", value, "Cannot specify ',' with 'b'")
814+
assertRaisesGroup("_b", value,
815+
"Unknown format code 'b' for object of type 'complex'")
816+
assertRaisesGroup("._b", value,
817+
"Cannot specify '_' in fractional part with 'b'")
818+
assertRaisesGroup(",s", value, "Cannot specify ',' with 's'")
819+
assertRaisesGroup("._s", value,
820+
"Cannot specify '_' in fractional part with 's'")
821+
assertRaisesGroup(",n", value, "Cannot specify ',' with 'n'")
822+
assertRaisesGroup("._n", value,
823+
"Cannot specify '_' in fractional part with 'n'")
824+
825+
value = "1234567"
826+
assertRaisesGroup(",", value, "Cannot specify ',' with 's'")
827+
assertRaisesGroup("._", value,
828+
"Cannot specify '_' in fractional part with 's'")
829+
assertRaisesGroup(",d", value,
830+
"Unknown format code 'd' for object of type 'str'")
831+
assertRaisesGroup("._d", value,
832+
"Cannot specify '_' in fractional part with 'd'")
833+
assertRaisesGroup(",e", value,
834+
"Unknown format code 'e' for object of type 'str'")
835+
assertRaisesGroup("._e", value,
836+
"Unknown format code 'e' for object of type 'str'")
837+
assertRaisesGroup(",b", value, "Cannot specify ',' with 'b'")
838+
assertRaisesGroup("_b", value,
839+
"Unknown format code 'b' for object of type 'str'")
840+
assertRaisesGroup("._b", value,
841+
"Cannot specify '_' in fractional part with 'b'")
842+
assertRaisesGroup(",s", value, "Cannot specify ',' with 's'")
843+
assertRaisesGroup("._s", value,
844+
"Cannot specify '_' in fractional part with 's'")
845+
assertRaisesGroup(",n", value, "Cannot specify ',' with 'n'")
846+
assertRaisesGroup("._n", value,
847+
"Cannot specify '_' in fractional part with 'n'")
725848

726849
def test_negative_zero(self):
727850
## default behavior

Lib/test/test_fstring.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,14 +1697,16 @@ def test_invalid_syntax_error_message(self):
16971697
compile("f'{a $ b}'", "?", "exec")
16981698

16991699
def test_with_two_commas_in_format_specifier(self):
1700-
error_msg = re.escape("Cannot specify grouping ',' more than once")
1700+
error_msg = re.escape(
1701+
"Cannot specify grouping character ',' more than once")
17011702
with self.assertRaisesRegex(ValueError, error_msg):
17021703
f'{1:,,}'
17031704
with self.assertRaisesRegex(ValueError, error_msg):
17041705
f'{1.1:.,,}'
17051706

17061707
def test_with_two_underscore_in_format_specifier(self):
1707-
error_msg = re.escape("Cannot specify grouping '_' more than once")
1708+
error_msg = re.escape(
1709+
"Cannot specify grouping character '_' more than once")
17081710
with self.assertRaisesRegex(ValueError, error_msg):
17091711
f'{1:__}'
17101712
with self.assertRaisesRegex(ValueError, error_msg):

Lib/test/test_str.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,14 @@ def __repr__(self):
12621262
self.assertEqual('{0!a}'.format(F('Hello')), 'F(Hello)')
12631263
self.assertEqual('{0!a}'.format(F('\u0374')), 'F(\\u0374)')
12641264

1265+
self.assertEqual('{0:10.10}'.format(1.25), ' 1.25')
1266+
self.assertEqual('{0!s:10.10}'.format(1.25), '1.25 ')
1267+
self.assertEqual('{0!r:10.10}'.format(1.25), '1.25 ')
1268+
self.assertEqual('{0!a:10.10}'.format(1.25), '1.25 ')
1269+
1270+
# Not a conversion, but show that ! is allowed in a format spec.
1271+
self.assertEqual('{0:!<10.10}'.format(3.14), '3.14!!!!!!')
1272+
12651273
# test fallback to object.__format__
12661274
self.assertEqual('{0}'.format({}), '{}')
12671275
self.assertEqual('{0}'.format([]), '[]')
@@ -1320,9 +1328,24 @@ def __repr__(self):
13201328
self.assertRaises(ValueError, "{0}}".format, 0)
13211329
self.assertRaises(KeyError, "{foo}".format, bar=3)
13221330
self.assertRaises(ValueError, "{0!x}".format, 3)
1331+
self.assertRaises(ValueError, '{0!A}'.format, 3)
1332+
self.assertRaises(ValueError, '{0!G}'.format, 3)
1333+
self.assertRaises(ValueError, '{0!ä}'.format, 3)
1334+
self.assertRaises(ValueError, '{0!ɐ}'.format, 3)
1335+
self.assertRaises(ValueError, '{0!3}'.format, 3)
1336+
self.assertRaises(ValueError, '{0!!}'.format, 3)
13231337
self.assertRaises(ValueError, "{0!}".format, 0)
1338+
self.assertRaises(ValueError, "{0!s }".format, 0)
1339+
self.assertRaises(ValueError, "{0!s :10}".format, 0)
1340+
self.assertRaises(ValueError, '{0! s}'.format, 0)
1341+
self.assertRaises(ValueError, '{0! s }'.format, 0)
1342+
self.assertRaises(ValueError, '{0!ss}'.format, 0)
13241343
self.assertRaises(ValueError, "{0!rs}".format, 0)
1325-
self.assertRaises(ValueError, "{!}".format)
1344+
self.assertRaises(ValueError, '{0!rs:}'.format, 0)
1345+
self.assertRaises(ValueError, '{0!rs:s}'.format, 0)
1346+
self.assertRaises(ValueError, "{!}".format, 0)
1347+
self.assertRaises(ValueError, "{!:}".format, 0)
1348+
self.assertRaises(ValueError, "{!:8}".format, 0)
13261349
self.assertRaises(IndexError, "{:}".format)
13271350
self.assertRaises(IndexError, "{:s}".format)
13281351
self.assertRaises(IndexError, "{}".format)

Objects/unicode_formatter.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,8 @@ parse_internal_render_format_spec(PyObject *obj,
558558
if (next == ',' || next == '_') {
559559
/* Expect type, got another grouping character */
560560
PyErr_Format(PyExc_ValueError,
561-
"Cannot specify grouping '%c' more than once",
561+
"Cannot specify grouping character '%c' "
562+
"more than once",
562563
next);
563564
return 0;
564565
}

0 commit comments

Comments
 (0)