1313from xml .sax .saxutils import XMLGenerator , escape , unescape , quoteattr , \
1414 XMLFilterBase , prepare_input_source
1515from xml .sax .expatreader import create_parser
16- from xml .sax .handler import feature_namespaces
16+ from xml .sax .handler import feature_namespaces , feature_external_ges
1717from xml .sax .xmlreader import InputSource , AttributesImpl , AttributesNSImpl
1818from io import BytesIO , StringIO
1919import codecs
2020import gc
2121import os .path
2222import shutil
23+ from urllib .error import URLError
2324from test import support
2425from test .support import findfile , run_unittest , TESTFN
2526
@@ -911,6 +912,18 @@ def notationDecl(self, name, publicId, systemId):
911912 def unparsedEntityDecl (self , name , publicId , systemId , ndata ):
912913 self ._entities .append ((name , publicId , systemId , ndata ))
913914
915+
916+ class TestEntityRecorder :
917+ def __init__ (self ):
918+ self .entities = []
919+
920+ def resolveEntity (self , publicId , systemId ):
921+ self .entities .append ((publicId , systemId ))
922+ source = InputSource ()
923+ source .setPublicId (publicId )
924+ source .setSystemId (systemId )
925+ return source
926+
914927 def test_expat_dtdhandler (self ):
915928 parser = create_parser ()
916929 handler = self .TestDTDHandler ()
@@ -927,6 +940,32 @@ def test_expat_dtdhandler(self):
927940 [("GIF" , "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN" , None )])
928941 self .assertEqual (handler ._entities , [("img" , None , "expat.gif" , "GIF" )])
929942
943+ def test_expat_external_dtd_enabled (self ):
944+ parser = create_parser ()
945+ parser .setFeature (feature_external_ges , True )
946+ resolver = self .TestEntityRecorder ()
947+ parser .setEntityResolver (resolver )
948+
949+ with self .assertRaises (URLError ):
950+ parser .feed (
951+ '<!DOCTYPE external SYSTEM "unsupported://non-existing">\n '
952+ )
953+ self .assertEqual (
954+ resolver .entities , [(None , 'unsupported://non-existing' )]
955+ )
956+
957+ def test_expat_external_dtd_default (self ):
958+ parser = create_parser ()
959+ resolver = self .TestEntityRecorder ()
960+ parser .setEntityResolver (resolver )
961+
962+ parser .feed (
963+ '<!DOCTYPE external SYSTEM "unsupported://non-existing">\n '
964+ )
965+ parser .feed ('<doc />' )
966+ parser .close ()
967+ self .assertEqual (resolver .entities , [])
968+
930969 # ===== EntityResolver support
931970
932971 class TestEntityResolver :
@@ -936,8 +975,9 @@ def resolveEntity(self, publicId, systemId):
936975 inpsrc .setByteStream (BytesIO (b"<entity/>" ))
937976 return inpsrc
938977
939- def test_expat_entityresolver (self ):
978+ def test_expat_entityresolver_enabled (self ):
940979 parser = create_parser ()
980+ parser .setFeature (feature_external_ges , True )
941981 parser .setEntityResolver (self .TestEntityResolver ())
942982 result = BytesIO ()
943983 parser .setContentHandler (XMLGenerator (result ))
@@ -951,6 +991,22 @@ def test_expat_entityresolver(self):
951991 self .assertEqual (result .getvalue (), start +
952992 b"<doc><entity></entity></doc>" )
953993
994+ def test_expat_entityresolver_default (self ):
995+ parser = create_parser ()
996+ self .assertEqual (parser .getFeature (feature_external_ges ), False )
997+ parser .setEntityResolver (self .TestEntityResolver ())
998+ result = BytesIO ()
999+ parser .setContentHandler (XMLGenerator (result ))
1000+
1001+ parser .feed ('<!DOCTYPE doc [\n ' )
1002+ parser .feed (' <!ENTITY test SYSTEM "whatever">\n ' )
1003+ parser .feed (']>\n ' )
1004+ parser .feed ('<doc>&test;</doc>' )
1005+ parser .close ()
1006+
1007+ self .assertEqual (result .getvalue (), start +
1008+ b"<doc></doc>" )
1009+
9541010 # ===== Attributes support
9551011
9561012 class AttrGatherer (ContentHandler ):
0 commit comments