--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/Globals/E5ConfigParser.py Sun Apr 14 15:09:21 2019 +0200 @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2016 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a ConfigParser wrapper for Python 2 to provide the +dictionary like interface of the Python 3 variant. +""" + +from __future__ import unicode_literals + +try: + from configparser import ConfigParser as E5ConfigParser +except ImportError: + # Py2 part with the compatibility wrapper class + try: + from collections import OrderedDict as _default_dict + # __IGNORE_WARNING_N813__ + except ImportError: + # fallback for setup.py which hasn't yet built _collections + _default_dict = dict + + import re + import itertools + from ConfigParser import SafeConfigParser, DEFAULTSECT + + class E5ConfigParser(SafeConfigParser): + """ + Class implementing a wrapper of the ConfigParser class implementing + dictionary like special methods and some enhancements from Python 3. + """ + _OPT_TMPL = r""" + (?P<option>.*?) # very permissive! + \s*(?P<vi>{delim})\s* # any number of space/tab, + # followed by any of the + # allowed delimiters, + # followed by any space/tab + (?P<value>.*)$ # everything up to eol + """ + _OPT_NV_TMPL = r""" + (?P<option>.*?) # very permissive! + \s*(?: # any number of space/tab, + (?P<vi>{delim})\s* # optionally followed by + # any of the allowed + # delimiters, followed by any + # space/tab + (?P<value>.*))?$ # everything up to eol + """ + # Compiled regular expression for matching options with typical + # separators + OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE) + # Compiled regular expression for matching options with optional + # values delimited using typical separators + OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE) + + def __init__(self, defaults=None, dict_type=_default_dict, + allow_no_value=False, delimiters=('=', ':')): + """ + Constructor + """ + SafeConfigParser.__init__( + self, + defaults=defaults, dict_type=dict_type, + allow_no_value=allow_no_value) + + if delimiters == ('=', ':'): + self._optcre = \ + self.OPTCRE_NV if allow_no_value else self.OPTCRE + else: + d = "|".join(re.escape(d) for d in delimiters) + if allow_no_value: + self._optcre = re.compile( + self._OPT_NV_TMPL.format(delim=d), re.VERBOSE) + else: + self._optcre = re.compile( + self._OPT_TMPL.format(delim=d), re.VERBOSE) + + def __getitem__(self, key): + """ + Special method to get a section. + + @param key name of the section + @type str + @return section for the given key + @rtype dict + @exception KeyError raised if a non-existent key is given + """ + if key == DEFAULTSECT: + return self._defaults + elif self.has_section(key): + return self._sections[key] + else: + raise KeyError(key) + + def __setitem__(self, key, values): + """ + Special method to set the values of a section. + + @param key name of the section + @type str + @param values value for the section + @type dict + """ + # To conform with the mapping protocol, overwrites existing values + # in the section. + if key == DEFAULTSECT: + self._defaults.clear() + elif self.has_section(key): + self._sections[key].clear() + else: + self.add_section(key) + for subkey, value in values.items(): + subkey = self.optionxform(str(subkey)) + if value is not None: + value = str(value) + self.set(key, subkey, value) + + def __delitem__(self, key): + """ + Special method to delete a section. + + @param key name of the section + @type str + @exception ValueError raised to indicate non-removal of the + default section + @exception KeyError raised to indicate a non-existent section + """ + if key == DEFAULTSECT: + raise ValueError("Cannot remove the default section.") + if not self.has_section(key): + raise KeyError(key) + self.remove_section(key) + + def __contains__(self, key): + """ + Special method to test, if a section is contained in the config. + + @param key name of the section + @type str + @return flag indicating containment + @rtype bool + """ + return key == DEFAULTSECT or self.has_section(key) + + def __len__(self): + """ + Special method get the number of sections of the config. + + @return number of sections + @rtype int + """ + return len(self._sections) + 1 # the default section + + def __iter__(self): + """ + Special method to return an iterator of the section names starting + with the default section. + + @return iterator of the section names contained in the config + @rtype iterator of str + """ + return itertools.chain((DEFAULTSECT,), self._sections.keys()) + + +if __name__ == "__main__": + # This is some test code. + import sys + + c = E5ConfigParser() + c["DEFAULT"] = {'ServerAliveInterval': '45', + 'Compression': 'yes', + 'CompressionLevel': '9'} + c['bitbucket.org'] = {} + c['bitbucket.org']['User'] = 'hg' + c['topsecret.server.com'] = {} + topsecret = c['topsecret.server.com'] + topsecret['Port'] = '50022' + topsecret['ForwardX11'] = 'no' + c['DEFAULT']['ForwardX11'] = 'yes' + + c.write(sys.stdout)