Skip to content

Config

odin_control.config.parser - configuration parsing for the ODIN server.

Tim Nicholls, STFC Application Engineering Group

AdapterConfig

Bases: object

An adapter configuration container class.

A simple container class used by ConfigParser to define the options for an API adapter. This class contains at least the name and module path for the adapter, plus any other options can be set as attributes using the set() method

Source code in src/odin_control/config/parser.py
class AdapterConfig(object):
    """An adapter configuration container class.

    A simple container class used by ConfigParser to define the options for an API adapter. This
    class contains at least the name and module path for the adapter, plus any other options can
    be set as attributes using the set() method
    """

    def __init__(self, name, module):
        """Initialise the AdapterConfig object.

        :param name: name of the adapter
        :param module: module path for the adapter
        """
        self.name = name
        self.module = module
        self._options = {}

    def set(self, option, value):
        """Set an option for the adapter configuration object.

        :param option: name of the option
        :param value: value of the option
        :return: None
        """
        self._options[option] = value

    def __contains__(self, option):
        """Containment check operator - allows ``in`` to check for presence of option.

        :param option: name of option to check for
        :return: bool True or False indicating presence of option
        """
        return option in self._options

    def __getattr__(self, option):
        """Attribute getter allowing adapter options to be accessed by object.option syntax.

        :param option: name option to get
        :return: value of the requested option
        :raises AttributeError: raised for unrecognised option
        """
        if option not in self._options:
            raise AttributeError('Unrecognised option {}'.format(option))

        return self._options[option]

    def options(self):
        """Return a dictionary of the options set in the object.

        :return: dictionary of options
        """
        return self._options

__contains__(option)

Containment check operator - allows in to check for presence of option.

Parameters:

Name Type Description Default
option

name of option to check for

required

Returns:

Type Description

bool True or False indicating presence of option

Source code in src/odin_control/config/parser.py
def __contains__(self, option):
    """Containment check operator - allows ``in`` to check for presence of option.

    :param option: name of option to check for
    :return: bool True or False indicating presence of option
    """
    return option in self._options

__getattr__(option)

Attribute getter allowing adapter options to be accessed by object.option syntax.

Parameters:

Name Type Description Default
option

name option to get

required

Returns:

Type Description

value of the requested option

Raises:

Type Description
AttributeError

raised for unrecognised option

Source code in src/odin_control/config/parser.py
def __getattr__(self, option):
    """Attribute getter allowing adapter options to be accessed by object.option syntax.

    :param option: name option to get
    :return: value of the requested option
    :raises AttributeError: raised for unrecognised option
    """
    if option not in self._options:
        raise AttributeError('Unrecognised option {}'.format(option))

    return self._options[option]

__init__(name, module)

Initialise the AdapterConfig object.

Parameters:

Name Type Description Default
name

name of the adapter

required
module

module path for the adapter

required
Source code in src/odin_control/config/parser.py
def __init__(self, name, module):
    """Initialise the AdapterConfig object.

    :param name: name of the adapter
    :param module: module path for the adapter
    """
    self.name = name
    self.module = module
    self._options = {}

options()

Return a dictionary of the options set in the object.

Returns:

Type Description

dictionary of options

Source code in src/odin_control/config/parser.py
def options(self):
    """Return a dictionary of the options set in the object.

    :return: dictionary of options
    """
    return self._options

set(option, value)

Set an option for the adapter configuration object.

Parameters:

Name Type Description Default
option

name of the option

required
value

value of the option

required

Returns:

Type Description

None

Source code in src/odin_control/config/parser.py
def set(self, option, value):
    """Set an option for the adapter configuration object.

    :param option: name of the option
    :param value: value of the option
    :return: None
    """
    self._options[option] = value

ConfigError

Bases: Exception

ConfigParser exception class.

A trivial exception class for signalling configuration parsing errors

Source code in src/odin_control/config/parser.py
class ConfigError(Exception):
    """ConfigParser exception class.

    A trivial exception class for signalling configuration parsing errors
    """

    pass

ConfigOption

Bases: object

A configuration option container class.

A simple container class used internally by ConfigParser to define a configuration option, its type, default value and whether it has multiple values

Source code in src/odin_control/config/parser.py
class ConfigOption(object):
    """A configuration option container class.

    A simple container class used internally by ConfigParser to define a configuration option,
    its type, default value and whether it has multiple values
    """

    def __init__(self, name, option_type=None, default=None, multiple=False, callback=None):
        """Initialise the ConfigOption object.

        :param name: name of the option
        :param option_type: type of the option (e.g. int, bool, str, ...)
        :param default:  default value for the option
        :param multiple: flag indicating multiple-valued option
        :param callback: callback to be called whenever option has a value set at parse time
        """
        self.name = name
        self.option_type = option_type
        self.default = default
        self.multiple = multiple
        self.callback = callback

        # Raise an error if specified option_type and default are inconsistent
        if self.option_type is not None and self.default is not None:
            if self.default.__class__ != self.option_type:
                raise ConfigError(
                    'Default value {} for option {} does not have correct type ({})'.format(
                        self.default, self.name, self.option_type
                    )
                )

        # If absent, infer option_type from any default value given. Otherwise fall back
        # to string option_type to allow simple parsing from configuration input
        if self.option_type is None:
            if self.default is not None:
                self.option_type = self.default.__class__
            else:
                self.option_type = str

__init__(name, option_type=None, default=None, multiple=False, callback=None)

Initialise the ConfigOption object.

Parameters:

Name Type Description Default
name

name of the option

required
option_type

type of the option (e.g. int, bool, str, ...)

None
default

default value for the option

None
multiple

flag indicating multiple-valued option

False
callback

callback to be called whenever option has a value set at parse time

None
Source code in src/odin_control/config/parser.py
def __init__(self, name, option_type=None, default=None, multiple=False, callback=None):
    """Initialise the ConfigOption object.

    :param name: name of the option
    :param option_type: type of the option (e.g. int, bool, str, ...)
    :param default:  default value for the option
    :param multiple: flag indicating multiple-valued option
    :param callback: callback to be called whenever option has a value set at parse time
    """
    self.name = name
    self.option_type = option_type
    self.default = default
    self.multiple = multiple
    self.callback = callback

    # Raise an error if specified option_type and default are inconsistent
    if self.option_type is not None and self.default is not None:
        if self.default.__class__ != self.option_type:
            raise ConfigError(
                'Default value {} for option {} does not have correct type ({})'.format(
                    self.default, self.name, self.option_type
                )
            )

    # If absent, infer option_type from any default value given. Otherwise fall back
    # to string option_type to allow simple parsing from configuration input
    if self.option_type is None:
        if self.default is not None:
            self.option_type = self.default.__class__
        else:
            self.option_type = str

ConfigParser

Parses configuration options from the command-line and from a file.

Provides parsing of program configuration options from both command-line arguments and from an INI-style file. This class is designed to integrate and replace the default Tornado options parser, which has limitations in prioritising options from multiple sources. Parsed options are set as attributes of the object, allowing the calling program to easily resolve them.

Options specified at the command-line take priority over the value present in any configuration file specified. Any options already defined by Tornado packages at parse time are integrated into this parser and can be specified in the [tornado] section of the configuration file. Similarly, options defined by this parser are specified in the [server] section.

API adapters are treated as a special case of file-only configuration. If the adapters config option is defined (either in command-line args or in a configuration file) then the resolve_adapters() method can be used to extract adapter-specific options from a section named [adapter.].

Source code in src/odin_control/config/parser.py
class ConfigParser():
    """Parses configuration options from the command-line and from a file.

    Provides parsing of program configuration options from both command-line arguments and
    from an INI-style file. This class is designed to integrate and replace the default Tornado
    options parser, which has limitations in prioritising options from multiple sources. Parsed
    options are set as attributes of the object, allowing the calling program to easily resolve
    them.

    Options specified at the command-line take priority over the value present in any configuration
    file specified. Any options already defined by Tornado packages at parse time are integrated
    into this parser and can be specified in the [tornado] section of the configuration file.
    Similarly, options defined by this parser are specified in the [server] section.

    API adapters are treated as a special case of file-only configuration. If the adapters config
    option is defined (either in command-line args or in a configuration file) then the
    resolve_adapters() method can be used to extract adapter-specific options from a section named
    [adapter.<adapter_name>].
    """

    def __init__(self, has_adapters=True):
        """Initialise the configuration parser.

        :param has_adapters: if True, automatically define an --adapters option in the parser
        """
        # Initialise allowed options sections
        self.allowed_options = {
            'server':  {},
            'tornado': {},
        }

        # Create CLI argument and file configuration parsers. This class uses the python
        # argparse module for command-line arguments rather than the Tornado options implementation.
        self.arg_parser = ArgumentParser()
        self.file_parser = NativeConfigParser()
        self.file_parsed = False

        # Define a --version option to return the ODIN server version
        self.define('version', option_type=bool, default=False, action='store_true',
                    option_help='Show the server version information and exit',
                    callback=self._version_callback)

        # Define a --config option to specify configuration file to parse.
        self.define('config', default=None, metavar='FILE',
                    option_help='Specify a configuration file to parse')

        # If has_adapters argument is True, define a --adapters config option
        if has_adapters:
            self.define('adapters', option_type=str, multiple=True,
                        option_help='Comma-separated list of API adapters to load')

    def define(self, name, default=None, option_type=None, option_help=None, metavar=None,
               multiple=False, action='store', callback=None):
        """Define an option to be parsed from the command-line and/or a configuration file.

        This method replicates the functionality of the tornado options define(), allowing
        options to be defined for parsing. Named options can be given a default value, a type,
        which can either be given explicitly or inferred from the default value if given,
        ``help`` and ``metavar`` values for display in command-line help, and a ``multiple``
        flag to allow comma-delimited multiple values to be specified.

        :param name: name of the option
        :param default: default value for the option
        :param option_type: type of the option (e.g. int, bool, str)
        :param option_help: help text to be displayed for the option
        :param metavar: a name for the option to be used in help text
        :param multiple: defines if the option accepts multiple, comma-delimited values
        :param action: ArgumentParser-like action to be taken on parsing option
        :param callback: callback to run whenever a value for the option is set at parse time
        :return: None
        """
        # Add the option to allowed_options
        self.allowed_options['server'][name] = ConfigOption(
            name, option_type=option_type, default=default, multiple=multiple, callback=callback)

        # Format the CLI option switch
        opt_switch = '--{}'.format(name)

        # Resolve the option type, which may have been inferred from the default value
        # if not sepcified. If multiple values are allowed, create a type-specific
        # parsing partial function to allow comma-separated values to be resolved and
        # cast to appropriate type
        option_type = self.allowed_options['server'][name].option_type
        if multiple:
            option_type = partial(_parse_multiple_arg, arg_type=option_type)

        # Clear the default flag so that CLI arguments don't automatically have the
        # default value assigned by the parser, clobbering any value in the file. The
        # default value specified by this call is resolved at parse time if necessary.
        default = None

        # Build the kwargs for the add_argument call for the parser. If a boolean
        # flag action has been specified, i.e. store_true or store_false, don't add the
        # type and metavar keyword arguments as their presence causes an exception to be
        # thrown
        add_kwargs = {
            'action': action, 'default': default,
            'help': option_help,
        }
        if action != 'store_true' and action != 'store_false':
            add_kwargs['type'] = option_type
            add_kwargs['metavar'] = metavar

        # Add the option as an argument to the CLI parser
        self.arg_parser.add_argument(opt_switch, **add_kwargs)

        # Set this option as an attribute in the current instance but with an undefined
        # value until parsing occurs. The allows the parser.<option> syntax to be used
        # without error throughout the lifetime of the instance
        setattr(self, name, None)

    def parse(self, args=None):
        """Parse command-line and file configuration options.

        This method parses command-line and file (if specified a ``--config`` argument)
        configuration options. The parser will parse command-line options from the
        program invocation arguments or, if specified in the ``args`` parameter, from
        a string with the appropriate format.

        This method resolves any options already loaded into the global tornado options
        instance and parses them from the command-line or the [tornado] section of a
        configuration file. Those options are loaded back into the tornado options
        instance on completion of parsing, and the the tornado parser callbacks invoked
        to replicate the desired behaviour.

        :param args: optional string containing arguments to parse
        :return: None
        """
        # If args not specified, use the program command-line argv string
        if args is None:
            args = sys.argv

        # Load existing tornado options into the parser
        self._load_tornado_options()

        # Parse command-line arguments
        (arg_config, _) = self.arg_parser.parse_known_args(args)

        # Parse file configuration options
        file_config = self._parse_file_config(arg_config.config)

        # Now iterate over the allowed options and set attributes in the current parser for each,
        # using, in order of priority, the command-line argument, the config file or the default
        # value.
        arg_config_vars = vars(arg_config)
        for section in self.allowed_options:
            for option in self.allowed_options[section]:
                option_val = None
                if option in file_config[section] and file_config[section][option] is not None:
                    option_val = file_config[section][option]
                if option in arg_config_vars and arg_config_vars[option] is not None:
                    option_val = vars(arg_config)[option]
                if option_val is None:
                    option_val = self.allowed_options[section][option].default

                # Set option as attribute in this instance
                setattr(self, option, option_val)

                # If this option is in the tornado options, update its value
                if option in tornado.options.options:
                    setattr(tornado.options.options, option, option_val)

                # If this option has a callback, call it
                if self.allowed_options[section][option].callback is not None:
                    self.allowed_options[section][option].callback(option_val)

        # Run the tornado parser callbacks to replicate the tornado parser behaviour
        tornado.options.options.run_parse_callbacks()

    def _parse_file_config(self, config_file):
        """Parse a configuration file (INTERNAL METHOD).

        :param config_file: name of configuration file to parse
        :return: container of resolved file configuration options
        """
        # Initialise a container for resolved file configuration options
        file_config = {}
        for section in self.allowed_options:
            file_config[section] = {}

        # If a --config options was parsed, attempt to parse the specified file
        if config_file:

            try:
                with open(config_file) as config_fp:
                    self.file_parser.read_file(config_fp)
            except Exception as e:
                raise ConfigError('Failed to parse configuration file: {}'.format(e))

            self.file_parsed = True

            # Define a mapping between option types and the file parser getter methods
            parser_get_map = {
                int: self.file_parser.getint,
                float: self.file_parser.getfloat,
                bool: self.file_parser.getboolean,
                str: self.file_parser.get,
            }

            # Iterate over the allowed options from all sections and extract from the file
            # configuration if present
            for section in self.allowed_options:
                file_config[section] = {}
                if self.file_parser.has_section(section):
                    for option in self.allowed_options[section]:
                        if self.file_parser.has_option(section, option):

                            option_type = self.allowed_options[section][option].option_type

                            # Handle multiple values
                            if self.allowed_options[section][option].multiple:
                                option_str = self.file_parser.get(section, option)
                                value = _parse_multiple_arg(option_str, arg_type=option_type)
                            else:
                                if option_type in parser_get_map:
                                    value = parser_get_map[option_type](section, option)
                                else:
                                    value = self.file_parser.get(section, option)
                        else:
                            value = None

                        file_config[section][option] = value

        return file_config

    def _load_tornado_options(self):
        """Load tornado options into the parser (INTERNAL METHOD)."""
        tornado_opts = tornado.options.options._options
        self.allowed_options['tornado'] = {}
        for opt in sorted(tornado_opts):
            if opt != 'help':
                opt_switch = '--{}'.format(tornado_opts[opt].name)
                self.arg_parser.add_argument(opt_switch, type=tornado_opts[opt].type,
                                             help=tornado_opts[opt].help,
                                             metavar=tornado_opts[opt].metavar)
                self.allowed_options['tornado'][tornado_opts[opt].name] = ConfigOption(
                    tornado_opts[opt].name, tornado_opts[opt].type, tornado_opts[opt].default
                )

    def _version_callback(self, value):
        """Print the odin server version information and exit."""
        if value:
            print("odin control version {}".format(__version__))
            sys.exit(0)

    def resolve_adapters(self, adapter_list=None):
        """Resolve any adapter sections present in the configuration file.

        This method resovles adapter sections preent in the configuration file into a usable
        list of adapters to be loaded and configured by the server.

        :param adapter_list: a list of adapter names to resolve. If not set, the
            parsers adapters attribute will be substituted

        :return: dict of AdapterConfig objects from the configuration file
        """
        resolved_adapters = {}

        # If no adapter names to resolve were specified, determine if any were given
        # in the command line or file configuration.
        if adapter_list is None:
            try:
                adapter_list = getattr(self, 'adapters')
            except AttributeError:
                raise ConfigError('Configuration parser has no adapter option set')
            if adapter_list is None:
                raise ConfigError("No adapters specified in configuration")

        # If a configuration file wasn't parsed, it won't be possible to resolve adapter-specific
        # sections so raise an error
        if not self.file_parsed:
            raise ConfigError('No configuration file parsed, unable to resolve adapters')

        # Loop over the specified adapters
        for adapter in adapter_list:

            # Build the section name in the config file as [adapter.<adapter_name>]
            section_name = 'adapter.{}'.format(adapter)

            # Check section is present otherwise raise an error
            if not self.file_parser.has_section(section_name):
                raise ConfigError(
                    'Configuration file has no section for adapter {}'.format(adapter)
                )

            # Check that the compulsory module option is present in the adapter section
            if not self.file_parser.has_option(section_name, 'module'):
                raise ConfigError(
                    'Configuration file has no module parameter for adapter {}'.format(adapter)
                )

            # Create a new adapter configuration object and add it to the returned dictionary
            module = self.file_parser.get(section_name, 'module')
            resolved_adapters[adapter] = AdapterConfig(adapter, module)

            # Extract any other adapter-specific options and set them as attributes in the config
            # object
            for (name, value) in self.file_parser.items(section_name):
                if name == 'module':
                    continue
                resolved_adapters[adapter].set(name, value)

        return resolved_adapters

    def __contains__(self, item):
        """Containment check operator - allows ``in`` to check for presence of option.

        :param item: item to check for presence
        """
        return hasattr(self, item)

    def __iter__(self):
        """Return an iterator object over the options specified in the current instance."""
        return (name for section in self.allowed_options for name in self.allowed_options[section])

__contains__(item)

Containment check operator - allows in to check for presence of option.

Parameters:

Name Type Description Default
item

item to check for presence

required
Source code in src/odin_control/config/parser.py
def __contains__(self, item):
    """Containment check operator - allows ``in`` to check for presence of option.

    :param item: item to check for presence
    """
    return hasattr(self, item)

__init__(has_adapters=True)

Initialise the configuration parser.

Parameters:

Name Type Description Default
has_adapters

if True, automatically define an --adapters option in the parser

True
Source code in src/odin_control/config/parser.py
def __init__(self, has_adapters=True):
    """Initialise the configuration parser.

    :param has_adapters: if True, automatically define an --adapters option in the parser
    """
    # Initialise allowed options sections
    self.allowed_options = {
        'server':  {},
        'tornado': {},
    }

    # Create CLI argument and file configuration parsers. This class uses the python
    # argparse module for command-line arguments rather than the Tornado options implementation.
    self.arg_parser = ArgumentParser()
    self.file_parser = NativeConfigParser()
    self.file_parsed = False

    # Define a --version option to return the ODIN server version
    self.define('version', option_type=bool, default=False, action='store_true',
                option_help='Show the server version information and exit',
                callback=self._version_callback)

    # Define a --config option to specify configuration file to parse.
    self.define('config', default=None, metavar='FILE',
                option_help='Specify a configuration file to parse')

    # If has_adapters argument is True, define a --adapters config option
    if has_adapters:
        self.define('adapters', option_type=str, multiple=True,
                    option_help='Comma-separated list of API adapters to load')

__iter__()

Return an iterator object over the options specified in the current instance.

Source code in src/odin_control/config/parser.py
def __iter__(self):
    """Return an iterator object over the options specified in the current instance."""
    return (name for section in self.allowed_options for name in self.allowed_options[section])

define(name, default=None, option_type=None, option_help=None, metavar=None, multiple=False, action='store', callback=None)

Define an option to be parsed from the command-line and/or a configuration file.

This method replicates the functionality of the tornado options define(), allowing options to be defined for parsing. Named options can be given a default value, a type, which can either be given explicitly or inferred from the default value if given, help and metavar values for display in command-line help, and a multiple flag to allow comma-delimited multiple values to be specified.

Parameters:

Name Type Description Default
name

name of the option

required
default

default value for the option

None
option_type

type of the option (e.g. int, bool, str)

None
option_help

help text to be displayed for the option

None
metavar

a name for the option to be used in help text

None
multiple

defines if the option accepts multiple, comma-delimited values

False
action

ArgumentParser-like action to be taken on parsing option

'store'
callback

callback to run whenever a value for the option is set at parse time

None

Returns:

Type Description

None

Source code in src/odin_control/config/parser.py
def define(self, name, default=None, option_type=None, option_help=None, metavar=None,
           multiple=False, action='store', callback=None):
    """Define an option to be parsed from the command-line and/or a configuration file.

    This method replicates the functionality of the tornado options define(), allowing
    options to be defined for parsing. Named options can be given a default value, a type,
    which can either be given explicitly or inferred from the default value if given,
    ``help`` and ``metavar`` values for display in command-line help, and a ``multiple``
    flag to allow comma-delimited multiple values to be specified.

    :param name: name of the option
    :param default: default value for the option
    :param option_type: type of the option (e.g. int, bool, str)
    :param option_help: help text to be displayed for the option
    :param metavar: a name for the option to be used in help text
    :param multiple: defines if the option accepts multiple, comma-delimited values
    :param action: ArgumentParser-like action to be taken on parsing option
    :param callback: callback to run whenever a value for the option is set at parse time
    :return: None
    """
    # Add the option to allowed_options
    self.allowed_options['server'][name] = ConfigOption(
        name, option_type=option_type, default=default, multiple=multiple, callback=callback)

    # Format the CLI option switch
    opt_switch = '--{}'.format(name)

    # Resolve the option type, which may have been inferred from the default value
    # if not sepcified. If multiple values are allowed, create a type-specific
    # parsing partial function to allow comma-separated values to be resolved and
    # cast to appropriate type
    option_type = self.allowed_options['server'][name].option_type
    if multiple:
        option_type = partial(_parse_multiple_arg, arg_type=option_type)

    # Clear the default flag so that CLI arguments don't automatically have the
    # default value assigned by the parser, clobbering any value in the file. The
    # default value specified by this call is resolved at parse time if necessary.
    default = None

    # Build the kwargs for the add_argument call for the parser. If a boolean
    # flag action has been specified, i.e. store_true or store_false, don't add the
    # type and metavar keyword arguments as their presence causes an exception to be
    # thrown
    add_kwargs = {
        'action': action, 'default': default,
        'help': option_help,
    }
    if action != 'store_true' and action != 'store_false':
        add_kwargs['type'] = option_type
        add_kwargs['metavar'] = metavar

    # Add the option as an argument to the CLI parser
    self.arg_parser.add_argument(opt_switch, **add_kwargs)

    # Set this option as an attribute in the current instance but with an undefined
    # value until parsing occurs. The allows the parser.<option> syntax to be used
    # without error throughout the lifetime of the instance
    setattr(self, name, None)

parse(args=None)

Parse command-line and file configuration options.

This method parses command-line and file (if specified a --config argument) configuration options. The parser will parse command-line options from the program invocation arguments or, if specified in the args parameter, from a string with the appropriate format.

This method resolves any options already loaded into the global tornado options instance and parses them from the command-line or the [tornado] section of a configuration file. Those options are loaded back into the tornado options instance on completion of parsing, and the the tornado parser callbacks invoked to replicate the desired behaviour.

Parameters:

Name Type Description Default
args

optional string containing arguments to parse

None

Returns:

Type Description

None

Source code in src/odin_control/config/parser.py
def parse(self, args=None):
    """Parse command-line and file configuration options.

    This method parses command-line and file (if specified a ``--config`` argument)
    configuration options. The parser will parse command-line options from the
    program invocation arguments or, if specified in the ``args`` parameter, from
    a string with the appropriate format.

    This method resolves any options already loaded into the global tornado options
    instance and parses them from the command-line or the [tornado] section of a
    configuration file. Those options are loaded back into the tornado options
    instance on completion of parsing, and the the tornado parser callbacks invoked
    to replicate the desired behaviour.

    :param args: optional string containing arguments to parse
    :return: None
    """
    # If args not specified, use the program command-line argv string
    if args is None:
        args = sys.argv

    # Load existing tornado options into the parser
    self._load_tornado_options()

    # Parse command-line arguments
    (arg_config, _) = self.arg_parser.parse_known_args(args)

    # Parse file configuration options
    file_config = self._parse_file_config(arg_config.config)

    # Now iterate over the allowed options and set attributes in the current parser for each,
    # using, in order of priority, the command-line argument, the config file or the default
    # value.
    arg_config_vars = vars(arg_config)
    for section in self.allowed_options:
        for option in self.allowed_options[section]:
            option_val = None
            if option in file_config[section] and file_config[section][option] is not None:
                option_val = file_config[section][option]
            if option in arg_config_vars and arg_config_vars[option] is not None:
                option_val = vars(arg_config)[option]
            if option_val is None:
                option_val = self.allowed_options[section][option].default

            # Set option as attribute in this instance
            setattr(self, option, option_val)

            # If this option is in the tornado options, update its value
            if option in tornado.options.options:
                setattr(tornado.options.options, option, option_val)

            # If this option has a callback, call it
            if self.allowed_options[section][option].callback is not None:
                self.allowed_options[section][option].callback(option_val)

    # Run the tornado parser callbacks to replicate the tornado parser behaviour
    tornado.options.options.run_parse_callbacks()

resolve_adapters(adapter_list=None)

Resolve any adapter sections present in the configuration file.

This method resovles adapter sections preent in the configuration file into a usable list of adapters to be loaded and configured by the server.

Parameters:

Name Type Description Default
adapter_list

a list of adapter names to resolve. If not set, the parsers adapters attribute will be substituted

None

Returns:

Type Description

dict of AdapterConfig objects from the configuration file

Source code in src/odin_control/config/parser.py
def resolve_adapters(self, adapter_list=None):
    """Resolve any adapter sections present in the configuration file.

    This method resovles adapter sections preent in the configuration file into a usable
    list of adapters to be loaded and configured by the server.

    :param adapter_list: a list of adapter names to resolve. If not set, the
        parsers adapters attribute will be substituted

    :return: dict of AdapterConfig objects from the configuration file
    """
    resolved_adapters = {}

    # If no adapter names to resolve were specified, determine if any were given
    # in the command line or file configuration.
    if adapter_list is None:
        try:
            adapter_list = getattr(self, 'adapters')
        except AttributeError:
            raise ConfigError('Configuration parser has no adapter option set')
        if adapter_list is None:
            raise ConfigError("No adapters specified in configuration")

    # If a configuration file wasn't parsed, it won't be possible to resolve adapter-specific
    # sections so raise an error
    if not self.file_parsed:
        raise ConfigError('No configuration file parsed, unable to resolve adapters')

    # Loop over the specified adapters
    for adapter in adapter_list:

        # Build the section name in the config file as [adapter.<adapter_name>]
        section_name = 'adapter.{}'.format(adapter)

        # Check section is present otherwise raise an error
        if not self.file_parser.has_section(section_name):
            raise ConfigError(
                'Configuration file has no section for adapter {}'.format(adapter)
            )

        # Check that the compulsory module option is present in the adapter section
        if not self.file_parser.has_option(section_name, 'module'):
            raise ConfigError(
                'Configuration file has no module parameter for adapter {}'.format(adapter)
            )

        # Create a new adapter configuration object and add it to the returned dictionary
        module = self.file_parser.get(section_name, 'module')
        resolved_adapters[adapter] = AdapterConfig(adapter, module)

        # Extract any other adapter-specific options and set them as attributes in the config
        # object
        for (name, value) in self.file_parser.items(section_name):
            if name == 'module':
                continue
            resolved_adapters[adapter].set(name, value)

    return resolved_adapters