Skip to content

Commit

Permalink
Support passing Config subclasses to SubConfig and ConfigItems
Browse files Browse the repository at this point in the history
They are validated by default
  • Loading branch information
oprypin committed Sep 14, 2022
1 parent 78fe746 commit 28424bb
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 12 deletions.
2 changes: 1 addition & 1 deletion mkdocs/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class LegacyConfig(Config):
"""

def __init__(self, schema: PlainConfigSchema, config_file_path: Optional[str] = None):
self._schema = schema
self._schema = tuple((k, v) for k, v in schema) # Re-create just for validation
super().__init__(config_file_path)


Expand Down
60 changes: 49 additions & 11 deletions mkdocs/config/config_options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import functools
import ipaddress
import os
import string
Expand All @@ -25,26 +26,54 @@
from mkdocs.exceptions import ConfigurationError

T = TypeVar('T')
SomeConfig = TypeVar('SomeConfig', bound=Config)


class SubConfig(BaseConfigOption[Config]):
class SubConfig(Generic[SomeConfig], BaseConfigOption[SomeConfig]):
"""
Subconfig Config Option
New: If targeting MkDocs 1.4+, please pass a subclass of Config to the
constructor, instead of the old style of a sequence of ConfigOption instances.
Validation is then enabled by default.
A set of `config_options` grouped under a single config option.
By default, validation errors and warnings resulting from validating
`config_options` are ignored (`validate=False`). Users should typically
enable validation with `validate=True`.
"""

def __init__(self, *config_options: PlainConfigSchemaItem, validate: bool = False):
@overload
def __init__(
self: SubConfig[SomeConfig], config_class: t.Type[SomeConfig], *, validate: bool = True
):
"""Create a sub-config in a type-safe way, using fields defined in a Config subclass."""

@overload
def __init__(
self: SubConfig[LegacyConfig],
*config_options: PlainConfigSchemaItem,
validate: bool = False,
):
"""Create an untyped sub-config, using directly passed fields."""

def __init__(self, *config_options, validate=None):
super().__init__()
self.default = {}
self.config_options = config_options
self._do_validation = validate
if (
len(config_options) == 1
and isinstance(config_options[0], type)
and issubclass(config_options[0], Config)
):
if validate is None:
validate = True
(self._make_config,) = config_options
else:
self._make_config = functools.partial(LegacyConfig, config_options)
self._do_validation = bool(validate)

def run_validation(self, value):
config = LegacyConfig(self.config_options)
config = self._make_config()
try:
config.load_dict(value)
failed, warnings = config.validate()
Expand Down Expand Up @@ -152,25 +181,34 @@ def run_validation(self, value):
return [fake_config[k] for k in fake_keys]


class ConfigItems(ListOfItems[Config]):
class ConfigItems(Generic[SomeConfig], ListOfItems[SomeConfig]):
"""
Config Items Option
Validates a list of mappings that all must match the same set of
options.
New: If targeting MkDocs 1.4+, please pass a subclass of Config to the
constructor, instead of the old style of a sequence of ConfigOption instances.
Validation is then enabled by default.
"""

@overload
def __init__(self, *config_options: PlainConfigSchemaItem, validate: bool = False):
...
def __init__(
self: ConfigItems[SomeConfig], config_class: t.Type[SomeConfig], *, validate: bool = True
):
"""Create a config for a list of sub-configs in a type-safe way, using fields defined in a Config subclass."""

@overload
def __init__(
self, *config_options: PlainConfigSchemaItem, required: bool, validate: bool = False
self: ConfigItems[LegacyConfig],
*config_options: PlainConfigSchemaItem,
validate: bool = False,
required: bool = False,
):
...
"""Create a config for a list of untyped sub-configs, using directly passed fields."""

def __init__(self, *config_options, required=None, validate=False):
def __init__(self, *config_options, required=None, validate=None):
super().__init__(SubConfig(*config_options, validate=validate))
self._legacy_required = required
self.required = bool(required)
Expand Down

0 comments on commit 28424bb

Please sign in to comment.