MkDocs 插件
安装、使用和创建 MkDocs 插件的指南
安装插件
在使用插件之前,必须先将其安装到系统中。如果您使用的是 MkDocs 自带的插件,那么您在安装 MkDocs 时就已经安装了它。但是,要安装第三方插件,您需要确定相应的包名称并使用 pip
进行安装。
pip install mkdocs-foo-plugin
警告
安装 MkDocs 插件意味着安装 Python 包并执行作者在其中放入的任何代码。因此,请谨慎行事;没有任何沙盒尝试。
成功安装插件后,即可使用它。只需在配置文件中启用它即可。目录存储库中有一个大型的排名插件列表,您可以安装并使用它们。
使用插件
在构建站点时,plugins
配置选项应该包含一个要使用的插件列表。每个“插件”必须是一个分配给插件的字符串名称(请参阅给定插件的文档以确定其“名称”)。此处列出的插件必须已经安装。
plugins:
- search
某些插件可能提供自己的配置选项。如果要设置任何配置选项,则可以在给定插件支持的任何选项的键值映射(option_name: option value
)中嵌套。请注意,冒号(:
)必须跟随插件名称,然后在新行上,选项名称和值必须缩进并用冒号分隔。如果要为单个插件定义多个选项,则每个选项必须在单独的行上定义。
plugins:
- search:
lang: en
foo: bar
有关给定插件的可用配置选项的信息,请参阅该插件的文档。
有关默认插件列表以及如何覆盖它们的信息,请参阅配置文档。
开发插件
与 MkDocs 一样,插件必须用 Python 编写。通常情况下,每个插件都将作为一个独立的 Python 模块进行分发,尽管可以在同一个模块中定义多个插件。至少,MkDocs 插件必须包含一个BasePlugin子类和一个入口点,该入口点指向它。
BasePlugin
mkdocs.plugins.BasePlugin
的子类应该定义插件的行为。该类通常包含在构建过程中的特定事件中执行的操作以及插件的配置方案。
所有 BasePlugin
子类都包含以下属性
config_scheme
配置验证实例的元组。每个项目必须包含一个两项元组,其中第一项是配置选项的字符串名称,第二项是 mkdocs.config.config_options.BaseConfigOption
或其任何子类的实例。
例如,以下 config_scheme
定义三个配置选项:foo
,它接受一个字符串;bar
,它接受一个整数;baz
,它接受一个布尔值。
class MyPlugin(mkdocs.plugins.BasePlugin):
config_scheme = (
('foo', mkdocs.config.config_options.Type(str, default='a default value')),
('bar', mkdocs.config.config_options.Type(int, default=0)),
('baz', mkdocs.config.config_options.Type(bool, default=True))
)
版本 1.4 中的新功能
通过子类化 Config
来指定配置方案
为了获得类型安全优势,如果您只针对 MkDocs 1.4+,请将配置方案定义为一个类
class MyPluginConfig(mkdocs.config.base.Config):
foo = mkdocs.config.config_options.Type(str, default='a default value')
bar = mkdocs.config.config_options.Type(int, default=0)
baz = mkdocs.config.config_options.Type(bool, default=True)
class MyPlugin(mkdocs.plugins.BasePlugin[MyPluginConfig]):
...
配置定义示例
示例
from mkdocs.config import base, config_options as c
class _ValidationOptions(base.Config):
enabled = c.Type(bool, default=True)
verbose = c.Type(bool, default=False)
skip_checks = c.ListOfItems(c.Choice(('foo', 'bar', 'baz')), default=[])
class MyPluginConfig(base.Config):
definition_file = c.File(exists=True) # required
checksum_file = c.Optional(c.File(exists=True)) # can be None but must exist if specified
validation = c.SubConfig(_ValidationOptions)
从用户的角度来看,SubConfig
类似于 Type(dict)
,只是它也保留了完整的验证能力:您定义所有有效的键以及每个值应该遵守的内容。
而 ListOfItems
类似于 Type(list)
,但同样,我们定义了每个值必须遵守的约束。
这接受以下配置
my_plugin:
definition_file: configs/test.ini # relative to mkdocs.yml
validation:
enabled: !ENV [CI, false]
verbose: true
skip_checks:
- foo
- baz
示例
import numbers
from mkdocs.config import base, config_options as c
class _Rectangle(base.Config):
width = c.Type(numbers.Real) # required
height = c.Type(numbers.Real) # required
class MyPluginConfig(base.Config):
add_rectangles = c.ListOfItems(c.SubConfig(_Rectangle)) # required
在这个例子中,我们定义了一个复杂项目的列表,这是通过将具体的 SubConfig
传递给 ListOfItems
来实现的。
这接受以下配置
my_plugin:
add_rectangles:
- width: 5
height: 7
- width: 12
height: 2
当加载用户的配置时,上述方案将用于验证配置并填充用户未提供的任何设置的默认值。验证类可以是 mkdocs.config.config_options
中提供的任何类,也可以是插件中定义的第三方子类。
用户提供的任何在验证中失败或未在 config_scheme
中定义的设置都会引发 mkdocs.config.base.ValidationError
。
config
插件的配置选项字典,在配置验证完成后由 load_config
方法填充。使用此属性访问用户提供的选项。
def on_pre_build(self, config, **kwargs):
if self.config['baz']:
# implement "baz" functionality here...
版本 1.4 中的新功能
安全的基于属性的访问
为了获得类型安全优势,如果您只针对 MkDocs 1.4+,请将选项作为属性访问
def on_pre_build(self, config, **kwargs):
if self.config.baz:
print(self.config.bar ** 2) # OK, `int ** 2` is valid.
所有 BasePlugin
子类都包含以下方法:
load_config(options)
从选项字典加载配置。返回一个 (errors, warnings)
元组。此方法由 MkDocs 在配置验证期间调用,插件不需要调用它。
on_<event_name>()
定义特定事件行为的可选方法。插件应该在这些方法中定义其行为。将 <event_name>
替换为事件的实际名称。例如,pre_build
事件将在 on_pre_build
方法中定义。
大多数事件接受一个位置参数和各种关键字参数。通常情况下,位置参数会被插件修改(或替换)并返回。如果没有返回任何内容(方法返回 None
),则使用原始的、未修改的对象。关键字参数只是为了提供上下文和/或提供可能用于确定如何修改位置参数的数据。将关键字参数作为 **kwargs
接受是一个好习惯。如果在 MkDocs 的未来版本中向事件提供了其他关键字,则无需更改您的插件。
例如,以下事件将向主题配置添加一个额外的 static_template
class MyPlugin(BasePlugin):
def on_config(self, config, **kwargs):
config['theme'].static_templates.add('my_template.html')
return config
版本 1.4 中的新功能
为了获得类型安全优势,如果您只针对 MkDocs 1.4+,请将配置选项作为属性访问
def on_config(self, config: MkDocsConfig):
config.theme.static_templates.add('my_template.html')
return config
事件
查看所有插件事件之间关系的图表
- 事件本身以黄色显示,带有它们的参数。
- 箭头显示每个事件的参数和输出流。有时它们会被省略。
- 事件按时间顺序从上到下排序。
- 从全局事件到每页事件的分裂处会出现虚线。
- 点击事件的标题跳转到其描述。
一次性事件
一次性事件在每次 mkdocs
调用时运行一次。这些事件与全局事件的不同之处仅在于 mkdocs serve
:全局事件与这些事件不同,它们会运行多次——每次构建运行一次。
on_startup
startup
事件在每次 mkdocs
调用开始时运行一次。
MkDocs 1.4 中的新功能。
on_startup
方法的存在(即使是空的)也会将插件迁移到新的系统,在这个系统中,插件对象在一次 mkdocs serve
中的构建之间保持不变。
请注意,对于初始化变量,__init__
方法仍然是首选。对于初始化每次构建的变量(以及在任何情况下有疑问时),请使用 on_config
事件。
参数
-
command
(Literal['build', 'gh-deploy', 'serve']
) –MkDocs 使用的命令,例如
mkdocs serve
的“serve”。 -
dirty
(bool
) –是否传递了
--dirty
标志。
on_shutdown
shutdown
事件在每次 mkdocs
调用结束时运行一次,在退出之前运行。
此事件仅与 mkdocs serve
的支持相关,否则在单个构建中,它与 on_post_build
无法区分。
MkDocs 1.4 中的新功能。
on_shutdown
方法的存在(即使是空的)也会将插件迁移到新的系统,在这个系统中,插件对象在一次 mkdocs serve
中的构建之间保持不变。
请注意,on_post_build
方法仍然是首选用于清理,只要有可能,因为它更有可能实际触发。on_shutdown
是“尽力而为”,因为它依赖于检测 MkDocs 的正常关闭。
on_serve
serve
事件仅在开发过程中使用 serve
命令时调用。它只运行一次,在第一次构建完成后运行。它传递了 Server
实例,可以在激活它之前对其进行修改。例如,可以将其他文件或目录添加到“监视”文件的列表中,以进行自动重新加载。
参数
-
server
(LiveReloadServer
) –livereload.Server
实例 -
config
(MkDocsConfig
) –全局配置对象
-
builder
(Callable
) –传递给每次调用
server.watch
的可调用对象
返回值
-
LiveReloadServer | None
–livereload.Server
实例
全局事件
全局事件在每次构建时在构建过程的开始或结束时调用一次。在这些事件中进行的任何更改都会对整个站点产生全局影响。
on_config
config
事件是在构建时调用的第一个事件,在加载并验证用户配置后立即运行。应在此处对配置进行任何更改。
参数
-
config
(MkDocsConfig
) –全局配置对象
返回值
-
MkDocsConfig | None
–全局配置对象
on_pre_build
pre_build
事件不会更改任何变量。使用此事件调用预构建脚本。
参数
-
config
(MkDocsConfig
) –全局配置对象
on_files
on_nav
nav
事件在站点导航创建后被调用,可用于更改站点导航。
参数
-
nav
(Navigation
) –全局导航对象
-
config
(MkDocsConfig
) –全局配置对象
-
files
(Files
) –全局文件集合
返回值
-
Navigation | None
–全局导航对象
on_env
on_post_build
post_build
事件不更改任何变量。使用此事件调用构建后脚本。
参数
-
config
(MkDocsConfig
) –全局配置对象
on_build_error
build_error
事件在 MkDocs 在构建过程中捕获任何类型的异常后被调用。使用此事件在 MkDocs 终止之前清理内容。请注意,计划在错误后运行的任何其他事件都将被跳过。有关更多详细信息,请参阅 处理错误。
参数
-
error
(Exception
) –引发的异常
模板事件
模板事件对每个非页面模板调用一次。每个模板事件都会对在 extra_templates 配置设置中定义的每个模板以及主题中定义的任何 static_templates 调用。所有模板事件都在 env 事件之后和任何 页面事件 之前被调用。
on_pre_template
on_template_context
template_context
事件在为主题模板创建上下文后立即被调用,可用于仅更改该特定模板的上下文。
参数
-
context
(TemplateContext
) –模板上下文变量的字典
-
template_name
(str
) –模板的字符串文件名
-
config
(MkDocsConfig
) –全局配置对象
返回值
-
TemplateContext | None
–模板上下文变量的字典
on_post_template
post_template
事件在渲染模板后但写入磁盘之前被调用,可用于更改模板的输出。如果返回空字符串,则跳过模板,不会将任何内容写入磁盘。
参数
-
output_content
(str
) –渲染后的模板的输出,以字符串形式
-
template_name
(str
) –模板的字符串文件名
-
config
(MkDocsConfig
) –全局配置对象
返回值
-
str | None
–渲染后的模板的输出,以字符串形式
页面事件
页面事件对网站中包含的每个 Markdown 页面调用一次。所有页面事件都在 post_template 事件之后和 post_build 事件之前被调用。
on_pre_page
on_page_read_source
已弃用
请使用以下替代方案之一代替此事件
- 从 MkDocs 1.6 开始,请在
on_files
中设置File
的content_bytes
/content_string
。 - 通常(虽然它不是完全的替代方案),
on_page_markdown
可以起到相同的作用。
on_page_read_source
事件可以替换从文件系统读取页面源内容的默认机制。
参数
-
page
(Page
) –mkdocs.structure.pages.Page
实例 -
config
(MkDocsConfig
) –全局配置对象
返回值
-
str | None
–页面的原始源代码,以 unicode 字符串形式。如果返回
None
,则将执行从文件的默认加载。
on_page_markdown
on_page_content
on_page_context
page_context
事件在创建页面的上下文后被调用,可用于仅更改该特定页面的上下文。
参数
-
context
(TemplateContext
) –模板上下文变量的字典
-
page
(Page
) –mkdocs.structure.pages.Page
实例 -
config
(MkDocsConfig
) –全局配置对象
-
nav
(Navigation
) –全局导航对象
返回值
-
TemplateContext | None
–模板上下文变量的字典
on_post_page
post_page
事件在渲染模板后但写入磁盘之前被调用,可用于更改页面的输出。如果返回空字符串,则跳过页面,不会将任何内容写入磁盘。
参数
-
output
(str
) –渲染后的模板的输出,以字符串形式
-
page
(Page
) –mkdocs.structure.pages.Page
实例 -
config
(MkDocsConfig
) –全局配置对象
返回值
-
str | None
–渲染后的模板的输出,以字符串形式
事件优先级
对于每种事件类型,插件的对应方法将按照插件在 plugins
配置 中出现的顺序调用。
从 MkDocs 1.4 开始,插件可以选择为其事件设置优先级值。优先级较高的事件将首先被调用。没有选择优先级的事件的默认值为 0。具有相同优先级的事件按其在配置中出现的顺序排序。
mkdocs.plugins.event_priority(priority: float) -> Callable[[T], T]
一个装饰器,用于为事件处理程序方法设置事件优先级。
推荐的优先级值:100
“first”,50
“early”,0
“default”,-50
“late”,-100
“last”。随着不同的插件发现彼此之间更精确的关系,应该进一步调整这些值。
使用示例
@plugins.event_priority(-100) # Wishing to run this after all other plugins' `on_files` events.
def on_files(self, files, config, **kwargs):
...
MkDocs 1.4 中的新增功能。推荐用于向后兼容的垫片
try:
from mkdocs.plugins import event_priority
except ImportError:
event_priority = lambda priority: lambda f: f # No-op fallback
1.6 版中的新增功能
也可能需要以多个不同优先级注册同一个事件的处理程序。
CombinedEvent
使这成为可能。
mkdocs.plugins.CombinedEvent
基类:Generic[P, T]
一个描述符,允许定义多个事件处理程序并在一个事件的名称下声明它们。
使用示例
@plugins.event_priority(100)
def _on_page_markdown_1(self, markdown: str, **kwargs):
...
@plugins.event_priority(-50)
def _on_page_markdown_2(self, markdown: str, **kwargs):
...
on_page_markdown = plugins.CombinedEvent(_on_page_markdown_1, _on_page_markdown_2)
注意
子方法的名称不能以 on_
开头;相反,它们可以像上面示例中那样以 _on_
或任何其他内容开头。
处理错误
MkDocs 定义了四种错误类型
mkdocs.exceptions.MkDocsException
基类:ClickException
所有 MkDocs 异常都继承自该基类。不应直接引发此异常。应改为引发子类之一。
mkdocs.exceptions.ConfigurationError
当遇到验证错误时,此错误由配置验证引发。插件的 config_scheme 中定义的任何配置选项都应引发此错误。
mkdocs.exceptions.BuildError
此错误可能由 MkDocs 在构建过程中引发。插件不应引发此错误。
mkdocs.exceptions.PluginError
基类:BuildError
mkdocs.exceptions.BuildError
的子类,可由插件事件引发。
意外的未捕获异常将中断构建过程并产生典型的 Python 追溯,这对调试代码很有用。但是,用户通常发现追溯过于繁琐,并且经常会错过有用的错误消息。因此,MkDocs 将捕获上面列出的任何错误,检索错误消息并立即退出,仅显示有用的消息给用户。
因此,您可能希望在插件中捕获任何异常并引发 PluginError
,传入您自己的自定义消息,以便构建过程因有用的消息而中止。
on_build_error 事件将针对任何异常触发。
例如
from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin
class MyPlugin(BasePlugin):
def on_post_page(self, output, page, config, **kwargs):
try:
# some code that could throw a KeyError
...
except KeyError as error:
raise PluginError(f"Failed to find the item by key: '{error}'")
def on_build_error(self, error, **kwargs):
# some code to clean things up
...
在插件中记录
为了确保插件的日志消息符合 MkDocs 的格式和 --verbose
/--debug
标志,请将日志写入 mkdocs.plugins.
命名空间下的记录器。
示例
import logging
log = logging.getLogger(f"mkdocs.plugins.{__name__}")
log.warning("File '%s' not found. Breaks the build if --strict is passed", my_file_name)
log.info("Shown normally")
log.debug("Shown only with `--verbose`")
if log.getEffectiveLevel() <= logging.DEBUG:
log.debug("Very expensive calculation only for debugging: %s", get_my_diagnostics())
log.error()
是另一种通过外观区分的日志记录级别,但在所有其他方面,它的功能与 warning
相同,因此使用它很奇怪。如果插件遇到实际错误,最好通过引发 mkdocs.exceptions.PluginError
来中断构建(这也将记录 ERROR 消息)。
1.5 版中的新增功能
MkDocs 现在提供了一个 get_plugin_logger()
方便函数,它返回一个与上面类似的记录器,该记录器还以插件的名称为前缀。
mkdocs.plugins.get_plugin_logger(name: str) -> PrefixedLogger
返回插件的记录器。
参数
-
name
(str
) –要与
logging.getLogger
一起使用的名称。
返回值
-
PrefixedLogger
–一个配置为在 MkDocs 中良好工作的记录器,在每条消息前面加上插件包名称。
示例
from mkdocs.plugins import get_plugin_logger
log = get_plugin_logger(__name__)
log.info("My plugin message")
入口点
插件需要打包为 Python 库(在 PyPI 上单独分发,独立于 MkDocs),并且每个插件都必须通过 setuptools entry_points
注册为插件。将以下内容添加到您的 setup.py
脚本中
entry_points={
'mkdocs.plugins': [
'pluginname = path.to.some_plugin:SomePluginClass',
]
}
pluginname
将是用户在配置文件中使用的名称,而 path.to.some_plugin:SomePluginClass
将是可导入的插件本身(from path.to.some_plugin import SomePluginClass
),其中 SomePluginClass
是 BasePlugin 的子类,它定义了插件行为。自然,同一个模块中可能存在多个 Plugin 类。只需将每个类定义为单独的入口点即可。
entry_points={
'mkdocs.plugins': [
'featureA = path.to.my_plugins:PluginA',
'featureB = path.to.my_plugins:PluginB'
]
}
请注意,注册插件不会激活它。用户仍然需要通过配置告诉 MkDocs 使用它。
发布插件
您应该将软件包发布到 PyPI,然后将其添加到 目录 以提高可发现性。强烈建议插件根据目录拥有唯一的插件名称(入口点名称)。