plugins.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. """
  2. Implements the plugin API for MkDocs.
  3. """
  4. import pkg_resources
  5. import logging
  6. from collections import OrderedDict
  7. from mkdocs.config.base import Config
  8. log = logging.getLogger('mkdocs.plugins')
  9. EVENTS = (
  10. 'config', 'pre_build', 'files', 'nav', 'env', 'pre_template', 'template_context',
  11. 'post_template', 'pre_page', 'page_read_source', 'page_markdown',
  12. 'page_content', 'page_context', 'post_page', 'post_build', 'serve'
  13. )
  14. def get_plugins():
  15. """ Return a dict of all installed Plugins by name. """
  16. plugins = pkg_resources.iter_entry_points(group='mkdocs.plugins')
  17. return {plugin.name: plugin for plugin in plugins}
  18. class BasePlugin:
  19. """
  20. Plugin base class.
  21. All plugins should subclass this class.
  22. """
  23. config_scheme = ()
  24. config = {}
  25. def load_config(self, options, config_file_path=None):
  26. """ Load config from a dict of options. Returns a tuple of (errors, warnings)."""
  27. self.config = Config(schema=self.config_scheme, config_file_path=config_file_path)
  28. self.config.load_dict(options)
  29. return self.config.validate()
  30. class PluginCollection(OrderedDict):
  31. """
  32. A collection of plugins.
  33. In addition to being a dict of Plugin instances, each event method is registered
  34. upon being added. All registered methods for a given event can then be run in order
  35. by calling `run_event`.
  36. """
  37. def __init__(self, *args, **kwargs):
  38. super().__init__(*args, **kwargs)
  39. self.events = {x: [] for x in EVENTS}
  40. def _register_event(self, event_name, method):
  41. """ Register a method for an event. """
  42. self.events[event_name].append(method)
  43. def __setitem__(self, key, value, **kwargs):
  44. if not isinstance(value, BasePlugin):
  45. raise TypeError(
  46. '{0}.{1} only accepts values which are instances of {2}.{3} '
  47. 'sublcasses'.format(self.__module__, self.__name__,
  48. BasePlugin.__module__, BasePlugin.__name__))
  49. super().__setitem__(key, value, **kwargs)
  50. # Register all of the event methods defined for this Plugin.
  51. for event_name in (x for x in dir(value) if x.startswith('on_')):
  52. method = getattr(value, event_name)
  53. if callable(method):
  54. self._register_event(event_name[3:], method)
  55. def run_event(self, name, item=None, **kwargs):
  56. """
  57. Run all registered methods of an event.
  58. `item` is the object to be modified or replaced and returned by the event method.
  59. If it isn't given the event method creates a new object to be returned.
  60. All other keywords are variables for context, but would not generally
  61. be modified by the event method.
  62. """
  63. pass_item = item is not None
  64. for method in self.events[name]:
  65. if pass_item:
  66. result = method(item, **kwargs)
  67. else:
  68. result = method(**kwargs)
  69. # keep item if method returned `None`
  70. if result is not None:
  71. item = result
  72. return item