Post-production of pages
Introduction
Within mkdocs-macros, we call post-production all actions that follow
the definition of macros, with the define_env(env)
hook.
They are of three types of post-production:
- Modifying markdown pages (either before or after macro rendering)
- Influencing the HTML pages generated by mkdocs
- Performing operations after mkdocs has completely executed
Markdown pages (headers or footers, etc.)
Introduction
From version 0.5.2
From version 1.0.0
The env.raw_markdown
attribute is deprecated. Use env.markdown
instead.
There are specific cases where you want your module code to be able to modify the markdown code of a page, without using macros. One typical example is when you wish to alter the pages generated e.g. by adding footer information at the bottom of each page, or header information at the top of each page, or any other operation..
The proper time to do that, would be before or after the macros (Jinja2 directives) have been processed.
Technical note: a limitation of the macros mechanism
The define_env()
function operates at the time when MkDocs prepares the
configuration of website (the on_config()
event). This is a
global event, i.e. any change made at this point will affect the whole
website.
The limitation is that the define_env()
function is
"aware" of the general configuration, not of the content of single pages.
True,
it allows you to declare macros which will be interpreted later,
for each page (on the on_page()
event). But it won't
allow you to modify pages outside of that mechanism.
To act on such cases that vary with each markdown page (and depend on each page, not on the general configuration), you may use the two hooks in your module, which are executed before the markdown is actually rendered:
on_pre_page_macros(env)
: just before the macros are rendered on a specific page (macros are still present). At this stage you can still insert text that contains macros.on_post_page_macros(env)
: just after the macros are rendered on a specific page (macros have turned into their markdown equivalent). At this stage you can insert
You can access the markdown through the env.markdown
property (string).
Application to headers or footers
From version 1.0.0
on_pre_page_macros(env)
works as shown
only from that version.
For lower versions, in on_post_page_macros(env)
replace env.raw_markdown
with env.markdown
Headers or footers can be added before or after the rendering process. If you add, e.g. a footer before the rendering process, the macros inserted will be interpreted.
You may a add a variable footer after the rendering process, but in that case you must use the Python syntax of the module to calculate the values that go in each page footer.
Availability of env.markdown
env.markdown
is accessible only at this stage of the production
process (page production). It is not
available in the define_env()
hook, which is executed
before the production of the pages has started.
If you wish to add a footer, you have the choice between:
- using macros (before rendering):
def on_pre_page_macros(env):
"""
Actions to be done before macro interpretation,
when the macros have not yet been rendered.
"""
footer = "\n\n{{ page.title }}"
env.markdown += footer
- or doing it programmatically:
def on_post_page_macros(env):
"""
Actions to be done after macro interpretation,
when the macros have been rendered
This will add a (Markdown or HTML) footer -- produced by Python.
"""
footer = "f'\n\n'{env.page.title}"
env.markdown += footer
If you wish to use jinja2 directives, do it it before rendering:
def on_pre_page_macros(env):
"""
Actions to be done before macro interpretation,
"""
footer = "\n\n{% include('glossary.md') %}"
env.markdown += footer
Technical Notes
Time of execution
on_pre_page_macros(env)
and on_post_page_macros(env)
are executed by the on_page_markdown()
event of MkDocs:
- before the rendering the page
- before or after interpretation of the macros, respectively
They operates on all pages, page by page.
Use of global variables
To facilitate the communication within your module
between define_env()
and
on_page_markdown()
you may want to define global variables.
For a refresher on this,
see the summary on W3 Schools.
Content and availability of attributes
the env. markdown
and env.page
attributes are available
only from the point of on_pre_page_macros()
on.
Thay are not available for the define_env(env)
hook.
env.markdown contains the markdown of the page, before rendering
(in on_pre_page_macros()
) or after rendering (in on_post_page_macros()
))
env.page contains notably the following information:
Attribute | Value |
---|---|
title |
title of the page |
abs_url |
the absolute url of the page from the top of the hierarchy |
canonical_url |
the complete url of the page (typically with https://... ) |
markdown |
the whole markdown code (before or after rendering). |
meta |
the meta data dictionary, as updated (typically) from the YAML header. |
env.page is a copy so modifying it won't affect the rendering process (no black magic allowed).
Html pages
Introduction
It is possible to influence to some degree, the production of html files, mostly by acting on templates.
Trickling values into the mkdocs theme
You may want to programmatically add some meta values to a page, which should be forwarded to the HTML theme used by mkdocs (which has its own, distinct Jinja2 engine).
This is called trickling (values are trickled from the mkdocs-macros environment of variables to the html page).
Do not confuse
Note that that the Jinja2 engine that operates on a mkdocs theme's pages is completely distinct from the mkdocs-macros Jinja2 engine, which is used to render macros on Markdown pages. The rendering of template variables occurs at a later stage of the page production process.
For example you'd want to be able to always have a value for this:
<meta name="description" content="{{ page.meta.description }}" />
Normally metadata would be defined in the YAML header of the markdown page:
---
title: my title
description: This is a description
---
Issue
But supposing this was not the case ? Or supposing you want to check or alter that information?
def on_post_page_macros(env):
"""
Actions to be done after macro interpretation,
when the markdown is already generated
"""
# This information will get carried into the HTML template.
env.page.meta['description'] = ...
Post-build files
From version 0.5
Introduction
Sometimes, you want your Python code to add some files to the HTML website that MkDocs has just produced (after MkDoc's usual production workflow).
These could be:
- an extra HTML page
- an additional or updated image
- a RSS feed
- a form processor (written for example in the php language)
- ....
Tip
The logical idea is to add files to the site (HTML) directory,
which is given by env.conf['site_dir']
.
Beware the of the 'disappeared file' trap
One problem will occur if you attempt to add files to the site directory
from within the define_env()
function in your macro module.
The file will be created, but nevertheless it is going to "disappear".
The reason is that the code of define_env()
is executed during the
on_config
event of MkDocs; and you can expect the site directory
to be wiped out later, during the build phase (which produces
the HTML files). So, of course, the files you
just created will be deleted.
Post-Build Actions
The solution to do that, is to perform those additions
as post-build actions (i.e. executed with on_post_build
event).
Here is an example. Suppose you want to add a special file (e.g. HTML).
import os
MY_FILENAME = 'foo.html'
my_HTML = None
def define_env(env):
"Definition of the module"
# put here your HTML content
my_HTML = ......
def on_post_build(env):
"Post-build actions"
site_dir = env.conf['site_dir']
file_path = os.path.join(site_dir, MY_FILENAME)
with open(file_path, 'w') as f:
f.write(my_HTML)
The mkdocs-macros plugin will pick up that function and execute it during
as on on_post_build()
action.
Argument of on_post_build()
In this case, the argument is env
(as for define_env()
);
it is not
config
as in the on_post_build()
method in an MkDocs plugin.
If you want to get the plugin's arguments, you can find them in the
env.conf
dictionary.
Global variables
To facilitate the communication between define_env()
and
on_post_build
you may want to define global variables within
your module (in this example: MY_FILENAME
and my_HTML
).
Warning
Do not forget that any variable assigned for the first time within a function is by default a local variable: its content will be lost once the function is fully executed.
In the example above, my_HTML
must appear in the global definitions;
which is why it was assigned an empty value.