@@ -4088,6 +4088,49 @@ def test_chunk_size(self):
40884088 # a failed assignment does not change the value
40894089 self .assertEqual (t ._CHUNK_SIZE , 1024 )
40904090
4091+ def test_reentrant_seek_during_tell (self ):
4092+ # gh-153539: reading short of _CHUNK_SIZE leaves residual bytes in the
4093+ # snapshot, so tell() re-decodes and calls the decoder's getstate(); a
4094+ # reentrant seek() there must not free the snapshot tell() still uses.
4095+ # C-only: _pyio binds next_input as a strong local and cannot crash.
4096+ wrapper = None
4097+ armed = False
4098+
4099+ class ReentrantDecoder (codecs .IncrementalDecoder ):
4100+ def decode (self , input , final = False ):
4101+ return bytes (input ).decode ("latin-1" )
4102+ def getstate (self ):
4103+ nonlocal armed
4104+ if wrapper is not None and armed :
4105+ armed = False
4106+ wrapper .seek (0 )
4107+ return (b"" , 0 )
4108+ def setstate (self , state ):
4109+ pass
4110+
4111+ def search (name ):
4112+ if name != "reentrant_tell_test" :
4113+ return None
4114+ return codecs .CodecInfo (
4115+ name = name ,
4116+ encode = lambda s , e = 'strict' : (s .encode ("latin-1" ), len (s )),
4117+ decode = lambda b , e = 'strict' : (bytes (b ).decode ("latin-1" ), len (b )),
4118+ incrementaldecoder = ReentrantDecoder )
4119+
4120+ codecs .register (search )
4121+ self .addCleanup (codecs .unregister , search )
4122+ raw = self .BytesIO (b"abcdefghijklmnop" * 8 )
4123+ wrapper = self .TextIOWrapper (self .BufferedReader (raw ),
4124+ encoding = "reentrant_tell_test" , newline = "" )
4125+ wrapper ._CHUNK_SIZE = 8
4126+ wrapper .read (5 )
4127+ armed = True
4128+ self .assertIsInstance (wrapper .tell (), int )
4129+ # tell() at the snapshot boundary takes the early return that owns and
4130+ # must release next_input; exercise it too (leak-checked under -R).
4131+ wrapper .seek (0 )
4132+ self .assertIsInstance (wrapper .tell (), int )
4133+
40914134 def test_initialization (self ):
40924135 r = self .BytesIO (b"\xc3 \xa9 \n \n " )
40934136 b = self .BufferedReader (r , 1000 )
0 commit comments