@@ -535,6 +535,114 @@ def check_format(expected, format, *args):
535535 check_format ("repr= 12" ,
536536 b'repr=%5.2V' , None , b'123' )
537537
538+ # test the escaping mode ("#" flag) with %c
539+ check_format ('A' ,
540+ b'%#c' , c_int (ord ('A' )))
541+ check_format ('\\ n' ,
542+ b'%#c' , c_int (ord ('\n ' )))
543+ check_format ('\\ \\ ' ,
544+ b'%#c' , c_int (ord ('\\ ' )))
545+ check_format ('\\ x1b' ,
546+ b'%#c' , c_int (0x1b ))
547+ check_format ('\\ x7f' ,
548+ b'%#c' , c_int (0x7f ))
549+ check_format ('\\ u009f' ,
550+ b'%#c' , c_int (0x9f ))
551+ check_format ('\xe9 ' ,
552+ b'%#c' , c_int (0xe9 ))
553+ check_format ('\\ u2028' ,
554+ b'%#c' , c_int (0x2028 ))
555+ check_format ('\U0001f600 ' ,
556+ b'%#c' , c_int (0x1f600 ))
557+ check_format ('\\ ud800' ,
558+ b'%#c' , c_int (0xd800 ))
559+ check_format ('\\ udc9f' ,
560+ b'%#c' , c_int (0xdc9f ))
561+ check_format ('\\ u00e9' ,
562+ b'%+#c' , c_int (0xe9 ))
563+ check_format ('\\ u20ac' ,
564+ b'%+#c' , c_int (0x20ac ))
565+ check_format ('\\ U0001f600' ,
566+ b'%+#c' , c_int (0x1f600 ))
567+
568+ # test the escaping mode with %s
569+ check_format ('a\\ tb\\ r\\ nc\\ \\ d' ,
570+ b'%#s' , b'a\t b\r \n c\\ d' )
571+ check_format ('quote\' "' ,
572+ b'%#s' , b'quote\' "' )
573+ check_format ('caf\xe9 ' ,
574+ b'%#s' , b'caf\xc3 \xa9 ' )
575+ check_format ('caf\\ u00e9' ,
576+ b'%+#s' , b'caf\xc3 \xa9 ' )
577+ # valid UTF-8 is escaped as a character, invalid -- as a raw byte
578+ check_format ('\\ u009f' ,
579+ b'%#s' , b'\xc2 \x9f ' )
580+ check_format ('\\ x9f' ,
581+ b'%#s' , b'\x9f ' )
582+ check_format ('\\ x9f' ,
583+ b'%+#s' , b'\x9f ' )
584+ check_format ('a\\ xffb' ,
585+ b'%#s' , b'a\xff b' )
586+ check_format ('abc\\ xc3' ,
587+ b'%#s' , b'abc\xc3 ' )
588+ # the precision is applied before escaping, the width -- after
589+ check_format ('ab\\ n' ,
590+ b'%#.3s' , b'ab\n cd' )
591+ check_format (' ab\\ n' ,
592+ b'%#6.3s' , b'ab\n cd' )
593+ check_format ('ab\\ n ' ,
594+ b'%-#6.3s' , b'ab\n cd' )
595+ check_format ('a\\ xff' ,
596+ b'%#.2s' , b'a\xff bc' )
597+ check_format ('abc' ,
598+ b'%#.4s' , b'abc\xc3 \xa9 ' )
599+
600+ # test the escaping mode with %U, %V and %S
601+ check_format ('a\\ tb' ,
602+ b'%#U' , 'a\t b' )
603+ check_format ('caf\xe9 ' ,
604+ b'%#U' , 'caf\xe9 ' )
605+ check_format ('caf\\ u00e9' ,
606+ b'%+#U' , 'caf\xe9 ' )
607+ check_format ('a\\ tb' ,
608+ b'%#S' , 'a\t b' )
609+ check_format ('caf\xe9 ' ,
610+ b'%#S' , 'caf\xe9 ' )
611+ check_format ('caf\\ u00e9' ,
612+ b'%+#S' , 'caf\xe9 ' )
613+ check_format (' ab' ,
614+ b'%#5.2S' , 'ab\n cd' )
615+ # a lone surrogate in a string argument stays a surrogate
616+ check_format ('\\ udc9f' ,
617+ b'%#U' , '\udc9f ' )
618+ check_format ('\\ udc9f' ,
619+ b'%+#U' , '\udc9f ' )
620+ check_format (' ab' ,
621+ b'%#5.2U' , 'ab\n cd' )
622+ check_format ('a\\ tb' ,
623+ b'%#V' , 'a\t b' , b'ignored' )
624+ check_format ('\\ u009f' ,
625+ b'%#V' , None , b'\xc2 \x9f ' )
626+ check_format ('\\ x9f' ,
627+ b'%#V' , None , b'\x9f ' )
628+
629+ # test the escaping mode with %ls and %lV
630+ check_format ('a\\ tb' ,
631+ b'%#ls' , c_wchar_p ('a\t b' ))
632+ check_format ('caf\xe9 ' ,
633+ b'%#ls' , c_wchar_p ('caf\xe9 ' ))
634+ check_format ('caf\\ u00e9' ,
635+ b'%+#ls' , c_wchar_p ('caf\xe9 ' ))
636+ check_format ('a\\ tb' ,
637+ b'%#lV' , None , c_wchar_p ('a\t b' ))
638+
639+ # "+" requires "#" and is only supported for %c, %s, %S, %U and %V
640+ for format in (b'%+c' , b'%+s' , b'%+S' , b'%+U' , b'%+d' , b'%+#d' ,
641+ b'%+#R' , b'%+#A' , b'%+#T' , b'%+#p' ):
642+ with self .subTest (format = format ):
643+ with self .assertRaises (SystemError ):
644+ PyUnicode_FromFormat (format , py_object ('abc' ))
645+
538646 # test integer formats (%i, %d, %u, %o, %x, %X)
539647 check_format ('010' ,
540648 b'%03i' , c_int (10 ))
@@ -2003,6 +2111,11 @@ def test_format(self):
20032111 writer .write_char (ord ('.' ))
20042112 self .assertEqual (writer .finish (), 'abc 123.' )
20052113
2114+ def test_format_escape (self ):
2115+ writer = self .create_writer (0 )
2116+ self .writer_format (writer , b'%#s %+#s' , b'a\t b\xff ' , b'caf\xc3 \xa9 ' )
2117+ self .assertEqual (writer .finish (), 'a\\ tb\\ xff caf\\ u00e9' )
2118+
20062119 def test_recover_error (self ):
20072120 # test recovering from PyUnicodeWriter_Format() error
20082121 writer = self .create_writer (0 )
0 commit comments