"""
Configuration file definition and parsing
"""
import inspect
import logging
import os
import ZConfig.info
from ZConfig import SchemaResourceError
from ZConfig.loader import ConfigLoader, SchemaLoader
from ZConfig.schema import BaseParser
from dhcpkit.ipv6.server.config_elements import MainConfig
from dhcpkit.ipv6.server.extension_registry import server_extension_registry
logger = logging.getLogger(__name__)
[docs]def get_config_loader() -> ConfigLoader:
"""
Get the config loader with all extensions
:return: The fully extended config loader
"""
# Patch the parser because otherwise it will reject the example section in the schema
# Construct the paths to all necessary files
schema_filename = os.path.abspath(os.path.join(os.path.dirname(__file__), "config_schema.xml"))
# Patch the parser if we have an old version of ZConfig. Necessary patches depend on the ZConfig version.
# noinspection PyProtectedMember
if 'schema' not in BaseParser._allowed_parents['example']:
# noinspection PyProtectedMember
BaseParser._allowed_parents['example'].append('schema')
# Add some properties
ZConfig.info.SchemaType.example = None
# Patch a method by saving the old method and replacing the original with a patched version
ZConfig.info.oldCreateDerivedSchema = ZConfig.info.createDerivedSchema
# noinspection PyUnresolvedReferences,PyPep8Naming
def patchedCreateDerivedSchema(base: ZConfig.info.SchemaType) -> ZConfig.info.SchemaType:
"""
Also copy the example section.
:param base: The original
:return: The copy
"""
new = ZConfig.info.oldCreateDerivedSchema(base)
new.example = base.example
return new
ZConfig.info.createDerivedSchema = patchedCreateDerivedSchema
# noinspection PyProtectedMember
if 'sectiontype' not in BaseParser._allowed_parents['example']:
# noinspection PyProtectedMember
BaseParser._allowed_parents['example'].append('sectiontype')
# Add some properties
ZConfig.info.SectionType.example = None
# noinspection PyProtectedMember
if 'section' not in BaseParser._allowed_parents['example']:
# noinspection PyProtectedMember
BaseParser._allowed_parents['example'].append('section')
# Add some properties
ZConfig.info.SectionInfo.example = None
# noinspection PyProtectedMember
if 'multisection' not in BaseParser._allowed_parents['example']:
# noinspection PyProtectedMember
BaseParser._allowed_parents['example'].append('multisection')
# Add some properties
ZConfig.info.SectionInfo.example = None
# Load the schema
schema_loader = SchemaLoader()
schema = schema_loader.loadURL(url=schema_filename)
# Build the config loader based on the schema, extended with the schemas of option handlers
config_loader = ConfigLoader(schema=schema)
# Iterate over all server extensions
for extension_name, extension in server_extension_registry.items():
# Option handlers that refer to packages contain components
if inspect.ismodule(extension) and hasattr(extension, '__path__'):
# It's a package! Try to import
try:
config_loader.importSchemaComponent(extension.__name__)
logger.debug("Configuration extension {} loaded".format(extension_name))
except SchemaResourceError:
# Component missing, assume it's a package without a config component
pass
return config_loader
[docs]def load_config(config_filename: str) -> MainConfig:
"""
Load the given configuration file.
:param config_filename: The configuration file
:return: The parsed config
"""
logger.debug("Loading configuration file {}".format(config_filename))
config_loader = get_config_loader()
config_filename = os.path.realpath(config_filename)
config, handlers = config_loader.loadURL(config_filename)
return config