:py:`module_doc_contents` Module documentation contents
:py:`class_doc_contents` Class documentation contents
:py:`data_doc_contents` Data documentation contents
+:py:`hooks_pre_page` Hooks to call before each page gets rendered
+:py:`hooks_post_run` Hooks to call at the very end of the script run
=========================== ===================================================
The :py:`module_doc_contents`, :py:`class_doc_contents` and
'details': "This class is *pretty*."
}
+The :py:`hooks_pre_page` and :py:`hooks_post_run` variables are lists of
+parameter-less functions. Plugins that need to do something before each page
+of output gets rendered (for example, resetting an some internal counter for
+page-wide unique element IDs) or after the whole run is done (for example to
+serialize cached internal state) are supposed to add functions to the list.
+
Registration function for a plugin that needs to query the :py:`OUTPUT` setting
might look like this --- the remaining keyword arguments will collapse into
the :py:`**kwargs` parameter. See code of various m.css plugins for actual
-examples.
+examples. The below example shows registration of a hypothetic HTML validator
+plugin --- it saves the output path from settings and registers a post-run hook
+that validates everything in given output directory.
.. code:: py
…
- def register_mcss(mcss_settings, **kwargs):
+ def _validate_output():
+ validate_all_html_files(output_dir)
+
+ def register_mcss(mcss_settings, hooks_post_run, **kwargs):
global output_dir
output_dir = mcss_settings['OUTPUT']
+ hooks_post_run += [_validate_output]
`External documentation content`_
=================================
self.data_docs: Dict[str, Dict[str, str]] = {}
self.external_data: Set[str] = set()
+ self.hooks_pre_page: List = []
+ self.hooks_post_run: List = []
+
def is_internal_function_name(name: str) -> bool:
"""If the function name is internal.
def render_module(state: State, path, module, env):
logging.debug("generating %s.html", '.'.join(path))
+ # Call all registered page begin hooks
+ for hook in state.hooks_pre_page: hook()
+
url_base = ''
breadcrumb = []
for i in path:
def render_class(state: State, path, class_, env):
logging.debug("generating %s.html", '.'.join(path))
+ # Call all registered page begin hooks
+ for hook in state.hooks_pre_page: hook()
+
url_base = ''
breadcrumb = []
for i in path:
def render_doc(state: State, filename):
logging.debug("parsing docs from %s", filename)
+ # Page begin hooks are called before this in run(), once for all docs since
+ # these functions are not generating any pages
+
# Render the file. The directives should take care of everything, so just
# discard the output afterwards.
with open(filename, 'r') as f: publish_rst(state, f.read())
def render_page(state: State, path, filename, env):
logging.debug("generating %s.html", '.'.join(path))
+ # Call all registered page begin hooks
+ for hook in state.hooks_pre_page: hook()
+
# Render the file
with open(filename, 'r') as f: pub = publish_rst(state, f.read())
jinja_environment=env,
module_doc_contents=state.module_docs,
class_doc_contents=state.class_docs,
- data_doc_contents=state.data_docs)
+ data_doc_contents=state.data_docs,
+ hooks_pre_page=state.hooks_pre_page,
+ hooks_post_run=state.hooks_post_run)
+
+ # Call all registered page begin hooks for the first time
+ for hook in state.hooks_pre_page: hook()
# First process the doc input files so we have all data for rendering
# module pages
logging.debug("copying %s to output", i)
shutil.copy(i, os.path.join(config['OUTPUT'], os.path.basename(i)))
+ # Call all registered finalization hooks for the first time
+ for hook in state.hooks_post_run: hook()
+
if __name__ == '__main__': # pragma: no cover
parser = argparse.ArgumentParser()
parser.add_argument('conf', help="configuration file")
node['classes'] += ['m-transition']
return [node]
-def register_mcss(**kwargs):
+pre_page_call_count = 0
+post_run_call_count = 0
+
+def _pre_page():
+ global pre_page_call_count
+ pre_page_call_count = pre_page_call_count + 1
+
+def _post_run():
+ global post_run_call_count
+ post_run_call_count = post_run_call_count + 1
+
+def register_mcss(hooks_pre_page, hooks_post_run, **kwargs):
+ hooks_pre_page += [_pre_page]
+ hooks_post_run += [_post_run]
+
rst.directives.register_directive('fancy-line', FancyLine)
# The output is different for older Graphviz
self.assertEqual(*self.actual_expected_contents('dot.html', 'dot.html' if LooseVersion(dot_version()) >= LooseVersion("2.40.1") else 'dot-238.html'))
self.assertTrue(os.path.exists(os.path.join(self.path, 'output/tiny.png')))
+
+ import fancyline
+ self.assertEqual(fancyline.pre_page_call_count, 3)
+ self.assertEqual(fancyline.post_run_call_count, 1)
container.append(node)
return [container]
-def new_page(content):
+def new_page(*args):
latex2svgextra.counter = 0
def math(role, rawtext, text, lineno, inliner, options={}, content=[]):
node = nodes.raw(rawtext, latex2svgextra.patch(text, svg, depth, attribs), format='html', **options)
return [node], []
-def save_cache(pelicanobj):
+def save_cache(*args):
if settings['M_MATH_CACHE_FILE']:
latex2svgextra.pickle_cache(settings['M_MATH_CACHE_FILE'])
-def register_mcss(mcss_settings, **kwargs):
+def register_mcss(mcss_settings, hooks_pre_page, hooks_post_run, **kwargs):
global default_settings, settings
settings = copy.deepcopy(default_settings)
for key in settings.keys():
else:
latex2svgextra.unpickle_cache(None)
+ hooks_pre_page += [new_page]
+ hooks_post_run += [save_cache]
+
rst.directives.register_directive('math', Math)
rst.roles.register_canonical_role('math', math)
def _configure_pelican(pelicanobj):
- register_mcss(mcss_settings=pelicanobj.settings)
+ register_mcss(mcss_settings=pelicanobj.settings, hooks_pre_page=[], hooks_post_run=[])
def register():
pelican.signals.initialized.connect(_configure_pelican)
container.append(node)
return [container]
-def new_page(content):
+def new_page(*args):
mpl.rcParams['svg.hashsalt'] = 0
-def register_mcss(mcss_settings, **kwargs):
+def register_mcss(mcss_settings, hooks_pre_page, **kwargs):
font = mcss_settings.get('M_PLOTS_FONT', 'Source Sans Pro')
for i in range(len(_class_mapping)):
src, dst = _class_mapping[i]
_class_mapping[i] = (src.format(font=font), dst)
mpl.rcParams['font.family'] = font
+ hooks_pre_page += [new_page]
+
rst.directives.register_directive('plot', Plot)
def _pelican_configure(pelicanobj):
- register_mcss(mcss_settings=pelicanobj.settings)
+ register_mcss(mcss_settings=pelicanobj.settings, hooks_pre_page=[])
def register(): # for Pelican
pelican.signals.initialized.connect(_pelican_configure)