Skip to content

Commit a8b8112

Browse files
committed
Support MkDocs INHERIT and plugins as a dict; resolves #59
1 parent 494147a commit a8b8112

File tree

4 files changed

+87
-17
lines changed

4 files changed

+87
-17
lines changed

CHANGES.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changes
22

3+
## v1.1.0 (in progress)
4+
5+
- Add support for [`!ENV`][mkdocs-env] and [`INHERIT`][mkdocs-inherit] in
6+
`mkdocs.yml`
7+
8+
[mkdocs-env]: https://www.mkdocs.org/user-guide/configuration/#environment-variables
9+
[mkdocs-inherit]: https://www.mkdocs.org/user-guide/configuration/#configuration-inheritance
10+
11+
---
12+
313
## v1.0.1 (2021-05-31)
414

515
### Bug fixes

mike/mkdocs_utils.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import mkdocs.config
2+
import mkdocs.utils
23
import os
34
import re
45
import subprocess
56
import yaml
6-
from collections.abc import Iterable
7+
from collections.abc import Iterable, Mapping
78
from contextlib import contextmanager
89
from tempfile import NamedTemporaryFile
910

@@ -33,8 +34,8 @@ def load_config(config_file=None, **kwargs):
3334

3435
@contextmanager
3536
def inject_plugin(config_file):
36-
with open(config_file) as f:
37-
config = yaml.load(f, Loader=yaml.Loader)
37+
with _open_config(config_file) as f:
38+
config = mkdocs.utils.yaml_load(f)
3839

3940
plugins = config.setdefault('plugins', ['search'])
4041
for i in plugins:
@@ -43,11 +44,16 @@ def inject_plugin(config_file):
4344
yield config_file
4445
return
4546

46-
plugins.insert(0, 'mike')
47+
if isinstance(plugins, Mapping):
48+
config['plugins'] = {'mike': {}}
49+
config['plugins'].update(plugins)
50+
else:
51+
plugins.insert(0, 'mike')
52+
4753
with NamedTemporaryFile(mode='w', dir=os.path.dirname(config_file),
4854
prefix='mike-mkdocs', suffix='.yml',
4955
delete=False) as f:
50-
yaml.dump(config, f)
56+
yaml.dump(config, f, sort_keys=False)
5157

5258
try:
5359
yield f.name

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def distribution_files(self):
9494
packages=find_packages(exclude=['test', 'test.*']),
9595
include_package_data=True,
9696

97-
install_requires=(['mkdocs >= 1.0', 'jinja2', 'pyyaml', 'verspec']),
97+
install_requires=(['mkdocs >= 1.0', 'jinja2', 'pyyaml >= 5.1', 'verspec']),
9898
extras_require={
9999
'dev': ['coverage', 'flake8 >= 3.0'],
100100
'test': ['coverage', 'flake8 >= 3.0'],

test/unit/test_mkdocs_utils.py

+65-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@
88
from mike import mkdocs_utils
99

1010

11+
class Stream(StringIO):
12+
def __init__(self, name, data=''):
13+
super().__init__(data)
14+
self.name = name
15+
16+
def close(self):
17+
pass
18+
19+
20+
def mock_open_files(files):
21+
def wrapper(filename, *args, **kwargs):
22+
name = os.path.basename(filename)
23+
return Stream(name, files[name])
24+
25+
return wrapper
26+
27+
1128
# This mostly just tests `load_config` from MkDocs, but we want to be sure it
1229
# behaves as we want it.
1330
class TestLoadConfig(unittest.TestCase):
@@ -68,14 +85,8 @@ def test_nonexist(self):
6885

6986

7087
class TestInjectPlugin(unittest.TestCase):
71-
class Stream(StringIO):
72-
name = 'mike-mkdocs.yml'
73-
74-
def close(self):
75-
pass
76-
7788
def test_no_plugins(self):
78-
out = self.Stream()
89+
out = Stream('mike-mkdocs.yml')
7990
cfg = '{}'
8091
with mock.patch('builtins.open', mock.mock_open(read_data=cfg)), \
8192
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
@@ -89,7 +100,7 @@ def test_no_plugins(self):
89100
self.assertEqual(newcfg, {'plugins': ['mike', 'search']})
90101

91102
def test_other_plugins(self):
92-
out = self.Stream()
103+
out = Stream('mike-mkdocs.yml')
93104
cfg = 'plugins:\n - foo\n - bar:\n option: true'
94105
with mock.patch('builtins.open', mock.mock_open(read_data=cfg)), \
95106
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
@@ -101,11 +112,31 @@ def test_other_plugins(self):
101112
mremove.assert_called_once()
102113

103114
self.assertEqual(newcfg, {'plugins': [
104-
'mike', 'foo', {'bar': {'option': True}}
115+
'mike', 'foo', {'bar': {'option': True}},
105116
]})
106117

118+
def test_other_plugins_dict(self):
119+
out = Stream('mike-mkdocs.yml')
120+
cfg = 'plugins:\n foo: {}\n bar:\n option: true'
121+
with mock.patch('builtins.open', mock.mock_open(read_data=cfg)), \
122+
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
123+
return_value=out), \
124+
mock.patch('os.remove') as mremove: # noqa
125+
with mkdocs_utils.inject_plugin('mkdocs.yml') as f:
126+
self.assertEqual(f, out.name)
127+
newcfg = yaml.load(out.getvalue(), Loader=yaml.Loader)
128+
mremove.assert_called_once()
129+
130+
self.assertEqual(newcfg, {'plugins': {
131+
'mike': {}, 'foo': {}, 'bar': {'option': True},
132+
}})
133+
self.assertEqual(
134+
list(newcfg['plugins'].items()),
135+
[('mike', {}), ('foo', {}), ('bar', {'option': True})]
136+
)
137+
107138
def test_mike_plugin(self):
108-
out = self.Stream()
139+
out = Stream('mike-mkdocs.yml')
109140
cfg = 'plugins:\n - mike'
110141
with mock.patch('builtins.open', mock.mock_open(read_data=cfg)), \
111142
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
@@ -117,7 +148,7 @@ def test_mike_plugin(self):
117148
mremove.assert_not_called()
118149

119150
def test_mike_plugin_options(self):
120-
out = self.Stream()
151+
out = Stream('mike-mkdocs.yml')
121152
cfg = 'plugins:\n - mike:\n option: true'
122153
with mock.patch('builtins.open', mock.mock_open(read_data=cfg)), \
123154
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
@@ -128,6 +159,29 @@ def test_mike_plugin_options(self):
128159
self.assertEqual(out.getvalue(), '')
129160
mremove.assert_not_called()
130161

162+
def test_inherit(self):
163+
out = Stream('mike-mkdocs.yml')
164+
main_cfg = 'INHERIT: mkdocs-base.yml\nplugins:\n foo: {}\n'
165+
base_cfg = 'plugins:\n bar: {}\n'
166+
files = {'mkdocs.yml': main_cfg, 'mkdocs-base.yml': base_cfg}
167+
with mock.patch('builtins.open', mock_open_files(files)), \
168+
mock.patch('mike.mkdocs_utils.NamedTemporaryFile',
169+
return_value=out), \
170+
mock.patch('os.path.exists', return_value=True), \
171+
mock.patch('os.remove') as mremove: # noqa
172+
with mkdocs_utils.inject_plugin('mkdocs.yml') as f:
173+
self.assertEqual(f, 'mike-mkdocs.yml')
174+
newcfg = yaml.load(out.getvalue(), Loader=yaml.Loader)
175+
mremove.assert_called_once()
176+
177+
self.assertEqual(newcfg, {'plugins': {
178+
'mike': {}, 'bar': {}, 'foo': {},
179+
}})
180+
self.assertEqual(
181+
list(newcfg['plugins'].items()),
182+
[('mike', {}), ('bar', {}), ('foo', {})]
183+
)
184+
131185

132186
class TestBuild(unittest.TestCase):
133187
def test_build(self):

0 commit comments

Comments
 (0)