Show More
@@ -1,160 +1,162 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | # This program is free software: you can redistribute it and/or modify |
|
3 | 3 | # it under the terms of the GNU General Public License as published by |
|
4 | 4 | # the Free Software Foundation, either version 3 of the License, or |
|
5 | 5 | # (at your option) any later version. |
|
6 | 6 | # |
|
7 | 7 | # This program is distributed in the hope that it will be useful, |
|
8 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | # GNU General Public License for more details. |
|
11 | 11 | # |
|
12 | 12 | # You should have received a copy of the GNU General Public License |
|
13 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | |
|
15 | 15 | """ |
|
16 | 16 | kallithea.lib.inifile |
|
17 | 17 | ~~~~~~~~~~~~~~~~~~~~~ |
|
18 | 18 | |
|
19 | 19 | Handling of .ini files, mainly creating them from Mako templates and adding |
|
20 | 20 | other custom values. |
|
21 | 21 | """ |
|
22 | 22 | |
|
23 | 23 | import logging |
|
24 | 24 | import re |
|
25 | 25 | import os |
|
26 | 26 | |
|
27 | 27 | import mako.template |
|
28 | 28 | |
|
29 | 29 | |
|
30 | 30 | log = logging.getLogger(__name__) |
|
31 | 31 | |
|
32 | 32 | |
|
33 | 33 | template_file = os.path.join( |
|
34 | 34 | os.path.dirname(os.path.dirname(os.path.dirname(__file__))), |
|
35 | 35 | 'kallithea/lib/paster_commands/template.ini.mako') |
|
36 | 36 | |
|
37 | 37 | default_variables = { |
|
38 | 38 | 'database_engine': 'sqlite', |
|
39 | 39 | 'http_server': 'waitress', |
|
40 | 40 | 'host': '127.0.0.1', |
|
41 | 41 | 'port': '5000', |
|
42 | 42 | 'uuid': lambda: 'VERY-SECRET', |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | def expand(template, mako_variable_values, settings): |
|
47 | 47 | """Expand mako template and tweak it. |
|
48 | 48 | Not entirely stable for random templates as input, but good enough for our |
|
49 | 49 | single template. |
|
50 | 50 | |
|
51 | 51 | >>> template = ''' |
|
52 | 52 | ... [first-section] |
|
53 | 53 | ... |
|
54 | 54 | ... variable=${mako_variable} |
|
55 | 55 | ... variable2 =\tvalue after tab |
|
56 | 56 | ... ## This section had some whitespace and stuff |
|
57 | 57 | ... |
|
58 | 58 | ... |
|
59 | 59 | ... # ${mako_function()} |
|
60 | 60 | ... [second-section] |
|
61 | 61 | ... %if conditional_options == 'option-a': |
|
62 | 62 | ... # option a was chosen |
|
63 | 63 | ... %elif conditional_options == 'option-b': |
|
64 | 64 | ... some_variable = "never mind - option-b will not be used anyway ..." |
|
65 | 65 | ... %endif |
|
66 | 66 | ... ''' |
|
67 | 67 | >>> selected_mako_conditionals = [] |
|
68 | 68 | >>> mako_variable_values = {'mako_variable': 'VALUE', 'mako_function': (lambda: 'FUNCTION RESULT'), |
|
69 | 69 | ... 'conditional_options': 'option-a'} |
|
70 | 70 | >>> settings = { # only partially used |
|
71 | 71 | ... '[first-section]': {'variable2': 'VAL2', 'first_extra': 'EXTRA'}, |
|
72 | 72 | ... '[third-section]': {'third_extra': ' 3'}, |
|
73 | 73 | ... '[fourth-section]': {'fourth_extra': '4', 'fourth': '"four"'}, |
|
74 | 74 | ... } |
|
75 | 75 | >>> print expand(template, mako_variable_values, settings) |
|
76 | 76 | <BLANKLINE> |
|
77 | 77 | [first-section] |
|
78 | 78 | <BLANKLINE> |
|
79 | 79 | variable=VALUE |
|
80 | 80 | #variable2 = value after tab |
|
81 | 81 | variable2 = VAL2 |
|
82 | 82 | <BLANKLINE> |
|
83 | 83 | first_extra = EXTRA |
|
84 | 84 | <BLANKLINE> |
|
85 | 85 | <BLANKLINE> |
|
86 | 86 | # FUNCTION RESULT |
|
87 | 87 | [second-section] |
|
88 | 88 | # option a was chosen |
|
89 | 89 | <BLANKLINE> |
|
90 | 90 | [fourth-section] |
|
91 | 91 | fourth = "four" |
|
92 | 92 | fourth_extra = 4 |
|
93 | 93 | <BLANKLINE> |
|
94 | 94 | [third-section] |
|
95 | 95 | third_extra = 3 |
|
96 | 96 | <BLANKLINE> |
|
97 | 97 | """ |
|
98 | 98 | mako_variables = dict(default_variables) |
|
99 | 99 | mako_variables.update(mako_variable_values or {}) |
|
100 | 100 | settings = dict((k, dict(v)) for k, v in settings.items()) # deep copy before mutating |
|
101 | 101 | |
|
102 | 102 | ini_lines = mako.template.Template(template).render(**mako_variables) |
|
103 | 103 | |
|
104 | 104 | def process_section(m): |
|
105 | 105 | """process a ini section, replacing values as necessary""" |
|
106 | 106 | sectionname, lines = m.groups() |
|
107 | 107 | if sectionname in settings: |
|
108 | 108 | section_settings = settings.pop(sectionname) |
|
109 | 109 | |
|
110 | 110 | def process_line(m): |
|
111 | 111 | """process a section line and update value if necessary""" |
|
112 | 112 | key, value = m.groups() |
|
113 | 113 | line = m.group(0) |
|
114 | 114 | if key in section_settings: |
|
115 | new_line = '%s = %s' % (key, section_settings.pop(key)) | |
|
116 | if new_line != line: | |
|
115 | 117 | # keep old entry as example - comments might refer to it |
|
116 |
line = '#%s\n%s |
|
|
118 | line = '#%s\n%s' % (line, new_line) | |
|
117 | 119 | return line.rstrip() |
|
118 | 120 | |
|
119 | 121 | # process lines that not are comments or empty and look like name=value |
|
120 | 122 | lines = re.sub(r'^([^#\n\s]*)[ \t]*=[ \t]*(.*)$', process_line, lines, flags=re.MULTILINE) |
|
121 | 123 | # add unused section settings |
|
122 | 124 | if section_settings: |
|
123 | 125 | lines += '\n' + ''.join('%s = %s\n' % (key, value) for key, value in sorted(section_settings.items())) |
|
124 | 126 | |
|
125 | 127 | return sectionname + '\n' + lines |
|
126 | 128 | |
|
127 | 129 | # process sections until comments before next section or end |
|
128 | 130 | ini_lines = re.sub(r'''^ |
|
129 | 131 | (\[.*\])\n |
|
130 | 132 | # after the section name, a number of chunks with: |
|
131 | 133 | ( |
|
132 | 134 | (?: |
|
133 | 135 | # a number of comments or empty lines |
|
134 | 136 | (?:[#].*\n|\n)* |
|
135 | 137 | # one or more non-empty non-comments non-section-start lines |
|
136 | 138 | (?:[^\n#[].*\n)+ |
|
137 | 139 | # a number of comments - not empty lines |
|
138 | 140 | (?:[#].*\n)* |
|
139 | 141 | )* |
|
140 | 142 | ) |
|
141 | 143 | ''', |
|
142 | 144 | process_section, ini_lines, flags=re.MULTILINE|re.VERBOSE) \ |
|
143 | 145 | + \ |
|
144 | 146 | ''.join( |
|
145 | 147 | '\n' + sectionname + '\n' + ''.join('%s = %s\n' % (key, value) for key, value in sorted(section_settings.items())) |
|
146 | 148 | for sectionname, section_settings in sorted(settings.items()) |
|
147 | 149 | if section_settings) |
|
148 | 150 | |
|
149 | 151 | return ini_lines |
|
150 | 152 | |
|
151 | 153 | |
|
152 | 154 | def create(dest_file, mako_variable_values, settings): |
|
153 | 155 | """Create an ini file at dest_file""" |
|
154 | 156 | with open(template_file, 'rb') as f: |
|
155 | 157 | template = f.read().decode('utf-8') |
|
156 | 158 | |
|
157 | 159 | ini_lines = expand(template, mako_variable_values, settings) |
|
158 | 160 | |
|
159 | 161 | with open(dest_file, 'wb') as f: |
|
160 | 162 | f.write(ini_lines.encode('utf-8')) |
General Comments 0
You need to be logged in to leave comments.
Login now