Edgewall Software

Ticket #3425: orderedconfig.patch

File orderedconfig.patch, 6.0 KB (added by eblot, 2 years ago)

Ordered config parser (w/o the odict.py), for [3542]

  • trac/config.py

     
    1414# 
    1515# Author: Christopher Lenz <cmlenz@gmx.de> 
    1616 
    17 from ConfigParser import ConfigParser 
     17from ConfigParser import ConfigParser, DuplicateSectionError, DEFAULTSECT 
     18from odict import OrderedDict 
    1819import os 
    1920try: 
    2021    set 
     
    3132 
    3233_TRUE_VALUES = ('yes', 'true', 'on', 'aye', '1', 1, True) 
    3334 
     35class OrderedConfigParser(ConfigParser): 
     36    """ """ 
     37    def __init__(self, defaults=None): 
     38        """Override default constructor""" 
     39        self._sections = OrderedDict() 
     40        if defaults is None: 
     41            self._defaults = OrderedDict() 
     42        else: 
     43            self._defaults = defaults 
     44             
     45    def add_section(self, section): 
     46        """Override RawConfigParser method""" 
     47        if section in self._sections: 
     48            raise DuplicateSectionError(section) 
     49        self._sections[section] = OrderedDict() 
     50         
     51    def _read(self, fp, fpname): 
     52        """Override RawConfigParser method""" 
     53        cursect = None                            # None, or a dictionary 
     54        optname = None 
     55        lineno = 0 
     56        e = None                                  # None, or an exception 
     57        while True: 
     58            line = fp.readline() 
     59            if not line: 
     60                break 
     61            lineno = lineno + 1 
     62            # comment or blank line? 
     63            if line.strip() == '' or line[0] in '#;': 
     64                continue 
     65            if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": 
     66                # no leading whitespace 
     67                continue 
     68            # continuation line? 
     69            if line[0].isspace() and cursect is not None and optname: 
     70                value = line.strip() 
     71                if value: 
     72                    cursect[optname] = "%s\n%s" % (cursect[optname], value) 
     73            # a section header or option header? 
     74            else: 
     75                # is it a section header? 
     76                mo = self.SECTCRE.match(line) 
     77                if mo: 
     78                    sectname = mo.group('header') 
     79                    if sectname in self._sections: 
     80                        cursect = self._sections[sectname] 
     81                    elif sectname == DEFAULTSECT: 
     82                        cursect = self._defaults 
     83                    else: 
     84                        cursect = OrderedDict() 
     85                        cursect['__name__'] = sectname 
     86                        self._sections[sectname] = cursect 
     87                    # So sections can't start with a continuation line 
     88                    optname = None 
     89                # no section header in the file? 
     90                elif cursect is None: 
     91                    raise MissingSectionHeaderError(fpname, lineno, `line`) 
     92                # an option line? 
     93                else: 
     94                    mo = self.OPTCRE.match(line) 
     95                    if mo: 
     96                        optname, vi, optval = mo.group('option', 'vi', 'value') 
     97                        if vi in ('=', ':') and ';' in optval: 
     98                            # ';' is a comment delimiter only if it follows 
     99                            # a spacing character 
     100                            pos = optval.find(';') 
     101                            if pos != -1 and optval[pos-1].isspace(): 
     102                                optval = optval[:pos] 
     103                        optval = optval.strip() 
     104                        # allow empty values 
     105                        if optval == '""': 
     106                            optval = '' 
     107                        optname = self.optionxform(optname.rstrip()) 
     108                        cursect[optname] = optval 
     109                    else: 
     110                        # a non-fatal parsing error occurred.  set up the 
     111                        # exception but keep going. the exception will be 
     112                        # raised at the end of the file and will contain a 
     113                        # list of all bogus lines 
     114                        if not e: 
     115                            e = ParsingError(fpname) 
     116                        e.append(lineno, `line`) 
     117        # if any parsing errors occurred, raise an exception 
     118        if e: 
     119            raise e 
    34120 
    35121class ConfigurationError(TracError): 
    36122    """Exception raised when a value in the configuration file is not valid.""" 
     
    46132    def __init__(self, filename): 
    47133        self._sections = {} 
    48134        self.filename = filename 
    49         self.parser = ConfigParser() 
     135        self.parser = OrderedConfigParser() 
    50136        self._lastmtime = 0 
    51137        self.site_filename = os.path.join(default_dir('conf'), 'trac.ini') 
    52         self.site_parser = ConfigParser() 
     138        self.site_parser = OrderedConfigParser() 
    53139        self._lastsitemtime = 0 
    54140        self.parse_if_needed() 
    55141 
     
    142228            return 
    143229 
    144230        # Only save options that differ from the defaults 
    145         config = ConfigParser() 
     231        empty_sections = [] 
    146232        for section in self.sections(): 
    147233            for option in self[section]: 
    148234                default = self.site_parser.has_option(section, option) and \ 
    149235                          self.site_parser.get(section, option) 
    150236                current = self.parser.has_option(section, option) and \ 
    151237                          self.parser.get(section, option) 
    152                 if current is not False and current != default: 
    153                     if not config.has_section(section): 
    154                         config.add_section(section) 
    155                     config.set(section, option, current or '') 
     238                if default and current and current == default: 
     239                    self.parser.remove_option(section, option) 
     240            if not self.parser.options(section): 
     241                empty_sections.append(section) 
     242        if empty_sections: 
     243            map(self.parser.remove_section, empty_sections) 
    156244 
    157245        fileobj = file(self.filename, 'w') 
    158246        try: 
    159             config.write(fileobj) 
     247            self.parser.write(fileobj) 
    160248        finally: 
    161249            fileobj.close() 
    162250