Changeset - 94f6b23e52d0
[Not reviewed]
default
0 2 0
Mads Kiilerich - 8 years ago 2017-09-14 02:08:07
mads@kiilerich.com
ini: move high level functionality and defaults to inifiles library
2 files changed with 31 insertions and 14 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/inifile.py
Show inline comments
 
# -*- 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 os
 

	
 
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',
 
}
 

	
 

	
 
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
 
    ... '''
 
    >>> selected_mako_conditionals = []
 
    >>> mako_variable_values = {'mako_variable': 'VALUE', 'mako_function': (lambda: 'FUNCTION RESULT'),
 
    ...                         'conditional_options': 'option-a'}
 
@@ -60,74 +74,87 @@ def expand(template, mako_variable_value
 
    ... }
 
    >>> print expand(template, 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]
 
    # 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
 

	
 
    ini_lines = mako.template.Template(template).render(**mako_variable_values)
 
    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:
 
                    # 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
 

	
 

	
 
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'))
scripts/generate-ini.py
Show inline comments
 
#!/usr/bin/env python2
 
"""
 
Based on kallithea/lib/paster_commands/template.ini.mako, generate
 
  development.ini
 
  kallithea/tests/test.ini
 
"""
 

	
 
import re
 

	
 
from kallithea.lib import inifile
 

	
 
makofile = 'kallithea/lib/paster_commands/template.ini.mako'
 

	
 
# the mako variables used in all other ini files and templates
 
mako_variable_values = {
 
    'database_engine': 'sqlite',
 
    'http_server': 'waitress',
 
    'host': '127.0.0.1',
 
    'port': '5000',
 
    'uuid': lambda: 'VERY-SECRET',
 
}
 

	
 
# files to be generated from the mako template
 
ini_files = [
 
    ('kallithea/tests/test.ini',
 
        {
 
            '[server:main]': {
 
                'port': '4999',
 
            },
 
            '[app:main]': {
 
                'app_instance_uuid': 'test',
 
                'show_revision_number': 'true',
 
                'beaker.cache.sql_cache_short.expire': '1',
 
                'beaker.session.secret': '{74e0cd75-b339-478b-b129-07dd221def1f}',
 
            },
 
            '[handler_console]': {
 
                'formatter': 'color_formatter',
 
            },
 
            # The 'handler_console_sql' block is very similar to the one in
 
            # development.ini, but without the explicit 'level=DEBUG' setting:
 
            # it causes duplicate sqlalchemy debug logs, one through
 
            # handler_console_sql and another through another path.
 
            '[handler_console_sql]': {
 
                'formatter': 'color_formatter_sql',
 
            },
 
        },
 
@@ -48,40 +37,41 @@ ini_files = [
 
    ('development.ini',
 
        {
 
            '[server:main]': {
 
                'host': '0.0.0.0',
 
            },
 
            '[app:main]': {
 
                'initial_repo_scan': 'true',
 
                'debug': 'true',
 
                'app_instance_uuid': 'development-not-secret',
 
                'beaker.session.secret': 'development-not-secret',
 
            },
 
            '[handler_console]': {
 
                'formatter': 'color_formatter',
 
            },
 
            '[handler_console_sql]': {
 
                'formatter': 'color_formatter_sql',
 
            },
 
        },
 
    ),
 
]
 

	
 

	
 
def main():
 
    # make sure all mako lines starting with '#' (the '##' comments) are marked up as <text>
 
    makofile = inifile.template_file
 
    print 'reading:', makofile
 
    mako_org = open(makofile).read()
 
    mako_no_text_markup = re.sub(r'</?%text>', '', mako_org)
 
    mako_marked_up = re.sub(r'\n(##.*)', r'\n<%text>\1</%text>', mako_no_text_markup, flags=re.MULTILINE)
 
    if mako_marked_up != mako_org:
 
        print 'writing:', makofile
 
        open(makofile, 'w').write(mako_marked_up)
 

	
 
    # create ini files
 
    for fn, settings in ini_files:
 
        print 'updating:', fn
 
        ini_lines = inifile.expand(mako_marked_up, mako_variable_values, settings)
 
        open(fn, 'w').write(ini_lines)
 
        inifile.create(fn, None, settings)
 

	
 

	
 
if __name__ == '__main__':
 
    main()
0 comments (0 inline, 0 general)