1919from _testcapi import PY_SSIZE_T_MIN , PY_SSIZE_T_MAX , SIZEOF_WCHAR_T
2020
2121
22+ MAX_UNICODE = _testinternalcapi ._Py_MAX_UNICODE
23+ # The first invalid character after MAX_UNICODE
24+ INVALID_CHAR = MAX_UNICODE + 1
25+ # Maximum invalid character which fits into 32-bit Py_UCS4
26+ MAX_INVALID_CHAR = 0xFFFF_FFFF
2227NULL = None
2328
2429class Str (str ):
@@ -73,14 +78,14 @@ def test_new(self):
7378 self .assertEqual (new (0 , maxchar ), '' )
7479 self .assertEqual (new (5 , maxchar ), chr (maxchar )* 5 )
7580 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX , maxchar )
76- self .assertEqual (new (0 , 0x110000 ), '' )
81+ self .assertEqual (new (0 , INVALID_CHAR ), '' )
7782 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 2 , 0x4f60 )
7883 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 2 + 1 , 0x4f60 )
7984 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 2 , 0x1f600 )
8085 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 2 + 1 , 0x1f600 )
8186 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 4 , 0x1f600 )
8287 self .assertRaises (MemoryError , new , PY_SSIZE_T_MAX // 4 + 1 , 0x1f600 )
83- self .assertRaises (SystemError , new , 5 , 0x110000 )
88+ self .assertRaises (SystemError , new , 5 , INVALID_CHAR )
8489 self .assertRaises (SystemError , new , - 1 , 0 )
8590 self .assertRaises (SystemError , new , PY_SSIZE_T_MIN , 0 )
8691
@@ -115,7 +120,7 @@ def test_fill(self):
115120 s = strings [0 ]
116121 self .assertRaises (IndexError , fill , s , - 1 , 0 , 0x78 )
117122 self .assertRaises (IndexError , fill , s , PY_SSIZE_T_MIN , 0 , 0x78 )
118- self .assertRaises (ValueError , fill , s , 0 , 0 , 0x110000 )
123+ self .assertRaises (ValueError , fill , s , 0 , 0 , INVALID_CHAR )
119124 self .assertRaises (SystemError , fill , b'abc' , 0 , 0 , 0x78 )
120125 self .assertRaises (SystemError , fill , [], 0 , 0 , 0x78 )
121126 # CRASHES fill(s, 0, NULL, 0, 0)
@@ -129,7 +134,7 @@ def _test_writechar(self, writechar, *, check):
129134 '\U0001f600 \U0001f601 \U0001f602 '
130135 ]
131136 # one character for every kind + out of range code
132- chars = [0x78 , 0xa9 , 0x20ac , 0x1f638 , 0x110000 ]
137+ chars = [0x78 , 0xa9 , 0x20ac , 0x1f638 , INVALID_CHAR ]
133138 for i , s in enumerate (strings ):
134139 for j , c in enumerate (chars ):
135140 if j <= i :
@@ -301,7 +306,17 @@ def test_fromkindanddata(self):
301306 self .assertRaises (ValueError , fromkindanddata , 1 , NULL , - 1 )
302307 self .assertRaises (ValueError , fromkindanddata , 1 , NULL , PY_SSIZE_T_MIN )
303308 # CRASHES fromkindanddata(1, NULL, 1)
304- # CRASHES fromkindanddata(4, b'\xff\xff\xff\xff')
309+
310+ # Test invalid UCS-4 string
311+ for invalid_char in (INVALID_CHAR , MAX_INVALID_CHAR ):
312+ with self .subTest (invalid_char = invalid_char ):
313+ # Test single character
314+ ucs4_char = invalid_char .to_bytes (4 , byteorder = sys .byteorder )
315+ self .assertRaises (ValueError , fromkindanddata , 4 , ucs4_char )
316+
317+ # Test multiple characters
318+ s = 'valid' .encode (enc4 ) + ucs4_char
319+ self .assertRaises (ValueError , fromkindanddata , 4 , s )
305320
306321 def test_substring (self ):
307322 """Test PyUnicode_Substring()"""
@@ -446,7 +461,7 @@ def check_format(expected, format, *args):
446461 check_format ('\U0010ffff ' ,
447462 b'%c' , c_int (0x10ffff ))
448463 with self .assertRaises (OverflowError ):
449- PyUnicode_FromFormat (b'%c' , c_int (0x110000 ))
464+ PyUnicode_FromFormat (b'%c' , c_int (INVALID_CHAR ))
450465 # Issue #18183
451466 check_format ('\U00010000 \U00100000 ' ,
452467 b'%c%c' , c_int (0x10000 ), c_int (0x100000 ))
@@ -1015,7 +1030,7 @@ def test_fromordinal(self):
10151030 self .assertEqual (fromordinal (0x20ac ), '\u20ac ' )
10161031 self .assertEqual (fromordinal (0x1f600 ), '\U0001f600 ' )
10171032
1018- self .assertRaises (ValueError , fromordinal , 0x110000 )
1033+ self .assertRaises (ValueError , fromordinal , INVALID_CHAR )
10191034 self .assertRaises (ValueError , fromordinal , - 1 )
10201035
10211036 def test_asutf8 (self ):
@@ -1367,8 +1382,8 @@ def test_findchar(self):
13671382 self .assertEqual (unicode_findchar (str , ord (ch ), 0 , len (str ), - 1 ), i )
13681383
13691384 str = "!>_<!"
1370- self .assertEqual (unicode_findchar (str , 0x110000 , 0 , len (str ), 1 ), - 1 )
1371- self .assertEqual (unicode_findchar (str , 0x110000 , 0 , len (str ), - 1 ), - 1 )
1385+ self .assertEqual (unicode_findchar (str , INVALID_CHAR , 0 , len (str ), 1 ), - 1 )
1386+ self .assertEqual (unicode_findchar (str , INVALID_CHAR , 0 , len (str ), - 1 ), - 1 )
13721387 # start < end
13731388 self .assertEqual (unicode_findchar (str , ord ('!' ), 1 , len (str )+ 1 , 1 ), 4 )
13741389 self .assertEqual (unicode_findchar (str , ord ('!' ), 1 , PY_SSIZE_T_MAX , 1 ), 4 )
@@ -1757,7 +1772,7 @@ def test_max_char_value(self):
17571772 self .assertEqual (max_char_value ('ascii' ), 0x7f )
17581773 self .assertEqual (max_char_value ('latin1:\xe9 ' ), 0xff )
17591774 self .assertEqual (max_char_value ('bmp:\u20ac ' ), 0xffff )
1760- self .assertEqual (max_char_value (' \U0010ffff ' ), 0x10_ffff )
1775+ self .assertEqual (max_char_value (chr ( 0x10_0000 ) ), 0x10_ffff )
17611776
17621777 # CRASHES max_char_value(NULL)
17631778
@@ -1935,8 +1950,8 @@ def test_write_char(self):
19351950 writer .write_char (ord ('$' ))
19361951 writer .write_char (0x20ac )
19371952 writer .write_char (0x10_ffff )
1938- self .assertRaises (ValueError , writer .write_char , 0x11_0000 )
1939- self .assertRaises (ValueError , writer .write_char , 0xFFFF_FFFF )
1953+ self .assertRaises (ValueError , writer .write_char , INVALID_CHAR )
1954+ self .assertRaises (ValueError , writer .write_char , MAX_INVALID_CHAR )
19401955 self .assertEqual (writer .finish (),
19411956 "\0 $\u20AC \U0010FFFF " )
19421957
@@ -2106,11 +2121,20 @@ def test_ucs4(self):
21062121 writer .write_ucs4 ("pair\uD83D \uDC0D " .encode (encoding , 'surrogatepass' ))
21072122 writer .write_char (ord ("-" ))
21082123 writer .write_ucs4 ("null[\0 ]" .encode (encoding ), 7 )
2109- invalid = (b'\x00 \x00 \x11 \x00 ' if sys .byteorder == 'little' else
2110- b'\x00 \x11 \x00 \x00 ' )
2111- # CRASHES writer.write_ucs4("invalid".encode(encoding) + invalid)
21122124 writer .write_ucs4 (NULL , 0 )
21132125 # CRASHES writer.write_ucs4(NULL, 1)
2126+
2127+ # Invalid UCS-4 strings
2128+ for invalid_char in (INVALID_CHAR , MAX_INVALID_CHAR ):
2129+ with self .subTest (invalid_char = invalid_char ):
2130+ # Test single character
2131+ ucs4_char = invalid_char .to_bytes (4 , byteorder = sys .byteorder )
2132+ self .assertRaises (ValueError , writer .write_ucs4 , ucs4_char )
2133+
2134+ # Test multiple characters
2135+ s = 'valid' .encode (encoding ) + ucs4_char
2136+ self .assertRaises (ValueError , writer .write_ucs4 , s )
2137+
21142138 self .assertEqual (writer .finish (),
21152139 "lone\udc80 -pair\ud83d \udc0d -null[\x00 ]" )
21162140
0 commit comments