%PDF- %PDF-
| Direktori : /proc/self/root/lib/python3/dist-packages/mkdocs/config/ |
| Current File : //proc/self/root/lib/python3/dist-packages/mkdocs/config/base.py |
import logging
import os
import sys
from yaml import YAMLError
from collections import UserDict
from mkdocs import exceptions
from mkdocs import utils
log = logging.getLogger('mkdocs.config')
class ValidationError(Exception):
"""Raised during the validation process of the config on errors."""
class Config(UserDict):
"""
MkDocs Configuration dict
This is a fairly simple extension of a standard dictionary. It adds methods
for running validation on the structure and contents.
"""
def __init__(self, schema, config_file_path=None):
"""
The schema is a Python dict which maps the config name to a validator.
"""
self._schema = schema
self._schema_keys = set(dict(schema).keys())
# Ensure config_file_path is a Unicode string
if config_file_path is not None and not isinstance(config_file_path, str):
try:
# Assume config_file_path is encoded with the file system encoding.
config_file_path = config_file_path.decode(encoding=sys.getfilesystemencoding())
except UnicodeDecodeError:
raise ValidationError("config_file_path is not a Unicode string.")
self.config_file_path = config_file_path
self.data = {}
self.user_configs = []
self.set_defaults()
def set_defaults(self):
"""
Set the base config by going through each validator and getting the
default if it has one.
"""
for key, config_option in self._schema:
self[key] = config_option.default
def _validate(self):
failed, warnings = [], []
for key, config_option in self._schema:
try:
value = self.get(key)
self[key] = config_option.validate(value)
warnings.extend([(key, w) for w in config_option.warnings])
config_option.reset_warnings()
except ValidationError as e:
failed.append((key, e))
for key in (set(self.keys()) - self._schema_keys):
warnings.append((
key, "Unrecognised configuration name: {}".format(key)
))
return failed, warnings
def _pre_validate(self):
failed, warnings = [], []
for key, config_option in self._schema:
try:
config_option.pre_validation(self, key_name=key)
warnings.extend([(key, w) for w in config_option.warnings])
config_option.reset_warnings()
except ValidationError as e:
failed.append((key, e))
return failed, warnings
def _post_validate(self):
failed, warnings = [], []
for key, config_option in self._schema:
try:
config_option.post_validation(self, key_name=key)
warnings.extend([(key, w) for w in config_option.warnings])
config_option.reset_warnings()
except ValidationError as e:
failed.append((key, e))
return failed, warnings
def validate(self):
failed, warnings = self._pre_validate()
run_failed, run_warnings = self._validate()
failed.extend(run_failed)
warnings.extend(run_warnings)
# Only run the post validation steps if there are no failures, warnings
# are okay.
if len(failed) == 0:
post_failed, post_warnings = self._post_validate()
failed.extend(post_failed)
warnings.extend(post_warnings)
return failed, warnings
def load_dict(self, patch):
if not isinstance(patch, dict):
raise exceptions.ConfigurationError(
"The configuration is invalid. The expected type was a key "
"value mapping (a python dict) but we got an object of type: "
"{}".format(type(patch)))
self.user_configs.append(patch)
self.data.update(patch)
def load_file(self, config_file):
try:
return self.load_dict(utils.yaml_load(config_file))
except YAMLError as e:
# MkDocs knows and understands ConfigurationErrors
raise exceptions.ConfigurationError(
"MkDocs encountered as error parsing the configuration file: {}".format(e)
)
def _open_config_file(config_file):
# Default to the standard config filename.
if config_file is None:
config_file = os.path.abspath('mkdocs.yml')
# If closed file descriptor, get file path to reopen later.
if hasattr(config_file, 'closed') and config_file.closed:
config_file = config_file.name
log.debug("Loading configuration file: {}".format(config_file))
# If it is a string, we can assume it is a path and attempt to open it.
if isinstance(config_file, str):
if os.path.exists(config_file):
config_file = open(config_file, 'rb')
else:
raise exceptions.ConfigurationError(
"Config file '{}' does not exist.".format(config_file))
# Ensure file descriptor is at begining
config_file.seek(0)
return config_file
def load_config(config_file=None, **kwargs):
"""
Load the configuration for a given file object or name
The config_file can either be a file object, string or None. If it is None
the default `mkdocs.yml` filename will loaded.
Extra kwargs are passed to the configuration to replace any default values
unless they themselves are None.
"""
options = kwargs.copy()
# Filter None values from the options. This usually happens with optional
# parameters from Click.
for key, value in options.copy().items():
if value is None:
options.pop(key)
config_file = _open_config_file(config_file)
options['config_file_path'] = getattr(config_file, 'name', '')
# Initialise the config with the default schema .
from mkdocs import config
cfg = Config(schema=config.DEFAULT_SCHEMA, config_file_path=options['config_file_path'])
# First load the config file
cfg.load_file(config_file)
# Then load the options to overwrite anything in the config.
cfg.load_dict(options)
errors, warnings = cfg.validate()
for config_name, warning in warnings:
log.warning("Config value: '%s'. Warning: %s", config_name, warning)
for config_name, error in errors:
log.error("Config value: '%s'. Error: %s", config_name, error)
for key, value in cfg.items():
log.debug("Config value: '%s' = %r", key, value)
if len(errors) > 0:
raise exceptions.ConfigurationError(
"Aborted with {} Configuration Errors!".format(len(errors))
)
elif cfg['strict'] and len(warnings) > 0:
raise exceptions.ConfigurationError(
"Aborted with {} Configuration Warnings in 'strict' mode!".format(len(warnings))
)
return cfg