Skip to content

Commit 7c011cf

Browse files
authored
Merge pull request #15 from ProperDocs/plugins
Support running existing mkdocs plugins and themes
2 parents 36407a3 + ce38e71 commit 7c011cf

File tree

10 files changed

+80
-16
lines changed

10 files changed

+80
-16
lines changed

docs/dev-guide/themes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ this can be used directly by prepending it to a local relative URL, it is best
315315
to use the [url](#url) template filter, which is smarter about how it applies
316316
`base_url`.
317317

318-
#### mkdocs_version
318+
#### properdocs_version
319319

320320
Contains the current ProperDocs version.
321321

properdocs/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import click
1414

15+
from properdocs import replacement # noqa: F401
1516
from properdocs import __version__, config, utils
1617

1718
if sys.platform.startswith("win"):

properdocs/commands/build.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ def get_context(
5151
base_url=base_url,
5252
extra_css=extra_css,
5353
extra_javascript=extra_javascript,
54-
mkdocs_version=properdocs.__version__,
54+
properdocs_version=properdocs.__version__,
55+
mkdocs_version=f"ProperDocs {properdocs.__version__}",
5556
build_date_utc=utils.get_build_datetime(),
5657
config=config,
5758
page=page,

properdocs/config/defaults.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ def load_file(self, config_file: IO) -> None:
213213
self.load_dict(yaml_load(config_file, loader))
214214

215215

216+
MkDocsConfig = ProperDocsConfig # Legacy alias
217+
218+
216219
def get_schema() -> base.PlainConfigSchema:
217220
"""Soft-deprecated, do not use."""
218221
return ProperDocsConfig._schema

properdocs/exceptions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class ProperDocsException(ClickException):
1010
"""
1111

1212

13+
MkDocsException = ProperDocsException # Legacy alias
14+
15+
1316
class Abort(ProperDocsException, SystemExit):
1417
"""Abort the build."""
1518

properdocs/plugins.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,18 @@
4545

4646
def get_plugins() -> dict[str, EntryPoint]:
4747
"""Return a dict of all installed Plugins as {name: EntryPoint}."""
48-
plugins = entry_points(group='properdocs.plugins')
49-
50-
# Allow third-party plugins to override core plugins
51-
pluginmap = {}
52-
for plugin in plugins:
53-
if plugin.name in pluginmap and plugin.value.startswith("properdocs.contrib."):
54-
continue
55-
56-
pluginmap[plugin.name] = plugin
57-
58-
return pluginmap
48+
pluginmaps = {'properdocs': {}, 'mkdocs': {}}
49+
50+
for prefix in pluginmaps:
51+
for plugin in entry_points(group=f'{prefix}.plugins'):
52+
if getattr(plugin, 'value', '').startswith('mkdocs.'):
53+
continue
54+
# Allow third-party plugins to override core plugins
55+
if plugin.name in pluginmaps[prefix] and plugin.value.startswith(f"{prefix}.contrib."):
56+
continue
57+
pluginmaps[prefix][plugin.name] = plugin
58+
59+
return pluginmaps['mkdocs'] | pluginmaps['properdocs']
5960

6061

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

properdocs/replacement.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""After this file is imported, all mkdocs.* imports get redirected to properdocs.* imports."""
2+
3+
import importlib.abc
4+
import importlib.util
5+
import sys
6+
7+
8+
class _AliasLoader(importlib.abc.Loader):
9+
"""Loads the module with the given name and replaces the passed spec's module."""
10+
11+
def __init__(self, realname):
12+
self.realname = realname
13+
14+
def create_module(self, spec):
15+
module = importlib.import_module(self.realname)
16+
sys.modules[spec.name] = module
17+
return module
18+
19+
def exec_module(self, module):
20+
pass
21+
22+
23+
class _AliasFinder:
24+
"""When searching for any mkdocs.* module, find the corresponding properdocs.* module instead."""
25+
26+
def find_spec(self, fullname, path, target=None):
27+
if fullname.startswith("mkdocs."):
28+
realname = "properdocs." + fullname.removeprefix("mkdocs.")
29+
spec = importlib.util.find_spec(realname)
30+
if spec is None:
31+
raise ImportError(f"No module named {realname!r}")
32+
return importlib.util.spec_from_loader(
33+
fullname,
34+
_AliasLoader(realname),
35+
is_package=spec.submodule_search_locations is not None,
36+
)
37+
return None
38+
39+
40+
sys.meta_path.insert(0, _AliasFinder())
41+
# Plus, handle the topmost module directly and without waiting for it to be requested.
42+
sys.modules['mkdocs'] = sys.modules['properdocs']

properdocs/tests/config/config_options_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,7 @@ class ThemePlugin2(BasePlugin[_FakePluginConfig]):
19391939
class FakeEntryPoint:
19401940
def __init__(self, name, cls):
19411941
self.name = name
1942+
self.value = 'properdocs'
19421943
self.cls = cls
19431944

19441945
def load(self):

properdocs/utils/__init__.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,21 @@ def get_theme_dir(name: str) -> str:
262262
@functools.lru_cache(maxsize=None)
263263
def get_themes() -> dict[str, EntryPoint]:
264264
"""Return a dict of all installed themes as {name: EntryPoint}."""
265-
themes: dict[str, EntryPoint] = {}
266-
eps: dict[EntryPoint, None] = dict.fromkeys(entry_points(group='properdocs.themes'))
267-
builtins = {ep.name for ep in eps if ep.dist is not None and ep.dist.name == 'properdocs'}
265+
# Ordered set of preferred entry points.
266+
eps: dict[EntryPoint, None] = {}
267+
builtins: set[str] = set()
268+
269+
for ep in entry_points(group='mkdocs.themes'):
270+
if ep.dist is not None and ep.dist.name != 'mkdocs':
271+
eps[ep] = None
272+
# These will get preference because they are later in the sequence:
273+
for ep in entry_points(group='properdocs.themes'):
274+
if ep.dist is not None:
275+
eps[ep] = None
276+
if ep.dist.name == 'properdocs':
277+
builtins.add(ep.name)
268278

279+
themes: dict[str, EntryPoint] = {}
269280
for theme in eps:
270281
assert theme.dist is not None
271282

properdocs/utils/templates.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class TemplateContext(TypedDict):
2828
base_url: str
2929
extra_css: Sequence[str] # Do not use, prefer `config.extra_css`.
3030
extra_javascript: Sequence[str] # Do not use, prefer `config.extra_javascript`.
31+
properdocs_version: str
3132
mkdocs_version: str
3233
build_date_utc: datetime.datetime
3334
config: ProperDocsConfig

0 commit comments

Comments
 (0)