@@ -117,6 +117,88 @@ def test_stateful_keep_buffer(self):
117117 self .assertRaises (UnicodeEncodeError , encoder .encode , '\u0123 ' )
118118 self .assertEqual (encoder .encode ('' , True ), b'\xa9 \xdc ' )
119119
120+ def test_state_methods_with_buffer_state (self ):
121+ # euc_jis_2004 stores state as a buffer of pending bytes
122+ encoder = codecs .getincrementalencoder ('euc_jis_2004' )()
123+
124+ initial_state = encoder .getstate ()
125+ self .assertEqual (encoder .encode ('\u00e6 \u0300 ' ), b'\xab \xc4 ' )
126+ encoder .setstate (initial_state )
127+ self .assertEqual (encoder .encode ('\u00e6 \u0300 ' ), b'\xab \xc4 ' )
128+
129+ self .assertEqual (encoder .encode ('\u00e6 ' ), b'' )
130+ partial_state = encoder .getstate ()
131+ self .assertEqual (encoder .encode ('\u0300 ' ), b'\xab \xc4 ' )
132+ encoder .setstate (partial_state )
133+ self .assertEqual (encoder .encode ('\u0300 ' ), b'\xab \xc4 ' )
134+
135+ def test_state_methods_with_non_buffer_state (self ):
136+ # iso2022_jp stores state without using a buffer
137+ encoder = codecs .getincrementalencoder ('iso2022_jp' )()
138+
139+ self .assertEqual (encoder .encode ('z' ), b'z' )
140+ en_state = encoder .getstate ()
141+
142+ self .assertEqual (encoder .encode ('\u3042 ' ), b'\x1b \x24 \x42 \x24 \x22 ' )
143+ jp_state = encoder .getstate ()
144+ self .assertEqual (encoder .encode ('z' ), b'\x1b \x28 \x42 z' )
145+
146+ encoder .setstate (jp_state )
147+ self .assertEqual (encoder .encode ('\u3042 ' ), b'\x24 \x22 ' )
148+
149+ encoder .setstate (en_state )
150+ self .assertEqual (encoder .encode ('z' ), b'z' )
151+
152+ def test_getstate_returns_expected_value (self ):
153+ # Note: getstate is implemented such that these state values
154+ # are expected to be the same across all builds of Python,
155+ # regardless of x32/64 bit, endianness and compiler.
156+
157+ # euc_jis_2004 stores state as a buffer of pending bytes
158+ buffer_state_encoder = codecs .getincrementalencoder ('euc_jis_2004' )()
159+ self .assertEqual (buffer_state_encoder .getstate (), 0 )
160+ buffer_state_encoder .encode ('\u00e6 ' )
161+ self .assertEqual (buffer_state_encoder .getstate (),
162+ int .from_bytes (
163+ b"\x02 "
164+ b"\xc3 \xa6 "
165+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " ,
166+ 'little' ))
167+ buffer_state_encoder .encode ('\u0300 ' )
168+ self .assertEqual (buffer_state_encoder .getstate (), 0 )
169+
170+ # iso2022_jp stores state without using a buffer
171+ non_buffer_state_encoder = codecs .getincrementalencoder ('iso2022_jp' )()
172+ self .assertEqual (non_buffer_state_encoder .getstate (),
173+ int .from_bytes (
174+ b"\x00 "
175+ b"\x42 \x42 \x00 \x00 \x00 \x00 \x00 \x00 " ,
176+ 'little' ))
177+ non_buffer_state_encoder .encode ('\u3042 ' )
178+ self .assertEqual (non_buffer_state_encoder .getstate (),
179+ int .from_bytes (
180+ b"\x00 "
181+ b"\xc2 \x42 \x00 \x00 \x00 \x00 \x00 \x00 " ,
182+ 'little' ))
183+
184+ def test_setstate_validates_input_size (self ):
185+ encoder = codecs .getincrementalencoder ('euc_jp' )()
186+ pending_size_nine = int .from_bytes (
187+ b"\x09 "
188+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 "
189+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " ,
190+ 'little' )
191+ self .assertRaises (UnicodeError , encoder .setstate , pending_size_nine )
192+
193+ def test_setstate_validates_input_bytes (self ):
194+ encoder = codecs .getincrementalencoder ('euc_jp' )()
195+ invalid_utf8 = int .from_bytes (
196+ b"\x01 "
197+ b"\xff "
198+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " ,
199+ 'little' )
200+ self .assertRaises (UnicodeDecodeError , encoder .setstate , invalid_utf8 )
201+
120202 def test_issue5640 (self ):
121203 encoder = codecs .getincrementalencoder ('shift-jis' )('backslashreplace' )
122204 self .assertEqual (encoder .encode ('\xff ' ), b'\\ xff' )
@@ -165,6 +247,37 @@ def test_decode_unicode(self):
165247 decoder = codecs .getincrementaldecoder (enc )()
166248 self .assertRaises (TypeError , decoder .decode , "" )
167249
250+ def test_state_methods (self ):
251+ decoder = codecs .getincrementaldecoder ('euc_jp' )()
252+
253+ # Decode a complete input sequence
254+ self .assertEqual (decoder .decode (b'\xa4 \xa6 ' ), '\u3046 ' )
255+ pending1 , _ = decoder .getstate ()
256+ self .assertEqual (pending1 , b'' )
257+
258+ # Decode first half of a partial input sequence
259+ self .assertEqual (decoder .decode (b'\xa4 ' ), '' )
260+ pending2 , flags2 = decoder .getstate ()
261+ self .assertEqual (pending2 , b'\xa4 ' )
262+
263+ # Decode second half of a partial input sequence
264+ self .assertEqual (decoder .decode (b'\xa6 ' ), '\u3046 ' )
265+ pending3 , _ = decoder .getstate ()
266+ self .assertEqual (pending3 , b'' )
267+
268+ # Jump back and decode second half of partial input sequence again
269+ decoder .setstate ((pending2 , flags2 ))
270+ self .assertEqual (decoder .decode (b'\xa6 ' ), '\u3046 ' )
271+ pending4 , _ = decoder .getstate ()
272+ self .assertEqual (pending4 , b'' )
273+
274+ def test_setstate_validates_input (self ):
275+ decoder = codecs .getincrementaldecoder ('euc_jp' )()
276+ self .assertRaises (TypeError , decoder .setstate , 123 )
277+ self .assertRaises (TypeError , decoder .setstate , ("invalid" , 0 ))
278+ self .assertRaises (TypeError , decoder .setstate , (b"1234" , "invalid" ))
279+ self .assertRaises (UnicodeError , decoder .setstate , (b"123456789" , 0 ))
280+
168281class Test_StreamReader (unittest .TestCase ):
169282 def test_bug1728403 (self ):
170283 try :
0 commit comments