Files
@ a75f3e12583a
Branch filter:
Location: kallithea/kallithea/lib/inifile.py
a75f3e12583a
5.9 KiB
text/x-python
config-create: report when database_engine or http_server are provided with invalid values
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | # -*- 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 os
import re
import mako.template
log = logging.getLogger(__name__)
template_file = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
'kallithea/lib/paster_commands/template.ini.mako')
default_variables = {
'database_engine': 'sqlite',
'http_server': 'waitress',
'host': '127.0.0.1',
'port': '5000',
'uuid': lambda: 'VERY-SECRET',
}
variable_options = {
'database_engine': ['sqlite', 'postgres', 'mysql'],
'http_server': ['waitress', 'gearbox', 'gevent', 'gunicorn', 'uwsgi'],
}
def expand(template, 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':
... # option a was chosen
... %elif conditional_options == 'option-b':
... some_variable = "never mind - option-b will not be used anyway ..."
... %endif
... '''
>>> mako_variable_values = {'mako_variable': 'VALUE', 'mako_function': (lambda: 'FUNCTION RESULT'),
... 'conditional_options': 'option-a', 'http_server': 'nc'}
>>> 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, mako_variable_values, settings))
ERROR: http_server is 'nc' - it should be one of 'waitress', 'gearbox', 'gevent', 'gunicorn', 'uwsgi'
<BLANKLINE>
[first-section]
<BLANKLINE>
variable=VALUE
#variable2 = value after tab
variable2 = VAL2
<BLANKLINE>
first_extra = EXTRA
<BLANKLINE>
<BLANKLINE>
# FUNCTION RESULT
[second-section]
# option a was chosen
<BLANKLINE>
[fourth-section]
fourth = "four"
fourth_extra = 4
<BLANKLINE>
[third-section]
third_extra = 3
<BLANKLINE>
"""
mako_variables = dict(default_variables)
mako_variables.update(mako_variable_values or {})
settings = dict((k, dict(v)) for k, v in settings.items()) # deep copy before mutating
for key, value in mako_variables.items():
if key in variable_options:
if value not in variable_options[key]:
print('ERROR: %s is %r - it should be one of %s' %
(key, value, ', '.join(repr(x) for x in variable_options[key])))
ini_lines = mako.template.Template(template).render(**mako_variables)
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:
new_line = '%s = %s' % (key, section_settings.pop(key))
if new_line != line:
# keep old entry as example - comments might refer to it
line = '#%s\n%s' % (line, new_line)
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
def create(dest_file, mako_variable_values, settings):
"""Create an ini file at dest_file"""
with open(template_file, 'rb') as f:
template = f.read().decode('utf-8')
ini_lines = expand(template, mako_variable_values, settings)
with open(dest_file, 'wb') as f:
f.write(ini_lines.encode('utf-8'))
|