@@ -3739,7 +3739,7 @@ def test_basic(self):
37393739 self .assertEqual (next (ET .iterparse (sourcefile , parser = parser ))[0 ], 'end' )
37403740
37413741 tree = ET .ElementTree (None )
3742- self .assertRaises ( AttributeError , tree .iter )
3742+ self .assertEqual ( list ( tree .iter ()), [] )
37433743
37443744 # Issue #16913
37453745 doc = ET .XML ("<root>a&<sub>b&</sub>c&</root>" )
@@ -3836,6 +3836,179 @@ def test_pickle(self):
38363836 pickle .dumps (it , proto )
38373837
38383838
3839+
3840+ class DocumentChildrenTest (unittest .TestCase ):
3841+ # gh-68475: comments and processing instructions outside the root element
3842+
3843+ sample = ('<!--lead--><?pi data?><r><?in?><a/></r><?after?><!--tail-->' )
3844+
3845+ def parse (self , text = None ):
3846+ builder = ET .TreeBuilder (insert_comments = True , insert_pis = True )
3847+ tree = ET .ElementTree ()
3848+ tree .parse (io .StringIO (text if text is not None else self .sample ),
3849+ ET .XMLParser (target = builder ))
3850+ return tree
3851+
3852+ def test_only_the_root_by_default (self ):
3853+ tree = ET .ElementTree ()
3854+ tree .parse (io .StringIO (self .sample ))
3855+ self .assertEqual (summarize_list (tree .children ), ['r' ])
3856+ self .assertEqual (len (tree .children ), 1 )
3857+ self .assertIs (tree .children [0 ], tree .getroot ())
3858+
3859+ def test_parse_keeps_the_prolog_and_the_epilog (self ):
3860+ tree = self .parse ()
3861+ self .assertEqual (summarize_list (tree .children ),
3862+ [ET .Comment , ET .ProcessingInstruction , 'r' ,
3863+ ET .ProcessingInstruction , ET .Comment ])
3864+ self .assertEqual (tree .children [0 ].text , 'lead' )
3865+ self .assertEqual (tree .children [- 1 ].text , 'tail' )
3866+ self .assertIs (tree .getroot (), tree .children [2 ])
3867+
3868+ def test_write (self ):
3869+ tree = self .parse ()
3870+ file = io .StringIO ()
3871+ tree .write (file , encoding = 'unicode' )
3872+ self .assertEqual (file .getvalue (),
3873+ '<!--lead--><?pi data?><r><?in?><a /></r>'
3874+ '<?after?><!--tail-->' )
3875+
3876+ def test_iter (self ):
3877+ tree = self .parse ()
3878+ self .assertEqual (summarize_list (tree .iter ()),
3879+ [ET .Comment , ET .ProcessingInstruction , 'r' ,
3880+ ET .ProcessingInstruction , 'a' ,
3881+ ET .ProcessingInstruction , ET .Comment ])
3882+ self .assertEqual (summarize_list (tree .iter ('*' )),
3883+ [ET .Comment , ET .ProcessingInstruction , 'r' ,
3884+ ET .ProcessingInstruction , 'a' ,
3885+ ET .ProcessingInstruction , ET .Comment ])
3886+ self .assertEqual (summarize_list (tree .iter ('a' )), ['a' ])
3887+ # comments and processing instructions can be selected by the factory
3888+ self .assertEqual (summarize_list (tree .iter (ET .ProcessingInstruction )),
3889+ [ET .ProcessingInstruction ] * 3 )
3890+ self .assertEqual (summarize_list (tree .iter (ET .Comment )),
3891+ [ET .Comment , ET .Comment ])
3892+
3893+ def test_find_searches_from_the_root (self ):
3894+ tree = self .parse ()
3895+ # find() and friends search from the root element, so they return
3896+ # the processing instruction inside it, but not those outside
3897+ self .assertEqual (summarize_list (tree .findall ('*' )),
3898+ [ET .ProcessingInstruction , 'a' ])
3899+ self .assertEqual (summarize_list (tree .findall ('.//*' )),
3900+ [ET .ProcessingInstruction , 'a' ])
3901+ self .assertEqual (tree .find ('a' ).tag , 'a' )
3902+
3903+ def test_append_and_insert (self ):
3904+ tree = ET .ElementTree (ET .Element ('r' ))
3905+ tree .children .insert (0 , ET .Comment ('lead' ))
3906+ tree .children .append (ET .ProcessingInstruction ('pi' , 'data' ))
3907+ self .assertEqual (summarize_list (tree .children ),
3908+ [ET .Comment , 'r' , ET .ProcessingInstruction ])
3909+ self .assertIs (tree .getroot (), tree .children [1 ])
3910+
3911+ def test_the_first_element_becomes_the_root (self ):
3912+ tree = ET .ElementTree ()
3913+ tree .children .append (ET .Comment ('lead' ))
3914+ self .assertIsNone (tree .getroot ())
3915+ elem = ET .Element ('r' )
3916+ tree .children .append (elem )
3917+ self .assertIs (tree .getroot (), elem )
3918+
3919+ def test_only_one_element (self ):
3920+ tree = ET .ElementTree (ET .Element ('r' ))
3921+ children = tree .children
3922+ children .insert (0 , ET .Comment ('lead' ))
3923+ self .assertRaises (ValueError , children .append , ET .Element ('second' ))
3924+ self .assertRaises (ValueError , children .insert , 0 , ET .Element ('second' ))
3925+ self .assertRaises (ValueError , children .extend , [ET .Element ('second' )])
3926+ # the comment cannot be replaced by an element either
3927+ self .assertRaises (ValueError , children .__setitem__ , 0 ,
3928+ ET .Element ('second' ))
3929+ self .assertEqual (summarize_list (tree .children ), [ET .Comment , 'r' ])
3930+ self .assertEqual (tree .getroot ().tag , 'r' )
3931+ # but the root element can be replaced
3932+ children [1 ] = ET .Element ('other' )
3933+ self .assertEqual (tree .getroot ().tag , 'other' )
3934+
3935+ def test_not_an_element (self ):
3936+ tree = ET .ElementTree (ET .Element ('r' ))
3937+ self .assertRaises (TypeError , tree .children .append , 'text' )
3938+ self .assertRaises (TypeError , tree .children .insert , 0 , None )
3939+ self .assertEqual (summarize_list (tree .children ), ['r' ])
3940+
3941+ def test_remove_and_delete (self ):
3942+ tree = self .parse ()
3943+ root = tree .getroot ()
3944+ tree .children .remove (root )
3945+ self .assertIsNone (tree .getroot ())
3946+ self .assertEqual (summarize_list (tree .children ),
3947+ [ET .Comment , ET .ProcessingInstruction ,
3948+ ET .ProcessingInstruction , ET .Comment ])
3949+ del tree .children [0 ]
3950+ self .assertEqual (summarize_list (tree .children ),
3951+ [ET .ProcessingInstruction , ET .ProcessingInstruction ,
3952+ ET .Comment ])
3953+ tree .children .clear ()
3954+ self .assertEqual (summarize_list (tree .children ), [])
3955+ self .assertIsNone (tree .getroot ())
3956+
3957+ def test_slices (self ):
3958+ tree = self .parse ()
3959+ root = tree .getroot ()
3960+ tree .children [0 :2 ] = [ET .Comment ('one' )]
3961+ self .assertEqual (summarize_list (tree .children ),
3962+ [ET .Comment , 'r' , ET .ProcessingInstruction ,
3963+ ET .Comment ])
3964+ self .assertIs (tree .getroot (), root )
3965+ # the slice which replaces the root element can add another one
3966+ new = ET .Element ('new' )
3967+ tree .children [1 :2 ] = [new ]
3968+ self .assertIs (tree .getroot (), new )
3969+ # but not two
3970+ self .assertRaises (ValueError , tree .children .__setitem__ ,
3971+ slice (0 , 2 ), [ET .Element ('a' ), ET .Element ('b' )])
3972+ self .assertIs (tree .getroot (), new )
3973+ del tree .children [1 :2 ]
3974+ self .assertIsNone (tree .getroot ())
3975+
3976+ def test_the_root_cannot_be_a_comment (self ):
3977+ self .assertRaises (ValueError , ET .ElementTree , ET .Comment ('c' ))
3978+ self .assertRaises (ValueError , ET .ElementTree ,
3979+ ET .ProcessingInstruction ('pi' ))
3980+ tree = ET .ElementTree (ET .Element ('r' ))
3981+ self .assertRaises (ValueError , tree ._setroot , ET .Comment ('c' ))
3982+
3983+ def test_tostring_of_a_comment (self ):
3984+ # tostring() serializes a single node, which can be a comment
3985+ self .assertEqual (ET .tostring (ET .Comment ('c' )), b'<!--c-->' )
3986+ self .assertEqual (ET .tostring (ET .ProcessingInstruction ('t' , 'd' )),
3987+ b'<?t d?>' )
3988+
3989+ def test_document_without_the_root (self ):
3990+ tree = ET .ElementTree ()
3991+ tree .children .extend ([ET .Comment ('a' ), ET .ProcessingInstruction ('p' )])
3992+ file = io .StringIO ()
3993+ tree .write (file , encoding = 'unicode' )
3994+ self .assertEqual (file .getvalue (), '<!--a--><?p?>' )
3995+
3996+ def test_builder_document (self ):
3997+ builder = ET .TreeBuilder (insert_comments = True , insert_pis = True )
3998+ parser = ET .XMLParser (target = builder )
3999+ parser .feed (self .sample )
4000+ root = parser .close ()
4001+ self .assertEqual (root .tag , 'r' )
4002+ self .assertEqual (len (builder .document ()), 5 )
4003+ self .assertIs (builder .document ()[2 ], root )
4004+
4005+ def test_builder_document_without_inserting (self ):
4006+ builder = ET .TreeBuilder ()
4007+ parser = ET .XMLParser (target = builder )
4008+ parser .feed (self .sample )
4009+ root = parser .close ()
4010+ self .assertEqual (builder .document (), [root ])
4011+
38394012class TreeBuilderTest (unittest .TestCase ):
38404013 sample1 = ('<!DOCTYPE html PUBLIC'
38414014 ' "-//W3C//DTD XHTML 1.0 Transitional//EN"'
0 commit comments