@@ -275,6 +275,89 @@ def test_parse_again(self):
275275 self .assertEqual (expat .ErrorString (cm .exception .code ),
276276 expat .errors .XML_ERROR_FINISHED )
277277
278+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
279+ def test_parse_reentrancy_with_encoding (self , encoding ):
280+ # See https://github.com/python/cpython/issues/146169.
281+ parser = expat .ParserCreate (encoding = encoding )
282+
283+ def CharacterDataHandler (data ):
284+ return parser .Parse (data , False )
285+ CharacterDataHandler = mock .Mock (wraps = CharacterDataHandler )
286+ parser .CharacterDataHandler = CharacterDataHandler
287+
288+ payload = "<a>x" .encode (encoding )
289+ msg = re .escape ("cannot call Parse() from within a handler" )
290+ with self .assertRaisesRegex (RuntimeError , msg ):
291+ for i in range (len (payload )):
292+ parser .Parse (payload [i :i + 1 ], i == len (payload ) - 1 )
293+ CharacterDataHandler .assert_called_once_with ("x" )
294+
295+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
296+ def test_parse_file_reentrancy_with_encoding (self , encoding ):
297+ # See https://github.com/python/cpython/issues/146169.
298+ parser = expat .ParserCreate (encoding = encoding )
299+
300+ def CharacterDataHandler (data ):
301+ return parser .ParseFile (BytesIO (data .encode (encoding )))
302+ CharacterDataHandler = mock .Mock (wraps = CharacterDataHandler )
303+ parser .CharacterDataHandler = CharacterDataHandler
304+
305+ payload = "<a>x" .encode (encoding )
306+ payload_buffer = BytesIO (payload )
307+ msg = re .escape ("cannot call ParseFile() from within a handler" )
308+ with self .assertRaisesRegex (RuntimeError , msg ):
309+ parser .ParseFile (payload_buffer )
310+ CharacterDataHandler .assert_called_once_with ("x" )
311+
312+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
313+ def test_parse_reentrancy_allowed_for_external_parser (self , encoding ):
314+ parser = expat .ParserCreate (encoding = encoding )
315+ subparser = parser .ExternalEntityParserCreate (None , encoding )
316+ payload_extstr = '<!ENTITY ext SYSTEM "entity.file">'
317+
318+ def ExternalEntityRefHandler (* args ):
319+ subparser .Parse (payload_extstr , True )
320+ # return a nonzero integer to indicate that parsing continues
321+ return 1
322+ ExternalEntityRefHandler = mock .Mock (wraps = ExternalEntityRefHandler )
323+ parser .ExternalEntityRefHandler = ExternalEntityRefHandler
324+
325+ payload = textwrap .dedent (f"""\
326+ <?xml version="1.0" standalone="no"?>
327+ <!DOCTYPE quotations SYSTEM "quotations.dtd" [{ payload_extstr } ]>
328+ <root>&ext;</root>
329+ """ ).encode (encoding )
330+
331+ # Check that external parsers be called from parent's handlers.
332+ for i in range (len (payload )):
333+ parser .Parse (payload [i :i + 1 ], i == len (payload ) - 1 )
334+ external_ref_args = ('ext' , None , 'entity.file' , None )
335+ ExternalEntityRefHandler .assert_called_once_with (* external_ref_args )
336+
337+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
338+ def test_parse_file_reentrancy_allowed_for_external_parser (self , encoding ):
339+ parser = expat .ParserCreate (encoding = encoding )
340+ subparser = parser .ExternalEntityParserCreate (None , encoding )
341+ payload_extstr = '<!ENTITY ext SYSTEM "entity.file">'
342+
343+ def ExternalEntityRefHandler (* args ):
344+ subparser .ParseFile (BytesIO (payload_extstr .encode (encoding )))
345+ # return a nonzero integer to indicate that parsing continues
346+ return 1
347+ ExternalEntityRefHandler = mock .Mock (wraps = ExternalEntityRefHandler )
348+ parser .ExternalEntityRefHandler = ExternalEntityRefHandler
349+
350+ payload = textwrap .dedent (f"""\
351+ <?xml version="1.0" standalone="no"?>
352+ <!DOCTYPE quotations SYSTEM "quotations.dtd" [{ payload_extstr } ]>
353+ <root>&ext;</root>
354+ """ ).encode (encoding )
355+
356+ # Check that external parsers be called from parent's handlers.
357+ parser .ParseFile (BytesIO (payload ))
358+ external_ref_args = ('ext' , None , 'entity.file' , None )
359+ ExternalEntityRefHandler .assert_called_once_with (* external_ref_args )
360+
278361 @support .subTests ('encoding' , [
279362 'utf-8' , 'utf-16' , 'utf-16be' , 'utf-16le' ,
280363 'iso8859-1' , 'iso8859-2' , 'iso8859-3' , 'iso8859-4' , 'iso8859-5' ,
0 commit comments