Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

Use Case Overview

I'm developing a plugin system where one plugin, referred to as a Visitor, needs to interact with other plugins during the build/serve process. These other plugins, referred to as Visitable, should be able to opt into this interaction by declaring a custom event that the Visitor can trigger.

Ideal Implementation

In an ideal scenario, the implementation might look like this:

class Visitor(BasePlugin):
   def on_pre_build(self, config, **kwargs):
      self.plugins.run_event('visit', data={})

class Visitable(BasePlugin):
   def on_visit(self, data: dict):
      print("I am currently visited")

Here, the Visitor plugin triggers a custom event called visit, and any Visitable plugin that defines an on_visit method would respond to this event.

Current Limitations

However, I've encountered a limitation in MkDocs that prevents this pattern from working as intended:

  1. Static Event Registration: MkDocs populates the config.plugins.events dictionary when the configuration is first created. Since plugins are loaded at this stage, no plugin can modify the events dictionary before MkDocs finalizes the plugin setup. This means custom events like visit cannot be dynamically added later in the process.

  2. Automatic Event Registration: Methods in a BasePlugin that start with on_ are automatically registered as events by MkDocs. However, since custom events are not pre-defined in the events dictionary, any attempt to register them will result in failure. Essentially, all on_ methods are reserved for predefined MkDocs events, leaving no room for custom ones.

To work around this, I tried manually registering a custom event:

class Foo(BasePlugin):
    def on_config(self, config, **kwargs):
        config.plugins.events['visit'] = []
        config.plugins._register_event('visit', self._on_visit)
        return config

    def _on_visit(self, **kwargs):
        print("I am currently visited")

While this approach technically works, it feels more like a hack than a robust solution. It’s not ideal for long-term maintainability or for broader plugin development.

Proposal

I propose that MkDocs should allow custom events to be automatically registered by any plugin. To avoid potential conflicts with MkDocs' own event system, these custom events could use a unique prefix, such as on_custom_<event>, making it clear that they are user-defined.

Real-World Application

In my specific use case, I'm developing a plugin that generates a PDF book from MkDocs documentation. This plugin creates a LaTeX project that can be compiled into a PDF. I opted not to use existing Markdown-to-PDF converters like Pandoc because I wanted the system to be highly extensible, allowing other plugins to provide custom transformations for LaTeX.

For example, if a latex plugin is loaded, it could automatically trigger events such as on_latex_page, enabling other plugins to perform their tasks as they would for the HTML output. However, without support for custom events, this level of extensibility becomes difficult to achieve.

Conclusion

While my use case is somewhat specialized, I believe that enabling custom events would benefit the MkDocs plugin ecosystem as a whole. It would allow for more flexible and modular plugin designs without requiring workarounds or hacks.

What are your thoughts on this proposal?

You must be logged in to vote

Replies: 4 comments

Comment options

Awesome use-case, great write-up ☺️

I personally find this proposal very interesting, and am actually considering implementing it in Griffe since I based its extension system on the same event/hook approach as MkDocs.

The ability to isolate the custom events from the main ones is very important IMO, to prevent future conflicts (just like top-level keys in pyproject.toml are reserved, and tools must put their configuration under [tool.NAME]). I'm not sure yet what's the best way to "namespace" events. I believe that two different plugins should be able to register events with the same name distinctly, so that other plugins can hook into both (once again, just like tools don't just write directly under [tool], but under [tool.NAMESPACE] in pyproject.toml).

You must be logged in to vote
0 replies
Comment options

great write-up

Yep nicely done.

I'll convert this into a discussion and answer there.

You must be logged in to vote
0 replies
Comment options

So... counter-intuitively this is actually a really good example of why I'm looking towards a future iteration of MkDocs that doesn't require or support plugins in order to customise the page generation.

My sense is that a revisited approach to templating, themes, and styling would remove almost all the requirements for custom Python code through plugins. There's also clear a smaller subset of cases that do strictly require a Python code-approach. I believe these could be supported through customising mkdocs at the core level, rather than injecting plugins.

I believe this'll be worth pursing in order to avoid the complexity feedback loop, and move instead towards a more design-constrained but equally capable system.

(I recognise that I'm not yet `demonstrating what I'm working towards here, figured it was worth keeping in touch tho since the discussion relates to this...)

You must be logged in to vote
0 replies
Comment options

I may not be the right person to say this, but as an MkDocs user, I believe that removing the plugin system would break the MkDocs ecosystem.

I understand that MkDocs itself is going to be redesigned, but MkDocs 2 is supposed to be compatible with the current version, which relies on plugins. Moreover, many projects and custom themes depend on plugins . For example, mkdocs-materialx uses plugins to add functionality to the theme, and many plugin authors have filled gaps by providing features that are not available in MkDocs core.

As I understand it, plugins were meant to allow users to extend MkDocs and make it more useful by adding the functionality they need without waiting for changes in the core project.

Instead of removing plugins, I think we should focus on improving the existing system—making it more robust, removing constraints, and fixing current issues—because, as it stands, developers often have to compromise due to limitations in the current system.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
💡
Ideas
Labels
None yet
4 participants
Converted from issue

This discussion was converted from issue #3806 on August 20, 2024 16:21.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.