mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	SF bug #997050: Document, test, & check for non-string values in ConfigParser. Moved the new string-only restriction added in rev. 1.65 to the SafeConfigParser class, leaving existing ConfigParser & RawConfigParser behavior alone, and documented the conditions under which non-string values work.
This commit is contained in:
		
							parent
							
								
									68a1abdade
								
							
						
					
					
						commit
						1cbf206d32
					
				
					 4 changed files with 78 additions and 21 deletions
				
			
		|  | @ -238,10 +238,12 @@ option in the given \var{section}. | |||
| \end{methoddesc} | ||||
| 
 | ||||
| \begin{methoddesc}{set}{section, option, value} | ||||
| If the given section exists, set the given option to the specified value; | ||||
| otherwise raise \exception{NoSectionError}.  \var{value} must be a | ||||
| string (\class{str} or \class{unicode}); if not, \exception{TypeError} | ||||
| is raised. | ||||
| If the given section exists, set the given option to the specified | ||||
| value; otherwise raise \exception{NoSectionError}.  While it is | ||||
| possible to use \class{RawConfigParser} (or \class{ConfigParser} with | ||||
| \var{raw} parameters set to true) for \emph{internal} storage of | ||||
| non-string values, full functionality (including interpolation and | ||||
| output to files) can only be achieved using string values. | ||||
| \versionadded{1.6} | ||||
| \end{methoddesc} | ||||
| 
 | ||||
|  | @ -281,8 +283,6 @@ option names case sensitive. | |||
| 
 | ||||
| The \class{ConfigParser} class extends some methods of the | ||||
| \class{RawConfigParser} interface, adding some optional arguments. | ||||
| The \class{SafeConfigParser} class implements the same extended | ||||
| interface. | ||||
| 
 | ||||
| \begin{methoddesc}{get}{section, option\optional{, raw\optional{, vars}}} | ||||
| Get an \var{option} value for the named \var{section}.  All the | ||||
|  | @ -297,3 +297,17 @@ option in the given \var{section}. Optional arguments have the | |||
| same meaning as for the \method{get()} method. | ||||
| \versionadded{2.3} | ||||
| \end{methoddesc} | ||||
| 
 | ||||
| 
 | ||||
| \subsection{SafeConfigParser Objects \label{SafeConfigParser-objects}} | ||||
| 
 | ||||
| The \class{SafeConfigParser} class implements the same extended | ||||
| interface as \class{ConfigParser}, with the following addition: | ||||
| 
 | ||||
| \begin{methoddesc}{set}{section, option, value} | ||||
| If the given section exists, set the given option to the specified | ||||
| value; otherwise raise \exception{NoSectionError}.  \var{value} must | ||||
| be a string (\class{str} or \class{unicode}); if not, | ||||
| \exception{TypeError} is raised. | ||||
| \versionadded{2.4} | ||||
| \end{methoddesc} | ||||
|  |  | |||
|  | @ -92,7 +92,8 @@ | |||
| __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError", | ||||
|            "InterpolationError", "InterpolationDepthError", | ||||
|            "InterpolationSyntaxError", "ParsingError", | ||||
|            "MissingSectionHeaderError", "ConfigParser", "SafeConfigParser", | ||||
|            "MissingSectionHeaderError", | ||||
|            "ConfigParser", "SafeConfigParser", "RawConfigParser", | ||||
|            "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] | ||||
| 
 | ||||
| DEFAULTSECT = "DEFAULT" | ||||
|  | @ -348,8 +349,6 @@ def has_option(self, section, option): | |||
| 
 | ||||
|     def set(self, section, option, value): | ||||
|         """Set an option.""" | ||||
|         if not isinstance(value, basestring): | ||||
|             raise TypeError("option values must be strings") | ||||
|         if not section or section == DEFAULTSECT: | ||||
|             sectdict = self._defaults | ||||
|         else: | ||||
|  | @ -633,3 +632,9 @@ def _interpolate_some(self, option, accum, rest, section, map, depth): | |||
|                 raise InterpolationSyntaxError( | ||||
|                     option, section, | ||||
|                     "'%%' must be followed by '%%' or '(', found: %r" % (rest,)) | ||||
| 
 | ||||
|     def set(self, section, option, value): | ||||
|         """Set an option.  Extend ConfigParser.set: check for string values.""" | ||||
|         if not isinstance(value, basestring): | ||||
|             raise TypeError("option values must be strings") | ||||
|         ConfigParser.set(self, section, option, value) | ||||
|  |  | |||
|  | @ -240,18 +240,6 @@ class mystr(str): | |||
|             cf.set("sect", "option1", unicode("splat")) | ||||
|             cf.set("sect", "option2", unicode("splat")) | ||||
| 
 | ||||
|     def test_set_nonstring_types(self): | ||||
|         cf = self.fromstring("[sect]\n" | ||||
|                              "option1=foo\n") | ||||
|         # Check that we get a TypeError when setting non-string values | ||||
|         # in an existing section: | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", 1) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", object()) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", 1) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", object()) | ||||
| 
 | ||||
|     def test_read_returns_file_list(self): | ||||
|         file1 = test_support.findfile("cfgparser.1") | ||||
|         # check when we pass a mix of readable and non-readable files: | ||||
|  | @ -344,6 +332,27 @@ def test_items(self): | |||
|                                  ('key', '|value|'), | ||||
|                                  ('name', 'value')]) | ||||
| 
 | ||||
|     def test_set_nonstring_types(self): | ||||
|         cf = self.newconfig() | ||||
|         cf.add_section('non-string') | ||||
|         cf.set('non-string', 'int', 1) | ||||
|         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%(']) | ||||
|         cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1, | ||||
|                                       '%(list)': '%(list)'}) | ||||
|         cf.set('non-string', 'string_with_interpolation', '%(list)s') | ||||
|         self.assertEqual(cf.get('non-string', 'int', raw=True), 1) | ||||
|         self.assertRaises(TypeError, cf.get, 'non-string', 'int') | ||||
|         self.assertEqual(cf.get('non-string', 'list', raw=True), | ||||
|                          [0, 1, 1, 2, 3, 5, 8, 13, '%(']) | ||||
|         self.assertRaises(TypeError, cf.get, 'non-string', 'list') | ||||
|         self.assertEqual(cf.get('non-string', 'dict', raw=True), | ||||
|                          {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'}) | ||||
|         self.assertRaises(TypeError, cf.get, 'non-string', 'dict') | ||||
|         self.assertEqual(cf.get('non-string', 'string_with_interpolation', | ||||
|                                 raw=True), '%(list)s') | ||||
|         self.assertRaises(ValueError, cf.get, 'non-string', | ||||
|                           'string_with_interpolation', raw=False) | ||||
| 
 | ||||
| 
 | ||||
| class RawConfigParserTestCase(TestCaseBase): | ||||
|     config_class = ConfigParser.RawConfigParser | ||||
|  | @ -368,6 +377,17 @@ def test_items(self): | |||
|                                  ('key', '|%(name)s|'), | ||||
|                                  ('name', 'value')]) | ||||
| 
 | ||||
|     def test_set_nonstring_types(self): | ||||
|         cf = self.newconfig() | ||||
|         cf.add_section('non-string') | ||||
|         cf.set('non-string', 'int', 1) | ||||
|         cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13]) | ||||
|         cf.set('non-string', 'dict', {'pi': 3.14159}) | ||||
|         self.assertEqual(cf.get('non-string', 'int'), 1) | ||||
|         self.assertEqual(cf.get('non-string', 'list'), | ||||
|                          [0, 1, 1, 2, 3, 5, 8, 13]) | ||||
|         self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159}) | ||||
|          | ||||
| 
 | ||||
| class SafeConfigParserTestCase(ConfigParserTestCase): | ||||
|     config_class = ConfigParser.SafeConfigParser | ||||
|  | @ -382,6 +402,18 @@ def test_safe_interpolation(self): | |||
|         self.assertEqual(cf.get("section", "ok"), "xxx/%s") | ||||
|         self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s") | ||||
| 
 | ||||
|     def test_set_nonstring_types(self): | ||||
|         cf = self.fromstring("[sect]\n" | ||||
|                              "option1=foo\n") | ||||
|         # Check that we get a TypeError when setting non-string values | ||||
|         # in an existing section: | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", 1) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option1", object()) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", 1) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0) | ||||
|         self.assertRaises(TypeError, cf.set, "sect", "option2", object()) | ||||
| 
 | ||||
| 
 | ||||
| def test_main(): | ||||
|     test_support.run_unittest( | ||||
|  |  | |||
|  | @ -99,6 +99,12 @@ Library | |||
|   consistent with the handling of config file entries and runtime-set | ||||
|   options. | ||||
| 
 | ||||
| - SF bug #997050: Document, test, & check for non-string values in | ||||
|   ConfigParser.  Moved the new string-only restriction added in | ||||
|   rev. 1.65 to the SafeConfigParser class, leaving existing | ||||
|   ConfigParser & RawConfigParser behavior alone, and documented the | ||||
|   conditions under which non-string values work. | ||||
| 
 | ||||
| Build | ||||
| ----- | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 David Goodger
						David Goodger