|
@@ -2,75 +2,127 @@
|
|
|
|
|
|
import configparser
|
|
import configparser
|
|
|
|
|
|
-# contains all configuration sections and subsections
|
|
|
|
-# the subsections are a tuple with their name and a boolean, which
|
|
|
|
-# tells us whether the option is mandatory
|
|
|
|
-VALID_CONFIG_SECTIONS = {
|
|
|
|
- 'Application': [
|
|
|
|
- ('name', True),
|
|
|
|
- ('version', True),
|
|
|
|
- ('entry_point', True),
|
|
|
|
- ('script', False),
|
|
|
|
- ('icon', False),
|
|
|
|
- ('console', False),
|
|
|
|
- ],
|
|
|
|
- 'Build': [
|
|
|
|
- ('directory', False),
|
|
|
|
- ('installer_name', False),
|
|
|
|
- ('nsi_template', False),
|
|
|
|
- ],
|
|
|
|
- 'Include': [
|
|
|
|
- ('packages', False),
|
|
|
|
- ('files', False),
|
|
|
|
- ],
|
|
|
|
- 'Python': [
|
|
|
|
- ('version', True),
|
|
|
|
- ('bitness', False),
|
|
|
|
- ],
|
|
|
|
-}
|
|
|
|
|
|
+class SectionValidator(object):
|
|
|
|
+ def __init__(self, name, subsections, identification_func=None):
|
|
|
|
+ """
|
|
|
|
+ name
|
|
|
|
+ a descriptive name of the section
|
|
|
|
+ subsections
|
|
|
|
+ list of tuples containing the names and whether the
|
|
|
|
+ subsection is mandatory
|
|
|
|
+ identy_func
|
|
|
|
+ returns True if this is the correct Validator for the
|
|
|
|
+ section name. If it is left out the descriptive name will be
|
|
|
|
+ used to identify the section
|
|
|
|
+ """
|
|
|
|
+ self.name = name
|
|
|
|
+ self.identification_func = identification_func
|
|
|
|
+ self.subsections = subsections
|
|
|
|
+
|
|
|
|
+ def check(self, config, section_name):
|
|
|
|
+ """
|
|
|
|
+ validates the section, if this is the correct validator for it
|
|
|
|
+ returns True if this is the correct validator for this section
|
|
|
|
+
|
|
|
|
+ raises InvalidConfig if something inside the section is wrong
|
|
|
|
+ """
|
|
|
|
+ was_identified = False
|
|
|
|
+ if self._identify(section_name):
|
|
|
|
+ was_identified = True
|
|
|
|
+ self._check_mandatory_fields(section_name, config[section_name])
|
|
|
|
+ self._check_invalid_subsections(section_name, config[section_name])
|
|
|
|
+ return was_identified
|
|
|
|
|
|
-class InvalidConfig(ValueError):
|
|
|
|
- pass
|
|
|
|
|
|
+ def _identify(self, section_name):
|
|
|
|
+ # if no identification function is set, just compare the section
|
|
|
|
+ # name with the name of the validator
|
|
|
|
+ if self.identification_func is None:
|
|
|
|
+ return section_name == self.name
|
|
|
|
+ else:
|
|
|
|
+ return self.identification_func(section_name)
|
|
|
|
|
|
-def read_and_validate(config_file):
|
|
|
|
- cfg = configparser.ConfigParser()
|
|
|
|
- cfg.read(config_file)
|
|
|
|
- # check mandatory sections
|
|
|
|
- for section_name, subsection_list in VALID_CONFIG_SECTIONS.items():
|
|
|
|
- for subsection_name, mandatory in subsection_list:
|
|
|
|
|
|
+ def _check_mandatory_fields(self, section_name, subsection):
|
|
|
|
+ for subsection_name, mandatory in self.subsections:
|
|
if mandatory:
|
|
if mandatory:
|
|
try:
|
|
try:
|
|
- cfg[section_name][subsection_name]
|
|
|
|
|
|
+ subsection[subsection_name]
|
|
except KeyError:
|
|
except KeyError:
|
|
err_msg = ("The section '{0}' must contain a "
|
|
err_msg = ("The section '{0}' must contain a "
|
|
"subsection '{1}'!").format(
|
|
"subsection '{1}'!").format(
|
|
section_name,
|
|
section_name,
|
|
subsection_name)
|
|
subsection_name)
|
|
raise InvalidConfig(err_msg)
|
|
raise InvalidConfig(err_msg)
|
|
-
|
|
|
|
- # check for invalid sections and subsections
|
|
|
|
- for section in cfg:
|
|
|
|
- # check section names
|
|
|
|
- section_name = str(section)
|
|
|
|
- is_valid_section_name = section_name in VALID_CONFIG_SECTIONS.keys()
|
|
|
|
- if section_name == 'DEFAULT':
|
|
|
|
- # DEFAULT is always inside the config, so just jump over it
|
|
|
|
- continue
|
|
|
|
- if not is_valid_section_name:
|
|
|
|
- err_msg = ("{0} is not a valid section header. Must "
|
|
|
|
- "be one of these: {1}").format(
|
|
|
|
- section_name, ', '.join(valid_section_headers))
|
|
|
|
- raise InvalidConfig(err_msg)
|
|
|
|
|
|
+
|
|
|
|
+ def _check_invalid_subsections(self, section_name, section):
|
|
# check subsection names
|
|
# check subsection names
|
|
- for subsection in cfg[section_name]:
|
|
|
|
|
|
+ for subsection in section:
|
|
subsection_name = str(subsection)
|
|
subsection_name = str(subsection)
|
|
- subsection_names = [s[0] for s in VALID_CONFIG_SECTIONS[section_name]]
|
|
|
|
- is_valid_subsection = subsection_name in subsection_names
|
|
|
|
|
|
+ valid_subsection_names = [s[0] for s in self.subsections]
|
|
|
|
+ is_valid_subsection = subsection_name in valid_subsection_names
|
|
if not is_valid_subsection:
|
|
if not is_valid_subsection:
|
|
err_msg = ("'{0}' is not a valid subsection name for '{1}'. Must "
|
|
err_msg = ("'{0}' is not a valid subsection name for '{1}'. Must "
|
|
"be one of these: {2}").format(
|
|
"be one of these: {2}").format(
|
|
subsection_name,
|
|
subsection_name,
|
|
section_name,
|
|
section_name,
|
|
- ', '.join(subsection_names))
|
|
|
|
|
|
+ ', '.join(valid_subsection_names))
|
|
raise InvalidConfig(err_msg)
|
|
raise InvalidConfig(err_msg)
|
|
- return cfg
|
|
|
|
|
|
+
|
|
|
|
+# contains all configuration sections and subsections
|
|
|
|
+# the subsections are a tuple with their name and a boolean, which
|
|
|
|
+# tells us whether the option is mandatory
|
|
|
|
+CONFIG_VALIDATORS = [
|
|
|
|
+ SectionValidator('Application',
|
|
|
|
+ [
|
|
|
|
+ ('name', True),
|
|
|
|
+ ('version', True),
|
|
|
|
+ ('entry_point', True),
|
|
|
|
+ ('script', False),
|
|
|
|
+ ('icon', False),
|
|
|
|
+ ('console', False),
|
|
|
|
+ ],
|
|
|
|
+ ),
|
|
|
|
+ SectionValidator('Build',
|
|
|
|
+ [
|
|
|
|
+ ('directory', False),
|
|
|
|
+ ('installer_name', False),
|
|
|
|
+ ('nsi_template', False),
|
|
|
|
+ ]
|
|
|
|
+ ),
|
|
|
|
+ SectionValidator('Include',
|
|
|
|
+ [
|
|
|
|
+ ('packages', False),
|
|
|
|
+ ('files', False),
|
|
|
|
+ ]
|
|
|
|
+ ),
|
|
|
|
+ SectionValidator('Python',
|
|
|
|
+ [
|
|
|
|
+ ('version', True),
|
|
|
|
+ ('bitness', False),
|
|
|
|
+ ]
|
|
|
|
+ ),
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+class InvalidConfig(ValueError):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+def read_and_validate(config_file):
|
|
|
|
+ config = configparser.ConfigParser()
|
|
|
|
+ config.read(config_file)
|
|
|
|
+ for section in config.sections():
|
|
|
|
+ # each section *must* be identified by at least one validator
|
|
|
|
+ # otherwise it is an invalid section (e.g. has an invalid name)
|
|
|
|
+ was_identified = False
|
|
|
|
+ for validator in CONFIG_VALIDATORS:
|
|
|
|
+ was_identified = validator.check(config, section)
|
|
|
|
+ # if we already found the correct validator and it did not
|
|
|
|
+ # raise InvalidConfig, we can skip the other validators
|
|
|
|
+ if was_identified:
|
|
|
|
+ break
|
|
|
|
+ if not was_identified:
|
|
|
|
+ valid_section_names = [v.name for v in CONFIG_VALIDATORS]
|
|
|
|
+ err_msg = ("{0} is not a valid section header. Must "
|
|
|
|
+ "be one of these: {1}").format(
|
|
|
|
+ section,
|
|
|
|
+ ', '.join(['"%s"' % n for n in valid_section_names]))
|
|
|
|
+ raise InvalidConfig(err_msg)
|
|
|
|
+ return config
|