diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index ba21117bcf8..e14068c8719 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -62,12 +62,16 @@ dictionary type is passed that sorts its keys, the sections will be sorted on write-back, as will be the keys within each section. -.. class:: RawConfigParser([defaults[, dict_type]]) +.. class:: RawConfigParser([defaults[, dict_type[, allow_no_value]]]) The basic configuration object. When *defaults* is given, it is initialized into the dictionary of intrinsic defaults. When *dict_type* is given, it will be used to create the dictionary objects for the list of sections, for the - options within a section, and for the default values. This class does not + options within a section, and for the default values. When *allow_no_value* + is true (default: ``False``), options without values are accepted; the value + presented for these is ``None``. + + This class does not support the magical interpolation behavior. .. versionadded:: 2.3 @@ -77,9 +81,10 @@ write-back, as will be the keys within each section. .. versionchanged:: 2.7 The default *dict_type* is :class:`collections.OrderedDict`. + *allow_no_value* was added. -.. class:: ConfigParser([defaults[, dict_type]]) +.. class:: ConfigParser([defaults[, dict_type[, allow_no_value]]]) Derived class of :class:`RawConfigParser` that implements the magical interpolation feature and adds optional arguments to the :meth:`get` and @@ -101,9 +106,10 @@ write-back, as will be the keys within each section. .. versionchanged:: 2.7 The default *dict_type* is :class:`collections.OrderedDict`. + *allow_no_value* was added. -.. class:: SafeConfigParser([defaults[, dict_type]]) +.. class:: SafeConfigParser([defaults[, dict_type[, allow_no_value]]]) Derived class of :class:`ConfigParser` that implements a more-sane variant of the magical interpolation feature. This implementation is more predictable as @@ -119,6 +125,7 @@ write-back, as will be the keys within each section. .. versionchanged:: 2.7 The default *dict_type* is :class:`collections.OrderedDict`. + *allow_no_value* was added. .. exception:: NoSectionError @@ -484,3 +491,38 @@ The function ``opt_move`` below can be used to move options between sections:: opt_move(config, section1, section2, option) else: config.remove_option(section1, option) + +Some configuration files are known to include settings without values, but which +otherwise conform to the syntax supported by :mod:`ConfigParser`. The +*allow_no_value* parameter to the constructor can be used to indicate that such +values should be accepted: + +.. doctest:: + + >>> import ConfigParser + >>> import io + + >>> sample_config = """ + ... [mysqld] + ... user = mysql + ... pid-file = /var/run/mysqld/mysqld.pid + ... skip-external-locking + ... old_passwords = 1 + ... skip-bdb + ... skip-innodb + ... """ + >>> config = ConfigParser.RawConfigParser(allow_no_value=True) + >>> config.readfp(io.BytesIO(sample_config)) + + >>> # Settings with values are treated as before: + >>> config.get("mysqld", "user") + 'mysql' + + >>> # Settings without values provide None: + >>> config.get("mysqld", "skip-bdb") + + >>> # Settings which aren't specified still raise an error: + >>> config.get("mysqld", "does-not-exist") + Traceback (most recent call last): + ... + ConfigParser.NoOptionError: No option 'does-not-exist' in section: 'mysqld' diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py index 14bfdb690f2..cee6b2aec5f 100644 --- a/Lib/ConfigParser.py +++ b/Lib/ConfigParser.py @@ -221,10 +221,15 @@ class MissingSectionHeaderError(ParsingError): class RawConfigParser: - def __init__(self, defaults=None, dict_type=_default_dict): + def __init__(self, defaults=None, dict_type=_default_dict, + allow_no_value=False): self._dict = dict_type self._sections = self._dict() self._defaults = self._dict() + if allow_no_value: + self._optcre = self.OPTCRE_NV + else: + self._optcre = self.OPTCRE if defaults: for key, value in defaults.items(): self._defaults[self.optionxform(key)] = value @@ -372,7 +377,7 @@ class RawConfigParser: return (option in self._sections[section] or option in self._defaults) - def set(self, section, option, value): + def set(self, section, option, value=None): """Set an option.""" if not section or section == DEFAULTSECT: sectdict = self._defaults @@ -394,8 +399,11 @@ class RawConfigParser: fp.write("[%s]\n" % section) for (key, value) in self._sections[section].items(): if key != "__name__": - fp.write("%s = %s\n" % - (key, str(value).replace('\n', '\n\t'))) + if value is None: + fp.write("%s\n" % (key)) + else: + fp.write("%s = %s\n" % + (key, str(value).replace('\n', '\n\t'))) fp.write("\n") def remove_option(self, section, option): @@ -436,6 +444,15 @@ class RawConfigParser: # by any # space/tab r'(?P.*)$' # everything up to eol ) + OPTCRE_NV = re.compile( + r'(?P