@@ -239,39 +239,60 @@ def test_resize(self):
239239 """Test _PyBytes_Resize()"""
240240 _resize = _testcapi .bytes_resize
241241
242- def resize (obj , size , new ):
243- result = _resize (obj , size , new )
244- if 1 <= len (result ):
245- if new or size != len (obj ):
246- # gh-156995: Make sure that the result is a fresh object.
247- # Previously, _PyBytes_Resize(&obj, 1) returned a singleton
248- # if _PyObject_IsUniquelyReferenced() is false.
249- self .assertEqual (sys .getrefcount (result ), 1 )
250- self .assertFalse (sys ._is_immortal (result ))
251- else :
242+ def assert_is_fresh_copy (result , refcnt , is_new_obj ):
243+ self .assertEqual (refcnt , 1 )
244+ self .assertTrue (is_new_obj )
245+ self .assertFalse (sys ._is_immortal (result ))
246+
247+ def resize (obj , size , new , compute_hash = False ):
248+ old_size = len (obj )
249+ result , refcnt , is_new_obj = _resize (obj , size , new ,
250+ compute_hash = compute_hash )
251+
252+ if size == old_size :
253+ # Return the same object unchanged
254+ self .assertFalse (is_new_obj )
255+ elif old_size == 0 :
256+ assert_is_fresh_copy (result , refcnt , is_new_obj )
257+ elif size == 0 :
252258 # check that the result is the empty bytes string singleton
259+ self .assertEqual (result , b'' )
253260 self .assertTrue (sys ._is_immortal (result ))
261+ self .assertTrue (is_new_obj )
262+ elif (not new ) or compute_hash :
263+ # gh-156995: Make sure that the result is a fresh object.
264+ # Previously, _PyBytes_Resize(&obj, 1) returned a singleton
265+ # if _PyObject_IsUniquelyReferenced() is false.
266+ assert_is_fresh_copy (result , refcnt , is_new_obj )
267+ else :
268+ # An in-place resize can return the same memory address, or
269+ # not. 'is_new_obj' cannot be tested.
270+ self .assertEqual (refcnt , 1 )
271+ self .assertFalse (sys ._is_immortal (result ))
272+
254273 return result
255274
256275 for new in True , False :
257- with self .subTest (new = new ):
258- self .assertEqual (resize (b'abc' , 0 , new ), b'' )
259- self .assertEqual (resize (b'abc' , 1 , new ), b'a' )
260- self .assertEqual (resize (b'abc' , 2 , new ), b'ab' )
261- self .assertEqual (resize (b'abc' , 3 , new ), b'abc' )
262- b = resize (b'abc' , 4 , new )
263- self .assertEqual (len (b ), 4 )
264- self .assertEqual (b [:3 ], b'abc' )
265-
266- self .assertEqual (resize (b'a' , 0 , new ), b'' )
267- self .assertEqual (resize (b'a' , 1 , new ), b'a' )
268- b = resize (b'a' , 2 , new )
269- self .assertEqual (len (b ), 2 )
270- self .assertEqual (b [:1 ], b'a' )
271-
272- self .assertEqual (resize (b'' , 0 , new ), b'' )
273- self .assertEqual (len (resize (b'' , 1 , new )), 1 )
274- self .assertEqual (len (resize (b'' , 2 , new )), 2 )
276+ for compute_hash in True , False :
277+ with self .subTest (new = new , compute_hash = compute_hash ):
278+ self .assertEqual (resize (b'abc' , 0 , new , compute_hash ), b'' )
279+ self .assertEqual (resize (b'abc' , 1 , new , compute_hash ), b'a' )
280+ self .assertEqual (resize (b'abc' , 2 , new , compute_hash ), b'ab' )
281+ self .assertEqual (resize (b'abc' , 3 , new , compute_hash ), b'abc' )
282+
283+ b = resize (b'abc' , 4 , new , compute_hash )
284+ self .assertEqual (len (b ), 4 )
285+ self .assertEqual (b [:3 ], b'abc' )
286+
287+ self .assertEqual (resize (b'a' , 0 , new , compute_hash ), b'' )
288+ self .assertEqual (resize (b'a' , 1 , new , compute_hash ), b'a' )
289+ b = resize (b'a' , 2 , new , compute_hash )
290+ self .assertEqual (len (b ), 2 )
291+ self .assertEqual (b [:1 ], b'a' )
292+
293+ self .assertEqual (resize (b'' , 0 , new , compute_hash ), b'' )
294+ self .assertEqual (len (resize (b'' , 1 , new , compute_hash )), 1 )
295+ self .assertEqual (len (resize (b'' , 2 , new , compute_hash )), 2 )
275296
276297 self .assertRaises (SystemError , resize , b'abc' , - 1 , False )
277298 self .assertRaises (SystemError , resize , bytearray (b'abc' ), 3 , False )
@@ -607,15 +628,45 @@ def test_format_i(self):
607628
608629 writer = self .create_writer ()
609630 writer .format_i (b'x=%i, ' , 123 )
610- writer .format_i (b'y=%i ' , 456 )
631+ writer .format_i (b'y=%d ' , 456 )
611632 self .bytes_equal (writer .finish (), b'x=123, y=456' )
612633
634+ # Make sure that %i allocates the right number of bytes
635+ n_str = '1234567'
636+ for i in range (1 , len (n_str ) + 1 ):
637+ n = int (n_str [:i ])
638+ writer = self .create_writer (0 )
639+ writer .format_i (b'n=%i' , n )
640+ self .bytes_equal (writer .finish (), f'n={ n } ' .encode ())
641+
642+ # Test invalid format: "%t" is invalid, stop the parser and copy
643+ # remaining bytes
644+ writer = self .create_writer ()
645+ writer .write_bytes (b'prefix ' , - 1 )
646+ writer .format_i (b'y=%._ t %i' , 0 )
647+ writer .write_bytes (b'.' , 1 )
648+ self .bytes_equal (writer .finish (), b'prefix y=%._ t %i.' )
649+
613650 def test_format_s (self ):
614651 # Test PyBytesWriter_Format()
615652 writer = self .create_writer ()
616653 writer .format_s (b's=%s' , b'Hello World' )
617654 self .bytes_equal (writer .finish (), b's=Hello World' )
618655
656+ # Test precision
657+ writer = self .create_writer ()
658+ writer .format_s (b'%.6s' , b'Monty Python' )
659+ writer .format_s (b'%.20s' , b'3.0' )
660+ self .bytes_equal (writer .finish (), b'Monty 3.0' )
661+
662+ # Make sure that %s allocates the right number of bytes
663+ text = b'1234567'
664+ for i in range (1 , len (text ) + 1 ):
665+ s = text [:i ]
666+ writer = self .create_writer (0 )
667+ writer .format_s (b's=%s' , s )
668+ self .bytes_equal (writer .finish (), b's=' + s )
669+
619670 @support .nomemtest
620671 def test_format_s_memory_error (self ):
621672 writer = self .create_writer ()
0 commit comments