Mkdocs-Macros
A plugin/mini-framework for unleashing the power of Mkdocs static website generator, with the use of variables and macros.
A mini-framework
Mkdocs-Macros is more than a "plugin". It is a mini-framework developed with one goal in mind:
To enhance mkdocs with the macro and automation capabilities available to a wiki engine. 1
For more information, see Why this project?
Getting started
Definition
Mkdocs-Macros is a plugin/mini-framework that
makes it easy for contributors
of an MkDocs website to produce richer and more
beautiful Markdown pages (with the .md
extension). It can do two things:
- Transforming the markdown pages
into Jinja2 templates
that:
- Use environment or custom variables,
- Call pre-defined or custom macros (functions, generally defined in a Python module),
- Exploit standard or custom filters
- Replacing MkDocs plugins for a wide range of tasks: e.g. manipulating the navigation, adding files after the html pages have already been generated etc.
The capabilities of mkdocs-macros-plugin are so wide that it can be called a "mini-framework".
Mkdocs-Macros is very easy to use out of the box, even for small documentation projects: it provides data about the platform, the git repository (if any), etc.
Mkdocs-Macros is not for HTML templates
The Jinja2 engine used for MkDocs-Macros is for Markdown pages distinct from the one used by MkDocs for HTML templates. For more information and tips, see the paragraph on HTML templates.
Simple Example
In this example, the markdown page (index.md
)
contains a variable unit price
:
The unit price of our product is {{ unit_price }} EUR.
In the config file (mkdocs.yml
), define the unit price
in the extra
section:
extra:
unit_price: 10
With mkdocs serve
the html page is rendered (translated) into markdown as:
The unit price of our product is 10 EUR.
This is the diagram:
flowchart LR
config["Mkdocs config file\n(extra section)\nunit_price: 10"] --> variable
variable["Variable in the page\n{{ unit_price }}"] --> renderer
renderer(("Template Renderer")) --> text
text("Markdown text\n10")
MkDocs will then proceed, as usual, to render the Markdown text as HTML.
Full Example
This example in the markdown page uses a variable and a macro (function):
The unit price of our product is {{ unit_price }} EUR.
Taking the standard discount into account,
the sale price of {{ units }} units is {{ price(unit_price, units) }} EUR.
As in the simple example above, define the unit price
in the extra
section of the config file:
extra:
unit_price: 10
units: 50
Then define a price()
function in the module (main.py
),
in the same directory as the config file:
def define_env(env):
"""
This is the hook for the variables, macros and filters.
"""
@env.macro
def price(unit_price, no):
"Calculate price"
return unit_price * no
This will translate into the final markdown as:
The unit price of our product A is 10.00 EUR.
Taking the standard discount into account,
the sale price of 50 units is 500.00 EUR.
This is the diagram:
flowchart LR
config["Mkdocs config file\n(extra section)\nunit_price: 10\nunits: 50"] --> macro
module["Python module\n(main.py)\ndef unit_price(...):"] --> macro
macro["Macro in the page\n{{ price(unit_price, units) }}"] --> renderer
renderer(("Template Renderer")) --> text
text("Markdown text\n500")
It is possible to use the wide range of facilities provided by Jinja2 templates.
Variables
Regular variables are loaded with each markdown page which is being rendered.
With Mkdocs-Macros there are five different ways to define variables:
flowchart LR
subgraph "MkDocs-Macros"
variable["Variable in the page\n{{ unit_price }}"] --> renderer
renderer(("Template Renderer")) --> text
text("Markdown text\n10")
end
subgraph Global
config["1a. Mkdocs config file\n(extra section)\nunit_price: 10"] --> variable
external["1b. External yaml file\nunit_price: 10"] --> variable
module["1c. In Python module\n(main.py)\nunit_price = 10"] --> variable
end
subgraph "Local (page-level)"
header["2a. YAML header"\nunit_price: 10] --> variable
page["2b. In the text\n{%set unit_price = 10 %}"] --> variable
end
- Global, i.e. for the whole documentation project:
- (for designers of the website): in the
mkdocs.yml
file, under theextra
heading - (for contributors): in external yaml definition files
- (for programmers): in a module (Python), by adding them to a dictionary
- (for designers of the website): in the
- Local, i.e. in each Markdown page (for contributors):
- in the YAML header.
- in the text of the page, with a
{%set variable = value %}
statement.
Create Your Own Macros and Filters
Instead of creating countless new plugins, programmers can define their macros and filters.
Macros are functions usable in a markdown page, which are exported from a Python module. Most of them produce text to be displayed.
Filters are special functions that operate on the result of a function, for formatting purposes.
Getting Started with Macros
Need a function to display some repetitive markdown, or environment information?
If you are a Python programmer, go ahead and create your own macros and filters in Python!
It's actually much, much easier than writing a VBA function for Excel...
Create a main.py
file in the top directory of your mkdocs
project and add this call:
import ...
def define_env(env):
"Hook function"
@env.macro
def mymacro(...)
...
return some_string
You can insert a call in any markdown page of your project:
{{ mymacro(...) }}
Restart your mkdocs server.
Et voilà !
Producing HTML
The result of a macro can also be HTML code: this makes macros especially useful to make custom extensions to the syntax of markdown, such as buttons, calls to email, embedding YouTube videos, etc.
Boosting large or complex documentation projects
mkdocs-macros was also developed with large or complex projects in mind.
You can include separate configurations files, import Jinja2 macro definitions, add "post-production" elements to your website, etc.
You can go all the way as to pre-package modules into pluglets that can be installed as Python packages.
Note
By using mkdocs-macros, you can cut down the number of plugins required for your documentation project.
In a wide range of cases, writing your own module with macros (Python functions for a single website), could save the effort of developing new plugins for mkdocs.
Installation
Prerequisites
- Python version > 3.5
- MkDocs version >= 1.0 (it should work > 0.17 (it should be compatible with post 1.0 versions)
Standard installation
pip install mkdocs-macros-plugin
If you wish to also run the tests or rebuild the project's documentation:
pip install 'mkdocs-macros-plugin[test]'
"Manual installation"
To install the package, download the code from its github page and run:
python setup.py install
Declaration of the macros plugin
Declare the plugin in the the file mkdocs.yml
:
plugins:
- search
- macros
Warning
If you are creating the plugins
entry in your config file,
you should also insert a line for the search
plugin.
In the absence of the plugins
entry,
MkDocs enables search
by default.
But when it is present, then you MUST declare it explicitly if you
want to use it.
Check that it works
Tip
The recommended way to check that the plugin works properly is to add the
following command in one of the pages of your site (let's say info.md
):
{{ macros_info() }}
In the terminal, restart the environment:
> mkdocs serve
INFO - Building documentation...
[macros] Macros arguments: {'module_name': 'main', 'include_yaml': [], 'j2_block_start_string': '', 'j2_block_end_string': '', 'j2_variable_start_string': '', 'j2_variable_end_string': ''}
Within the browser (e.g. http://127.0.0.1:8000/info), you should see a description of the plugins environment:
If you see it that information, you should be all set.
Give a good look at the General List, since it gives you an overview of what you can do out of the box with the macros plugin.
The other parts give you more detailed information.
Configuration of the plugin
Here are all the possible arguments in the plugin
section
of the MkDocs' config file:
Argument | Default | Description |
---|---|---|
render_by_default |
true |
Render macros on all pages by default. If set to false, sets an opt-in mode where only pages marked with render_macros: true in header will be displayed. |
module_name |
main | Name of the Python module containing macros, filters and variables. Indicate the file or directory, without extension; you may specify a path (e.g. include/module ). If no main module is available, it is ignored. |
modules |
[] |
List of pluglets to be added to mkdocs-macros (preinstalled Python modules that can be listed by pip list ). |
include_dir |
Directory for including external files | |
include_yaml |
[] |
List of yaml files or key: filename pairs to be included |
j2_block_start_string |
Non-standard Jinja2 marker for start of block | |
j2_block_end_string |
Non-standard Jinja2 marker for end of block | |
j2_variable_start_string |
Non-standard Jinja2 marker for start of variable | |
j2_variable_end_string |
Non-standard Jinja2 marker for end of variable | |
j2_comment_start_string |
Non-standard Jinja2 marker for start of comment | |
j2_comment_end_string |
Non-standard Jinja2 marker for end of comment | |
on_error_fail |
false |
Make the building process fail in case of an error in macro rendering (this is useful when the website is rebuilt automatically and errors must be detected.) |
on_undefined |
keep | Behavior of the macros renderer in case of an undefined variable in a page. By default, it leaves the Jinja2 statement untouched (e.g. {{ foo }} will appear as such in the page.) Use the value 'strict' to make it fail. |
verbose |
false |
Print debug (more detailed) statements in the console. |
For example:
plugins:
- search
- macros:
module_name: mymodule
include_dir: include
on_error_fail: true
on_undefined: strict
-
With reference to existing wiki engines such as Dokuwiki or Atlassian Confluence. ↩