11from test .support import verbose , TestFailed
22import locale
33import sys
4+ import re
45import test .support as support
56import unittest
67
@@ -248,7 +249,7 @@ def test_common_format(self):
248249 # base marker shouldn't change the size
249250 testcommon ("%0#35.33o" , big , "0o012345670123456701234567012345670" )
250251
251- # Some small ints, in both Python int and flavors) .
252+ # Some small ints, in both Python int and flavors.
252253 testcommon ("%d" , 42 , "42" )
253254 testcommon ("%d" , - 42 , "-42" )
254255 testcommon ("%d" , 42.0 , "42" )
@@ -274,9 +275,9 @@ def test_common_format(self):
274275 test_exc_common ('% %s' , 1 , ValueError ,
275276 "unsupported format character '%' (0x25) at index 2" )
276277 test_exc_common ('%d' , '1' , TypeError ,
277- "%d format: a number is required, not str" )
278+ "%d format: a real number is required, not str" )
278279 test_exc_common ('%d' , b'1' , TypeError ,
279- "%d format: a number is required, not bytes" )
280+ "%d format: a real number is required, not bytes" )
280281 test_exc_common ('%x' , '1' , TypeError ,
281282 "%x format: an integer is required, not str" )
282283 test_exc_common ('%x' , 3.14 , TypeError ,
@@ -493,6 +494,154 @@ def test_precision_c_limits(self):
493494 with self .assertRaises (ValueError ) as cm :
494495 format (c , ".%sf" % (INT_MAX + 1 ))
495496
497+ def test_g_format_has_no_trailing_zeros (self ):
498+ # regression test for bugs.python.org/issue40780
499+ self .assertEqual ("%.3g" % 1505.0 , "1.5e+03" )
500+ self .assertEqual ("%#.3g" % 1505.0 , "1.50e+03" )
501+
502+ self .assertEqual (format (1505.0 , ".3g" ), "1.5e+03" )
503+ self .assertEqual (format (1505.0 , "#.3g" ), "1.50e+03" )
504+
505+ self .assertEqual (format (12300050.0 , ".6g" ), "1.23e+07" )
506+ self .assertEqual (format (12300050.0 , "#.6g" ), "1.23000e+07" )
507+
508+ # TODO: RUSTPYTHON
509+ @unittest .expectedFailure
510+ def test_with_two_commas_in_format_specifier (self ):
511+ error_msg = re .escape ("Cannot specify ',' with ','." )
512+ with self .assertRaisesRegex (ValueError , error_msg ):
513+ '{:,,}' .format (1 )
514+
515+ # TODO: RUSTPYTHON
516+ @unittest .expectedFailure
517+ def test_with_two_underscore_in_format_specifier (self ):
518+ error_msg = re .escape ("Cannot specify '_' with '_'." )
519+ with self .assertRaisesRegex (ValueError , error_msg ):
520+ '{:__}' .format (1 )
521+
522+ # TODO: RUSTPYTHON
523+ @unittest .expectedFailure
524+ def test_with_a_commas_and_an_underscore_in_format_specifier (self ):
525+ error_msg = re .escape ("Cannot specify both ',' and '_'." )
526+ with self .assertRaisesRegex (ValueError , error_msg ):
527+ '{:,_}' .format (1 )
528+
529+ # TODO: RUSTPYTHON
530+ @unittest .expectedFailure
531+ def test_with_an_underscore_and_a_comma_in_format_specifier (self ):
532+ error_msg = re .escape ("Cannot specify both ',' and '_'." )
533+ with self .assertRaisesRegex (ValueError , error_msg ):
534+ '{:_,}' .format (1 )
535+
536+ # TODO: RUSTPYTHON
537+ @unittest .expectedFailure
538+ def test_better_error_message_format (self ):
539+ # https://bugs.python.org/issue20524
540+ for value in [12j , 12 , 12.0 , "12" ]:
541+ with self .subTest (value = value ):
542+ # The format spec must be invalid for all types we're testing.
543+ # '%M' will suffice.
544+ bad_format_spec = '%M'
545+ err = re .escape ("Invalid format specifier "
546+ f"'{ bad_format_spec } ' for object of type "
547+ f"'{ type (value ).__name__ } '" )
548+ with self .assertRaisesRegex (ValueError , err ):
549+ f"xx{{value:{ bad_format_spec } }}yy" .format (value = value )
550+
551+ # Also test the builtin format() function.
552+ with self .assertRaisesRegex (ValueError , err ):
553+ format (value , bad_format_spec )
554+
555+ # Also test f-strings.
556+ with self .assertRaisesRegex (ValueError , err ):
557+ eval ("f'xx{value:{bad_format_spec}}yy'" )
558+
559+ # TODO: RUSTPYTHON
560+ @unittest .expectedFailure
561+ def test_unicode_in_error_message (self ):
562+ str_err = re .escape (
563+ "Invalid format specifier '%ЫйЯЧ' for object of type 'str'" )
564+ with self .assertRaisesRegex (ValueError , str_err ):
565+ "{a:%ЫйЯЧ}" .format (a = 'a' )
566+
567+ # TODO: RUSTPYTHON
568+ @unittest .expectedFailure
569+ def test_negative_zero (self ):
570+ ## default behavior
571+ self .assertEqual (f"{ - 0. :.1f} " , "-0.0" )
572+ self .assertEqual (f"{ - .01 :.1f} " , "-0.0" )
573+ self .assertEqual (f"{ - 0 :.1f} " , "0.0" ) # integers do not distinguish -0
574+
575+ ## z sign option
576+ self .assertEqual (f"{ 0. :z.1f} " , "0.0" )
577+ self .assertEqual (f"{ 0. :z6.1f} " , " 0.0" )
578+ self .assertEqual (f"{ - 1. :z6.1f} " , " -1.0" )
579+ self .assertEqual (f"{ - 0. :z.1f} " , "0.0" )
580+ self .assertEqual (f"{ .01 :z.1f} " , "0.0" )
581+ self .assertEqual (f"{ - 0 :z.1f} " , "0.0" ) # z is allowed for integer input
582+ self .assertEqual (f"{ - .01 :z.1f} " , "0.0" )
583+ self .assertEqual (f"{ 0. :z.2f} " , "0.00" )
584+ self .assertEqual (f"{ - 0. :z.2f} " , "0.00" )
585+ self .assertEqual (f"{ .001 :z.2f} " , "0.00" )
586+ self .assertEqual (f"{ - .001 :z.2f} " , "0.00" )
587+
588+ self .assertEqual (f"{ 0. :z.1e} " , "0.0e+00" )
589+ self .assertEqual (f"{ - 0. :z.1e} " , "0.0e+00" )
590+ self .assertEqual (f"{ 0. :z.1E} " , "0.0E+00" )
591+ self .assertEqual (f"{ - 0. :z.1E} " , "0.0E+00" )
592+
593+ self .assertEqual (f"{ - 0.001 :z.2e} " , "-1.00e-03" ) # tests for mishandled
594+ # rounding
595+ self .assertEqual (f"{ - 0.001 :z.2g} " , "-0.001" )
596+ self .assertEqual (f"{ - 0.001 :z.2%} " , "-0.10%" )
597+
598+ self .assertEqual (f"{ - 00000.000001 :z.1f} " , "0.0" )
599+ self .assertEqual (f"{ - 00000. :z.1f} " , "0.0" )
600+ self .assertEqual (f"{ - .0000000000 :z.1f} " , "0.0" )
601+
602+ self .assertEqual (f"{ - 00000.000001 :z.2f} " , "0.00" )
603+ self .assertEqual (f"{ - 00000. :z.2f} " , "0.00" )
604+ self .assertEqual (f"{ - .0000000000 :z.2f} " , "0.00" )
605+
606+ self .assertEqual (f"{ .09 :z.1f} " , "0.1" )
607+ self .assertEqual (f"{ - .09 :z.1f} " , "-0.1" )
608+
609+ self .assertEqual (f"{ - 0. : z.0f} " , " 0" )
610+ self .assertEqual (f"{ - 0. :+z.0f} " , "+0" )
611+ self .assertEqual (f"{ - 0. :-z.0f} " , "0" )
612+ self .assertEqual (f"{ - 1. : z.0f} " , "-1" )
613+ self .assertEqual (f"{ - 1. :+z.0f} " , "-1" )
614+ self .assertEqual (f"{ - 1. :-z.0f} " , "-1" )
615+
616+ self .assertEqual (f"{ 0.j :z.1f} " , "0.0+0.0j" )
617+ self .assertEqual (f"{ - 0.j :z.1f} " , "0.0+0.0j" )
618+ self .assertEqual (f"{ .01j :z.1f} " , "0.0+0.0j" )
619+ self .assertEqual (f"{ - .01j :z.1f} " , "0.0+0.0j" )
620+
621+ self .assertEqual (f"{ - 0. :z>6.1f} " , "zz-0.0" ) # test fill, esp. 'z' fill
622+ self .assertEqual (f"{ - 0. :z>z6.1f} " , "zzz0.0" )
623+ self .assertEqual (f"{ - 0. :x>z6.1f} " , "xxx0.0" )
624+ self .assertEqual (f"{ - 0. :🖤>z6.1f} " , "🖤🖤🖤0.0" ) # multi-byte fill char
625+
626+ # TODO: RUSTPYTHON
627+ @unittest .expectedFailure
628+ def test_specifier_z_error (self ):
629+ error_msg = re .compile ("Invalid format specifier '.*z.*'" )
630+ with self .assertRaisesRegex (ValueError , error_msg ):
631+ f"{ 0 :z+f} " # wrong position
632+ with self .assertRaisesRegex (ValueError , error_msg ):
633+ f"{ 0 :fz} " # wrong position
634+
635+ error_msg = re .escape ("Negative zero coercion (z) not allowed" )
636+ with self .assertRaisesRegex (ValueError , error_msg ):
637+ f"{ 0 :zd} " # can't apply to int presentation type
638+ with self .assertRaisesRegex (ValueError , error_msg ):
639+ f"{ 'x' :zs} " # can't apply to string
640+
641+ error_msg = re .escape ("unsupported format character 'z'" )
642+ with self .assertRaisesRegex (ValueError , error_msg ):
643+ "%z.1f" % 0 # not allowed in old style string interpolation
644+
496645
497646if __name__ == "__main__" :
498647 unittest .main ()
0 commit comments