@@ -281,7 +281,8 @@ def test_parse_reentrancy_with_encoding(self, encoding):
281281 # See https://github.com/python/cpython/issues/146169.
282282 parser = expat .ParserCreate (encoding = encoding )
283283
284- CharacterDataHandler = lambda data : parser .Parse (data , False )
284+ def CharacterDataHandler (data ):
285+ return parser .Parse (data , False )
285286 CharacterDataHandler = mock .Mock (wraps = CharacterDataHandler )
286287 def StartElementHandler (name , attrs ):
287288 parser .CharacterDataHandler = CharacterDataHandler
@@ -294,6 +295,25 @@ def StartElementHandler(name, attrs):
294295 parser .Parse (payload [i :i + 1 ], i == len (payload ) - 1 )
295296 CharacterDataHandler .assert_called_once_with ("x" )
296297
298+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
299+ def test_parse_file_reentrancy_with_encoding (self , encoding ):
300+ # See https://github.com/python/cpython/issues/146169.
301+ parser = expat .ParserCreate (encoding = encoding )
302+
303+ def CharacterDataHandler (data ):
304+ return parser .ParseFile (BytesIO (data .encode (encoding )))
305+ CharacterDataHandler = mock .Mock (wraps = CharacterDataHandler )
306+ def StartElementHandler (name , attrs ):
307+ parser .CharacterDataHandler = CharacterDataHandler
308+ parser .StartElementHandler = StartElementHandler
309+
310+ payload = "<a>x" .encode (encoding )
311+ payload_buffer = BytesIO (payload )
312+ msg = re .escape ("cannot call ParseFile() from within a handler" )
313+ with self .assertRaisesRegex (RuntimeError , msg ):
314+ parser .ParseFile (payload_buffer )
315+ CharacterDataHandler .assert_called_once_with ("x" )
316+
297317 @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
298318 def test_parse_reentrancy_allowed_for_external_parser (self , encoding ):
299319 parser = expat .ParserCreate (encoding = encoding )
@@ -322,6 +342,33 @@ def StartElementHandler(*args):
322342 external_ref_args = ('ext' , None , 'entity.file' , None )
323343 ExternalEntityRefHandler .assert_called_once_with (* external_ref_args )
324344
345+ @support .subTests ("encoding" , ("utf-8" , "utf-16" ))
346+ def test_parse_file_reentrancy_allowed_for_external_parser (self , encoding ):
347+ parser = expat .ParserCreate (encoding = encoding )
348+ subparser = parser .ExternalEntityParserCreate (None , encoding )
349+ payload_extstr = '<!ENTITY ext SYSTEM "entity.file">'
350+
351+ def ExternalEntityRefHandler (* args ):
352+ subparser .ParseFile (BytesIO (payload_extstr .encode (encoding )))
353+ # return a nonzero integer to indicate that parsing continues
354+ return 1
355+ ExternalEntityRefHandler = mock .Mock (wraps = ExternalEntityRefHandler )
356+
357+ def StartElementHandler (* args ):
358+ parser .ExternalEntityRefHandler = ExternalEntityRefHandler
359+ parser .StartElementHandler = StartElementHandler
360+
361+ payload = textwrap .dedent (f"""\
362+ <?xml version="1.0" standalone="no"?>
363+ <!DOCTYPE quotations SYSTEM "quotations.dtd" [{ payload_extstr } ]>
364+ <root>&ext;</root>
365+ """ ).encode (encoding )
366+
367+ # Check that external parsers be called from parent's handlers.
368+ parser .ParseFile (BytesIO (payload ))
369+ external_ref_args = ('ext' , None , 'entity.file' , None )
370+ ExternalEntityRefHandler .assert_called_once_with (* external_ref_args )
371+
325372
326373class NamespaceSeparatorTest (unittest .TestCase ):
327374 def test_legal (self ):
0 commit comments