It's 2012 all up in here
[mediagoblin.git] / mediagoblin / init / config.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import pkg_resources
19
20 from configobj import ConfigObj, flatten_errors
21 from validate import Validator
22
23
24 CONFIG_SPEC_PATH = pkg_resources.resource_filename(
25 'mediagoblin', 'config_spec.ini')
26
27
28 def _setup_defaults(config, config_path):
29 """
30 Setup DEFAULTS in a config object from an (absolute) config_path.
31 """
32 config.setdefault('DEFAULT', {})
33 config['DEFAULT']['here'] = os.path.dirname(config_path)
34 config['DEFAULT']['__file__'] = config_path
35
36
37 def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
38 """
39 Read a config object from config_path.
40
41 Does automatic value transformation based on the config_spec.
42 Also provides %(__file__)s and %(here)s values of this file and
43 its directory respectively similar to paste deploy.
44
45 This function doesn't itself raise any exceptions if validation
46 fails, you'll have to do something
47
48 Args:
49 - config_path: path to the config file
50 - config_spec: config file that provides defaults and value types
51 for validation / conversion. Defaults to mediagoblin/config_spec.ini
52
53 Returns:
54 A tuple like: (config, validation_result)
55 ... where 'conf' is the parsed config object and 'validation_result'
56 is the information from the validation process.
57 """
58 config_path = os.path.abspath(config_path)
59
60 config_spec = ConfigObj(
61 config_spec,
62 encoding='UTF8', list_values=False, _inspec=True)
63
64 _setup_defaults(config_spec, config_path)
65
66 config = ConfigObj(
67 config_path,
68 configspec=config_spec,
69 interpolation='ConfigParser')
70
71 _setup_defaults(config, config_path)
72
73 # For now the validator just works with the default functions,
74 # but in the future if we want to add additional validation/configuration
75 # functions we'd add them to validator.functions here.
76 #
77 # See also:
78 # http://www.voidspace.org.uk/python/validate.html#adding-functions
79 validator = Validator()
80 validation_result = config.validate(validator, preserve_errors=True)
81
82 return config, validation_result
83
84
85 REPORT_HEADER = u"""\
86 There were validation problems loading this config file:
87 --------------------------------------------------------
88 """
89
90
91 def generate_validation_report(config, validation_result):
92 """
93 Generate a report if necessary of problems while validating.
94
95 Returns:
96 Either a string describing for a user the problems validating
97 this config or None if there are no problems.
98 """
99 report = []
100
101 # Organize the report
102 for entry in flatten_errors(config, validation_result):
103 # each entry is a tuple
104 section_list, key, error = entry
105
106 if key is not None:
107 section_list.append(key)
108 else:
109 section_list.append(u'[missing section]')
110
111 section_string = u':'.join(section_list)
112
113 if error == False:
114 # We don't care about missing values for now.
115 continue
116
117 report.append(u"%s = %s" % (section_string, error))
118
119 if report:
120 return REPORT_HEADER + u"\n".join(report)
121 else:
122 return None