@@ -884,6 +884,52 @@ def test_clear(self):
884884 self .assertEqual (set (cf .sections ()), set ())
885885 self .assertEqual (set (cf [self .default_section ].keys ()), {'foo' })
886886
887+ def test_section_popitem (self ):
888+ cf = self .fromstring (f"""
889+ [section]
890+ name1 { self .delimiters [0 ]} value1
891+ name2 { self .delimiters [0 ]} value2
892+ name3 { self .delimiters [0 ]} value3
893+ """ , defaults = {"name2" : "<default>" , "name4" : "<default>" })
894+ section = cf ['section' ]
895+ self .assertEqual (section .popitem (), ('name1' , 'value1' ))
896+ self .assertEqual (section .popitem (), ('name2' , 'value2' ))
897+ self .assertEqual (section .popitem (), ('name3' , 'value3' ))
898+ with self .assertRaises (KeyError ):
899+ section .popitem ()
900+ self .assertEqual (dict (section ), {'name2' : '<default>' ,
901+ 'name4' : '<default>' })
902+ default = cf [self .default_section ]
903+ self .assertEqual (default .popitem (), ('name2' , '<default>' ))
904+ self .assertEqual (default .popitem (), ('name4' , '<default>' ))
905+ with self .assertRaises (KeyError ):
906+ default .popitem ()
907+ self .assertEqual (dict (section ), {})
908+ del cf ['section' ]
909+ with self .assertRaises (configparser .NoSectionError ):
910+ section .popitem ()
911+
912+ def test_section_clear (self ):
913+ cf = self .fromstring (f"""
914+ [section1]
915+ name1 { self .delimiters [0 ]} value1
916+ name2 { self .delimiters [0 ]} value2
917+ [section2]
918+ name3 { self .delimiters [0 ]} value3
919+ """ , defaults = {"name2" : "<default>" , "name4" : "<default>" })
920+ section = cf ['section1' ]
921+ section .clear ()
922+ self .assertEqual (dict (section ), {'name2' : '<default>' ,
923+ 'name4' : '<default>' })
924+ self .assertEqual (cf .sections (), ['section1' , 'section2' ])
925+ cf [self .default_section ].clear ()
926+ self .assertEqual (dict (cf [self .default_section ]), {})
927+ self .assertEqual (dict (section ), {})
928+ self .assertEqual (dict (cf ['section2' ]), {'name3' : 'value3' })
929+ del cf ['section1' ]
930+ with self .assertRaises (configparser .NoSectionError ):
931+ section .clear ()
932+
887933 def test_setitem (self ):
888934 cf = self .fromstring ("""
889935 [section1]
@@ -970,6 +1016,11 @@ def test_interpolation_missing_value(self):
9701016 self .assertEqual (e .args , ('name' , 'Interpolation Error' ,
9711017 '%(reference)s' , 'reference' ))
9721018
1019+ def test_section_clear_does_not_interpolate (self ):
1020+ cf = self .get_interpolation_config ()
1021+ cf ["Interpolation Error" ].clear ()
1022+ self .assertEqual (cf .options ("Interpolation Error" ), [])
1023+
9731024 def test_items (self ):
9741025 self .check_items_config ([('default' , '<default>' ),
9751026 ('getdefault' , '|<default>|' ),
@@ -2341,6 +2392,33 @@ def test_option_regex_no_value_does_not_backtrack(self):
23412392 self .assertTrue (parser .has_option ("section" , "x" + " " * 40000 + "y" ))
23422393
23432394
2395+ class MappingComplexityTestCase (unittest .TestCase ):
2396+ """Removing all sections or all options should take linear time."""
2397+
2398+ @support .requires_resource ('cpu' )
2399+ def test_clear_many_sections (self ):
2400+ n = 50000
2401+ parser = configparser .RawConfigParser ()
2402+ parser .read_string ("" .join (f"[{ i } ]\n " for i in range (n )))
2403+ self .assertEqual (len (parser .sections ()), n )
2404+ # A quadratic clear() would copy the names of all remaining
2405+ # sections for every removed section.
2406+ parser .clear ()
2407+ self .assertEqual (parser .sections (), [])
2408+
2409+ @support .requires_resource ('cpu' )
2410+ def test_clear_many_options (self ):
2411+ n = 50000
2412+ parser = configparser .RawConfigParser ({"key" : "value" })
2413+ parser ["section" ] = {str (i ): str (i ) for i in range (n )}
2414+ section = parser ["section" ]
2415+ self .assertEqual (len (section ), n + 1 )
2416+ # A quadratic clear() would copy the whole section merged with
2417+ # the defaults for every removed option.
2418+ section .clear ()
2419+ self .assertEqual (dict (section ), {"key" : "value" })
2420+
2421+
23442422class MiscTestCase (unittest .TestCase ):
23452423 def test__all__ (self ):
23462424 support .check__all__ (self , configparser , not_exported = {"Error" })
0 commit comments