Files
@ f9b5deab451e
Branch filter:
Location: kallithea/kallithea/lib/inifile.py
f9b5deab451e
5.0 KiB
text/x-python
ini: add support for adding extra entries to sections
When adding entries to an existing section, assume that comments and empty
lines *before* a section header belongs to that section. That is an
approximation; not always entirely correct, but the best we can come up with.
When adding entries to an existing section, assume that comments and empty
lines *before* a section header belongs to that section. That is an
approximation; not always entirely correct, but the best we can come up with.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
kallithea.lib.inifile
~~~~~~~~~~~~~~~~~~~~~
Handling of .ini files, mainly creating them from Mako templates and adding
other custom values.
"""
import logging
import re
import mako.template
log = logging.getLogger(__name__)
def expand(template, desc, mako_variable_values, settings):
"""Expand mako template and tweak it.
Not entirely stable for random templates as input, but good enough for our
single template.
>>> template = '''
... [first-section]
...
... variable=${mako_variable}
... variable2 =\tvalue after tab
... ## This section had some whitespace and stuff
...
...
... # ${mako_function()}
... [second-section]
... %if conditional_options == 'option-a':
... # Kallithea - config file generated with kallithea-config #
... %elif conditional_options == 'option-b':
... some_variable = "never mind - option-b will not be used anyway ..."
... %endif
... '''
>>> desc = 'Description\\nof this config file'
>>> selected_mako_conditionals = []
>>> mako_variable_values = {'mako_variable': 'VALUE', 'mako_function': (lambda: 'FUNCTION RESULT'),
... 'conditional_options': 'option-a'}
>>> settings = { # only partially used
... '[first-section]': {'variable2': 'VAL2', 'first_extra': 'EXTRA'},
... '[third-section]': {'third_extra': ' 3'},
... '[fourth-section]': {'fourth_extra': '4', 'fourth': '"four"'},
... }
>>> print expand(template, desc, mako_variable_values, settings)
<BLANKLINE>
[first-section]
<BLANKLINE>
variable=VALUE
#variable2 = value after tab
variable2 = VAL2
<BLANKLINE>
first_extra = EXTRA
<BLANKLINE>
<BLANKLINE>
# FUNCTION RESULT
[second-section]
# Description #
# of this config file #
<BLANKLINE>
[fourth-section]
fourth = "four"
fourth_extra = 4
<BLANKLINE>
[third-section]
third_extra = 3
<BLANKLINE>
"""
settings = dict((k, dict(v)) for k, v in settings.items()) # deep copy before mutating
ini_lines = mako.template.Template(template).render(**mako_variable_values)
ini_lines = re.sub(
'# Kallithea - config file generated with kallithea-config *#\n',
''.join('# %-77s#\n' % l.strip() for l in desc.strip().split('\n')),
ini_lines)
def process_section(m):
"""process a ini section, replacing values as necessary"""
sectionname, lines = m.groups()
if sectionname in settings:
section_settings = settings.pop(sectionname)
def process_line(m):
"""process a section line and update value if necessary"""
key, value = m.groups()
line = m.group(0)
if key in section_settings:
# keep old entry as example - comments might refer to it
line = '#%s\n%s = %s' % (line, key, section_settings.pop(key))
return line.rstrip()
# process lines that not are comments or empty and look like name=value
lines = re.sub(r'^([^#\n\s]*)[ \t]*=[ \t]*(.*)$', process_line, lines, flags=re.MULTILINE)
# add unused section settings
if section_settings:
lines += '\n' + ''.join('%s = %s\n' % (key, value) for key, value in sorted(section_settings.items()))
return sectionname + '\n' + lines
# process sections until comments before next section or end
ini_lines = re.sub(r'''^
(\[.*\])\n
# after the section name, a number of chunks with:
(
(?:
# a number of comments or empty lines
(?:[#].*\n|\n)*
# one or more non-empty non-comments non-section-start lines
(?:[^\n#[].*\n)+
# a number of comments - not empty lines
(?:[#].*\n)*
)*
)
''',
process_section, ini_lines, flags=re.MULTILINE|re.VERBOSE) \
+ \
''.join(
'\n' + sectionname + '\n' + ''.join('%s = %s\n' % (key, value) for key, value in sorted(section_settings.items()))
for sectionname, section_settings in sorted(settings.items())
if section_settings)
return ini_lines
|