Skip to content

Commit

Permalink
Add an enabled setting for all plugins (#3395)
Browse files Browse the repository at this point in the history
For every plugin, the user can set `enabled: false` (or something based on an environment variable) and it will not kick in at all.

If the plugin has its own `enabled` config, there's no change for it in that case.
  • Loading branch information
oprypin committed Oct 30, 2023
1 parent f3d5c8d commit 7ab01c2
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 9 deletions.
35 changes: 26 additions & 9 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,8 @@ You might have seen this feature in the [mkdocs-simple-hooks plugin](https://git
A list of plugins (with optional configuration settings) to use when building
the site. See the [Plugins] documentation for full details.
**default**: `['search']` (the "search" plugin included with MkDocs).
If the `plugins` config setting is defined in the `mkdocs.yml` config file, then
any defaults (such as `search`) are ignored and you need to explicitly re-enable
the defaults if you would like to continue using them:
Expand All @@ -820,6 +822,30 @@ plugins:
option2: other value
```
To completely disable all plugins, including any defaults, set the `plugins`
setting to an empty list:
```yaml
plugins: []
```
#### `enabled` option
> NEW: **New in MkDocs 1.6.**
>
> Each plugin has its own options keys. However MkDocs also ensures that each plugin has the `enabled` boolean option. This can be used to conditionally enable a particular plugin, as in the following example:
>
> ```yaml
> plugins:
> - search
> - code-validator:
> enabled: !ENV [LINT, false]
> ```
>
> See: [Environment variables](#environment-variables)
#### Alternate syntax
In the above examples, each plugin is a list item (starts with a `-`). As an
alternative, key/value pairs can be used instead. However, in that case an empty
value must be provided for plugins for which no options are defined. Therefore,
Expand All @@ -836,15 +862,6 @@ plugins:
This alternative syntax is required if you intend to override some options via
[inheritance].
To completely disable all plugins, including any defaults, set the `plugins`
setting to an empty list:
```yaml
plugins: []
```
**default**: `['search']` (the "search" plugin included with MkDocs).
#### Search
A search plugin is provided by default with MkDocs which uses [lunr.js] as a
Expand Down
13 changes: 13 additions & 0 deletions mkdocs/config/config_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
T = TypeVar('T')
SomeConfig = TypeVar('SomeConfig', bound=Config)

log = logging.getLogger(__name__)


class SubConfig(Generic[SomeConfig], BaseConfigOption[SomeConfig]):
"""
Expand Down Expand Up @@ -1137,6 +1139,17 @@ def load_plugin(self, name: str, config) -> plugins.BasePlugin:
"because the plugin doesn't declare `supports_multiple_instances`."
)

# Only if the plugin doesn't have its own "enabled" config, apply a generic one.
if 'enabled' in config and not any(pair[0] == 'enabled' for pair in plugin.config_scheme):
enabled = config.pop('enabled')
if not isinstance(enabled, bool):
raise ValidationError(
f"Plugin '{name}' option 'enabled': Expected boolean but received: {type(enabled)}"
)
if not enabled:
log.debug(f"Plugin '{inst_name}' is disabled in the config, skipping.")
return plugin

errors, warns = plugin.load_config(
config, self._config.config_file_path if self._config else None
)
Expand Down
53 changes: 53 additions & 0 deletions mkdocs/tests/config/config_options_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,15 @@ class FakePlugin2(BasePlugin[_FakePlugin2Config]):
supports_multiple_instances = True


class _EnabledPluginConfig(Config):
enabled = c.Type(bool, default=True)
bar = c.Type(int, default=0)


class EnabledPlugin(BasePlugin[_EnabledPluginConfig]):
pass


class ThemePlugin(BasePlugin[_FakePluginConfig]):
pass

Expand All @@ -1949,6 +1958,7 @@ def load(self):
return_value=[
FakeEntryPoint('sample', FakePlugin),
FakeEntryPoint('sample2', FakePlugin2),
FakeEntryPoint('sample-e', EnabledPlugin),
FakeEntryPoint('readthedocs/sub_plugin', ThemePlugin),
FakeEntryPoint('overridden', FakePlugin2),
FakeEntryPoint('readthedocs/overridden', ThemePlugin2),
Expand Down Expand Up @@ -2096,6 +2106,49 @@ class Schema(Config):
self.assertEqual(set(conf.plugins), {'overridden'})
self.assertIsInstance(conf.plugins['overridden'], FakePlugin2)

def test_plugin_config_enabled_for_any_plugin(self) -> None:
class Schema(Config):
theme = c.Theme(default='mkdocs')
plugins = c.Plugins(theme_key='theme')

cfg = {'theme': 'readthedocs', 'plugins': {'sample': {'enabled': False, 'bar': 3}}}
conf = self.get_config(Schema, cfg)
self.assertEqual(set(conf.plugins), set())

cfg = {'theme': 'readthedocs', 'plugins': {'sample': {'enabled': True, 'bar': 3}}}
conf = self.get_config(Schema, cfg)
self.assertEqual(set(conf.plugins), {'sample'})
self.assertEqual(conf.plugins['sample'].config.bar, 3)

cfg = {'theme': 'readthedocs', 'plugins': {'sample': {'enabled': 5}}}
with self.expect_error(
plugins="Plugin 'sample' option 'enabled': Expected boolean but received: <class 'int'>"
):
self.get_config(Schema, cfg)

def test_plugin_config_enabled_for_plugin_with_setting(self) -> None:
class Schema(Config):
theme = c.Theme(default='mkdocs')
plugins = c.Plugins(theme_key='theme')

cfg = {'theme': 'readthedocs', 'plugins': {'sample-e': {'enabled': False, 'bar': 3}}}
conf = self.get_config(Schema, cfg)
self.assertEqual(set(conf.plugins), {'sample-e'})
self.assertEqual(conf.plugins['sample-e'].config.enabled, False)
self.assertEqual(conf.plugins['sample-e'].config.bar, 3)

cfg = {'theme': 'readthedocs', 'plugins': {'sample-e': {'enabled': True, 'bar': 3}}}
conf = self.get_config(Schema, cfg)
self.assertEqual(set(conf.plugins), {'sample-e'})
self.assertEqual(conf.plugins['sample-e'].config.enabled, True)
self.assertEqual(conf.plugins['sample-e'].config.bar, 3)

cfg = {'theme': 'readthedocs', 'plugins': {'sample-e': {'enabled': 5}}}
with self.expect_error(
plugins="Plugin 'sample-e' option 'enabled': Expected type: <class 'bool'> but received: <class 'int'>"
):
self.get_config(Schema, cfg)

def test_plugin_config_with_multiple_instances(self) -> None:
class Schema(Config):
theme = c.Theme(default='mkdocs')
Expand Down

0 comments on commit 7ab01c2

Please sign in to comment.