@@ -1880,6 +1880,36 @@ def test_basic(self):
18801880 self .assertEqual (writer .finish (),
18811881 "var=long value 'repr'" )
18821882
1883+ def test_create (self ):
1884+ # Test PyUnicodeWriter_Create() with non-zero size
1885+ s = 'Monty Python'
1886+
1887+ # Preallocate the exact length. Use 2 writes to force the creation
1888+ # of a buffer:
1889+ # 1. Use the read-only optimization.
1890+ # 2. Allocate a buffer of length character.
1891+ # No resize needed in finish().
1892+ writer = self .create_writer (len (s ))
1893+ writer .write_str (s [:5 ])
1894+ self .assertEqual (writer .get_buffer (), (5 , 127 , True ))
1895+ writer .write_str (s [5 :])
1896+ self .assertEqual (writer .get_buffer (), (len (s ), 127 , False ))
1897+ self .assertEqual (writer .finish (), s )
1898+
1899+ # Preallocate len(s)-1 characters. Use 3 writes:
1900+ # 1. Use read-only optimization.
1901+ # 2. Allocate a buffer of len-1 characters.
1902+ # 3. Resize the buffer with overallocation.
1903+ # finish() has to truncate the buffer.
1904+ writer = self .create_writer (len (s ) - 1 )
1905+ writer .write_str (s [:2 ])
1906+ self .assertEqual (writer .get_buffer (), (2 , 127 , True ))
1907+ writer .write_str (s [2 :5 ])
1908+ self .assertEqual (writer .get_buffer (), (len (s ) - 1 , 127 , False ))
1909+ writer .write_str (s [5 :])
1910+ self .assertGreater (writer .get_buffer ()[0 ], len (s ))
1911+ self .assertEqual (writer .finish (), s )
1912+
18831913 def test_repr_null (self ):
18841914 writer = self .create_writer (0 )
18851915 writer .write_utf8 (b'var=' , - 1 )
@@ -2087,32 +2117,39 @@ def test_substring_empty(self):
20872117 def test_singletons (self ):
20882118 for size in (0 , 123 ):
20892119 with self .subTest (size = size ):
2120+ # PyUnicodeWriter_Finish() returns the empty string singleton
2121+ # if no character has been written.
20902122 writer = self .create_writer (size )
20912123 writer .write_utf8 (b'utf8' , 0 )
20922124 writer .write_ascii (b'ascii' , 0 )
20932125 writer .write_widechar (b'wstr' , 0 )
20942126 writer .write_ucs4 (b'ucs4' , 0 )
2095- writer .write_substring ('text' , 0 , 0 )
2127+ writer .write_substring ('text' , 2 , 2 )
2128+ self .assertEqual (writer .get_buffer (), (None , 127 , False ))
20962129 self .assertIs (writer .finish (), '' )
20972130
20982131 for size in (0 , 123 ):
20992132 for ch in range (256 ):
21002133 with self .subTest (size = size , ch = ch ):
21012134 ch = chr (ch )
2135+ maxchar = (255 if ord (ch ) >= 128 else 127 )
21022136
2103- # If the first write is a Latin1 character and no buffer
2104- # was allocated yet, use the singleton as the read-only
2105- # buffer
2137+ # PyUnicodeWriter_WriteChar(ch) uses the read-only
2138+ # optimization with the character singleton if ch is a
2139+ # Latin1 character and no buffer was allocated yet.
21062140 writer = self .create_writer (size )
21072141 writer .write_char (ord (ch ))
2142+ self .assertEqual (writer .get_buffer (),
2143+ (1 , maxchar , True ))
21082144 self .assertIs (writer .finish (), ch )
21092145
2110- # PyUnicodeWriter_Finish() replaces the buffer
2111- # with the singleton
2146+ # PyUnicodeWriter_Finish() replaces the buffer with the
2147+ # singleton. Use PyUnicodeWriter_WriteSubstring() to avoid
2148+ # the read-only buffer optimization.
21122149 writer = self .create_writer (size )
2113- # Use PyUnicodeWriter_WriteSubstring() to avoid
2114- # the read-only buffer optimization
2115- writer . write_substring ( ch + 'xxx' , 0 , 1 )
2150+ writer . write_substring ( 'xxx' + ch + 'y' , 3 , 4 )
2151+ self . assertEqual ( writer . get_buffer (),
2152+ ( size or 1 , maxchar , False ) )
21162153 self .assertIs (writer .finish (), ch )
21172154
21182155 @unittest .skipUnless (support .Py_DEBUG , 'need debug build (Py_DEBUG)' )
@@ -2135,60 +2172,83 @@ def test_detect_overflow(self):
21352172 def test_memory_error (self ):
21362173 # Inject MemoryError in PyUnicodeWriter_WriteStr()
21372174 writer = self .create_writer (0 )
2138- writer .write_str ("start" )
2175+ writer .write_utf8 (b"start" , - 1 )
2176+ self .assertEqual (writer .get_buffer (), (5 , 127 , False ))
21392177 with self .assertRaises (MemoryError ):
21402178 with support .inject_memory_error_cm ():
21412179 # Resize the internal str object
21422180 writer .write_str ("s" * 1024 )
21432181 writer .write_str (" end" )
21442182 self .assertEqual (writer .finish (), "start end" )
21452183
2146- # Inject MemoryError in PyUnicodeWriter_Finish()
2184+ # Inject MemoryError in PyUnicodeWriter_Finish(). Use write_utf8() to
2185+ # allocate a buffer of 1024 character. finish() needs to truncate the
2186+ # buffer to 3 characters.
21472187 writer = self .create_writer (1024 )
2148- writer .write_str ("abc" )
2188+ writer .write_utf8 (b"abc" , - 1 )
2189+ self .assertEqual (writer .get_buffer (), (1024 , 127 , False ))
21492190 with self .assertRaises (MemoryError ):
21502191 with support .inject_memory_error_cm ():
2151- # Need to truncate the internal str object
21522192 writer .finish ()
21532193
21542194 def test_change_kind (self ):
21552195 writer = self .create_writer (0 )
2196+
21562197 # Create an ASCII buffer
21572198 writer .write_str ('ascii ' )
2199+ self .assertEqual (writer .get_buffer ()[1 ], 127 )
2200+
21582201 # Change the buffer to UCS1
21592202 writer .write_str ('latin1:\xe9 ' )
2203+ self .assertEqual (writer .get_buffer ()[1 ], 255 )
2204+
21602205 # Change the buffer to UCS2
21612206 writer .write_str ('ucs2:\u20ac ' )
2207+ self .assertEqual (writer .get_buffer ()[1 ], 0xffff )
2208+
21622209 # Change the buffer to UCS4
21632210 writer .write_str ('ucs4:\U0010ffff ' )
2211+ self .assertEqual (writer .get_buffer ()[1 ], 0x10_ffff )
2212+
21642213 self .assertEqual (writer .finish (),
21652214 'ascii latin1:\xe9 ucs2:\u20ac ucs4:\U0010ffff ' )
21662215
21672216 def test_readonly_optim (self ):
21682217 # Read-only optimization: if the first and only write is a Python str
21692218 # object and no buffer was allocated yet, return the object unchanged
21702219 unique_string = 'unique string'
2171- writer = self .create_writer (0 )
2172- writer .write_str (unique_string )
2173- self .assertIs (writer .finish (), unique_string )
2220+ expected = (len (unique_string ), 127 , True )
2221+ for size in (0 , 123 ):
2222+ with self .subTest (size = size ):
2223+ # PyUnicodeWriter_WriteStr() optimization
2224+ writer = self .create_writer (size )
2225+ writer .write_str (unique_string )
2226+ self .assertEqual (writer .get_buffer (), expected )
2227+ self .assertIs (writer .finish (), unique_string )
21742228
2175- writer = self .create_writer (0 )
2176- writer .write_substring (unique_string , 0 , len (unique_string ))
2177- self .assertIs (writer .finish (), unique_string )
2229+ # PyUnicodeWriter_WriteSubstring() optimization
2230+ writer = self .create_writer (size )
2231+ writer .write_substring (unique_string , 0 , len (unique_string ))
2232+ self .assertEqual (writer .get_buffer (), expected )
2233+ self .assertIs (writer .finish (), unique_string )
21782234
2179- class MyStr :
2180- def __str__ (self ):
2181- return unique_string
2182- writer = self .create_writer (0 )
2183- writer .write_str (MyStr ())
2184- self .assertIs (writer .finish (), unique_string )
2235+ # PyUnicodeWriter_WriteStr() optimization
2236+ class MyStr :
2237+ def __str__ (self ):
2238+ return unique_string
2239+ writer = self .create_writer (size )
2240+ writer .write_str (MyStr ())
2241+ self .assertEqual (writer .get_buffer (), expected )
2242+ self .assertIs (writer .finish (), unique_string )
21852243
2186- class MyRepr :
2187- def __repr__ (self ):
2188- return unique_string
2189- writer = self .create_writer (0 )
2190- writer .write_repr (MyRepr ())
2191- self .assertIs (writer .finish (), unique_string )
2244+ # PyUnicodeWriter_WriteRepr() optimization
2245+ class MyRepr :
2246+ def __repr__ (self ):
2247+ return unique_string
2248+ writer = self .create_writer (size )
2249+ writer .write_repr (MyRepr ())
2250+ self .assertEqual (writer .get_buffer (), expected )
2251+ self .assertIs (writer .finish (), unique_string )
21922252
21932253
21942254# Test PyUnicodeWriter_Format()
0 commit comments