Skip to content

Commit

Permalink
Add on_post_build() function to macros' code
Browse files Browse the repository at this point in the history
  - Added documentation, under "Advanced usage"
  • Loading branch information
Laurent Franceschetti committed Nov 23, 2020
1 parent 974b87e commit 4fcd265
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 28 deletions.
86 changes: 59 additions & 27 deletions mkdocs_macros/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import yaml
from jinja2 import Environment, FileSystemLoader
from mkdocs.plugins import BasePlugin
from mkdocs.config import config_options
from mkdocs.config.config_options import Type as PluginType


from .util import trace, debug, update, SuperDict, import_local_module, format_chatter, LOG
from .context import define_env

Expand Down Expand Up @@ -64,6 +66,35 @@ class MacrosPlugin(BasePlugin):
)


def start_chatting(self, prefix:str, color:str='yellow'):
"Generate a chatter function (trace for macros)"
def chatter(*args):
"""
Defines a tracer for the Verbose mode, to be used in macros.
If `verbose: true` in the YAML config file (under macros plugin),
it will start "chattering"
(talking a lot and in a friendly way,
about mostly unimportant things).
Otherwise, it will remain silent.
If you change the `verbose` while the local server is activated,
(`mkdocs server`) this should be instantly reflected.
Usage:
-----
chatter = env.make_chatter('MY_MODULE_NAME')
chatter("This is a dull debug message.")
Will result in:
INFO - [macros - Simple module] - This is a dull info message.
"""
if self.config['verbose']:
LOG.info(format_chatter(*args, prefix=prefix, color=color))

return chatter


# ------------------------------------------------
# These properties are available in the env object
# ------------------------------------------------
Expand Down Expand Up @@ -168,6 +199,19 @@ def reverse(x):
return v


@property
def post_build_functions(self):
"""
List of post build functions contained in modules.
These are deferred to the on_post_build() event.
"""
try:
return self._post_build_functions
except AttributeError:
raise AttributeError("You have called post_build_functions property "
"too early. Does not exist yet !")


# ----------------------------------
# load elements
# ----------------------------------
Expand Down Expand Up @@ -216,6 +260,8 @@ def foobar(x):
...
"""
self._post_build_functions = []

# installed modules (as in pip list)
modules = self.config['modules']
if modules:
Expand Down Expand Up @@ -252,6 +298,12 @@ def foobar(x):
"module '%s'. Prefer the define_env() function "
"(see documentation)!" % local_module_name)
function_found = True
if hasattr(module, 'on_post_build'):
# append the module to the list of functions
# `on_post_build_functions`
# NOTE: each of these functions requires
# self (the environment).
self._post_build_functions.append(module.on_post_build)
if not function_found:
raise NameError("No valid function found in module '%s'" %
local_module_name)
Expand Down Expand Up @@ -457,30 +509,10 @@ def on_page_markdown(self, markdown, page, config,
self.variables["page"] = page
return self.render(markdown)

def start_chatting(self, prefix:str, color:str='yellow'):
"Generate a chatter function"
def chatter(*args):
"""
Defines a tracer for the Verbose mode, to be used in macros.
If `verbose: true` in the YAML config file (under macros plugin),
it will start "chattering"
(talking a lot and in a friendly way, about mostly unimportant things).
Otherwise, it will remain silent.
If you change the `verbose` while the local server is activated,
(`mkdocs server`) this should be instantly reflected.
Usage:
-----
chatter = env.make_chatter('MY_MODULE_NAME')
chatter("This is a dull debug message.")
Will result in:
INFO - [macros - Simple module] - This is a dull info message.
"""
if self.config['verbose']:
LOG.info(format_chatter(*args, prefix=prefix, color=color))

return chatter

def on_post_build(self, config: config_options.Config):
"""
Hook for post build actions, typically adding
raw files to the setup.
"""
for func in self.post_build_functions:
func(self)
10 changes: 9 additions & 1 deletion test/module/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,12 @@ def button(label, url):
return HTML % (url, label)


env.variables.special_docs_dir = env.variables.config['docs_dir']
env.variables.special_docs_dir = env.variables.config['docs_dir']



def on_post_build(env):
"Post build action"
# activate trace
chatter = env.start_chatting("Simple module (post-build)")
chatter("This means `on_post_build(env)` works")
91 changes: 91 additions & 0 deletions webdoc/docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,94 @@ You may, of course, chose the combination that best suits your needs.
of trouble, please do not expect help from the maintainers of this
plugin.

## Adding post-build files to the HTML website

_From version 0.5_

### Use case

Sometimes, you want your Python code to add some files to the HTML website that
MkDocs is producing.

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']`.

!!! Note "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.


### Solution: 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).

```Python
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.

!!! Warning "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.

!!! Note "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.


0 comments on commit 4fcd265

Please sign in to comment.