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