123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- from collections import OrderedDict, namedtuple
- try:
- # Python 2
- from ConfigParser import RawConfigParser
- except ImportError:
- # Python 3
- from configparser import RawConfigParser
- Section_format = namedtuple('Section_format', ('name', 'options'))
- Config_option = namedtuple('Config_option', ('name', 'value_type', 'required'))
- def option(name, value_type=str, required=True):
- '''
- Given a config file option name, an expected type for its value, and whether it's required,
- return a Config_option capturing that information.
- '''
- return Config_option(name, value_type, required)
- def validate_configuration_format(parser, config_format):
- '''
- Given an open RawConfigParser and an expected config file format, validate that the parsed
- configuration file has the expected sections, that any required options are present in those
- sections, and that there aren't any unexpected options.
- A section is required if any of its contained options are required.
- Raise ValueError if anything is awry.
- '''
- section_names = set(parser.sections())
- required_section_names = tuple(
- section.name for section in config_format
- if any(option.required for option in section.options)
- )
- unknown_section_names = section_names - set(
- section_format.name for section_format in config_format
- )
- if unknown_section_names:
- raise ValueError(
- 'Unknown config sections found: {}'.format(', '.join(unknown_section_names))
- )
- missing_section_names = set(required_section_names) - section_names
- if missing_section_names:
- raise ValueError(
- 'Missing config sections: {}'.format(', '.join(missing_section_names))
- )
- for section_format in config_format:
- if section_format.name not in section_names:
- continue
- option_names = parser.options(section_format.name)
- expected_options = section_format.options
- unexpected_option_names = set(option_names) - set(option.name for option in expected_options)
- if unexpected_option_names:
- raise ValueError(
- 'Unexpected options found in config section {}: {}'.format(
- section_format.name,
- ', '.join(sorted(unexpected_option_names)),
- )
- )
- missing_option_names = tuple(
- option.name for option in expected_options if option.required
- if option.name not in option_names
- )
- if missing_option_names:
- raise ValueError(
- 'Required options missing from config section {}: {}'.format(
- section_format.name,
- ', '.join(missing_option_names)
- )
- )
- def parse_section_options(parser, section_format):
- '''
- Given an open RawConfigParser and an expected section format, return the option values from that
- section as a dict mapping from option name to value. Omit those options that are not present in
- the parsed options.
- Raise ValueError if any option values cannot be coerced to the expected Python data type.
- '''
- type_getter = {
- str: parser.get,
- int: parser.getint,
- }
- return OrderedDict(
- (option.name, type_getter[option.value_type](section_format.name, option.name))
- for option in section_format.options
- if parser.has_option(section_format.name, option.name)
- )
- def parse_configuration(config_filename, config_format):
- '''
- Given a config filename and an expected config file format, return the parsed configuration
- as a namedtuple with one attribute for each parsed section.
- Raise IOError if the file cannot be read, or ValueError if the format is not as expected.
- '''
- parser = RawConfigParser()
- parser.read(config_filename)
- validate_configuration_format(parser, config_format)
- # Describes a parsed configuration, where each attribute is the name of a configuration file
- # section and each value is a dict of that section's parsed options.
- Parsed_config = namedtuple('Parsed_config', (section_format.name for section_format in config_format))
- return Parsed_config(
- *(
- parse_section_options(parser, section_format)
- for section_format in config_format
- )
- )
|