/
plugins.py
108 lines (78 loc) · 3.29 KB
/
plugins.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""
Implements the plugin API for MkDocs.
"""
import logging
import importlib_metadata
from collections import OrderedDict
from mkdocs.config.base import Config
log = logging.getLogger('mkdocs.plugins')
EVENTS = (
'config', 'pre_build', 'files', 'nav', 'env', 'pre_template', 'template_context',
'post_template', 'pre_page', 'page_read_source', 'page_markdown',
'page_content', 'page_context', 'post_page', 'post_build', 'serve', 'build_error'
)
def get_plugins():
""" Return a dict of all installed Plugins as {name: EntryPoint}. """
plugins = importlib_metadata.entry_points(group='mkdocs.plugins')
# Allow third-party plugins to override core plugins
pluginmap = {}
for plugin in plugins:
if plugin.name in pluginmap and "mkdocs.contrib" in plugin.value:
continue
pluginmap[plugin.name] = plugin
return pluginmap
class BasePlugin:
"""
Plugin base class.
All plugins should subclass this class.
"""
config_scheme = ()
config = {}
def load_config(self, options, config_file_path=None):
""" Load config from a dict of options. Returns a tuple of (errors, warnings)."""
self.config = Config(schema=self.config_scheme, config_file_path=config_file_path)
self.config.load_dict(options)
return self.config.validate()
class PluginCollection(OrderedDict):
"""
A collection of plugins.
In addition to being a dict of Plugin instances, each event method is registered
upon being added. All registered methods for a given event can then be run in order
by calling `run_event`.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.events = {x: [] for x in EVENTS}
def _register_event(self, event_name, method):
""" Register a method for an event. """
self.events[event_name].append(method)
def __setitem__(self, key, value, **kwargs):
if not isinstance(value, BasePlugin):
raise TypeError(
'{}.{} only accepts values which are instances of {}.{} '
'sublcasses'.format(self.__module__, self.__name__,
BasePlugin.__module__, BasePlugin.__name__))
super().__setitem__(key, value, **kwargs)
# Register all of the event methods defined for this Plugin.
for event_name in (x for x in dir(value) if x.startswith('on_')):
method = getattr(value, event_name)
if callable(method):
self._register_event(event_name[3:], method)
def run_event(self, name, item=None, **kwargs):
"""
Run all registered methods of an event.
`item` is the object to be modified or replaced and returned by the event method.
If it isn't given the event method creates a new object to be returned.
All other keywords are variables for context, but would not generally
be modified by the event method.
"""
pass_item = item is not None
for method in self.events[name]:
if pass_item:
result = method(item, **kwargs)
else:
result = method(**kwargs)
# keep item if method returned `None`
if result is not None:
item = result
return item