documentation/extensions/odoo_theme/__init__.py
Victor Feyens 2ddac026f1 [FIX] extensions: support parallel read
Now that patchqueue was removed, we can consider supporting the parallel 
read with our local extensions.

Since we only have basic extensions, not storing any data on the build 
environment, the change mainly consists of specifying the extensions as 
parallelizable.

The only specific case is the html domain, since the base html domain 
requires the support of the method merge_domaindata in parallel read 
mode.  Since we do not need to share anything between the envs for this 
extension, we can simply ignore the method.
2021-05-19 15:03:39 +02:00

102 lines
3.9 KiB
Python

from docutils import nodes
from sphinx import addnodes
from sphinx.environment.adapters import toctree
from . import pygments_override, translator
def setup(app):
app.set_translator('html', translator.BootstrapTranslator)
app.connect('html-page-context', set_missing_meta)
app.add_js_file('js/utils.js') # Keep in first position
app.add_js_file('js/layout.js')
app.add_js_file('js/menu.js')
app.add_js_file('js/page_toc.js')
return {
'parallel_read_safe': True,
'parallel_write_safe': True
}
def set_missing_meta(app, pagename, templatename, context, doctree):
if context.get('meta') is None: # Pages without title (used with `include::`) have no meta
context['meta'] = {}
class Monkey(object):
""" Replace patched method of an object by a new method receiving the old one in argument. """
def __init__(self, obj):
self.obj = obj
def __call__(self, fn):
name = fn.__name__
old = getattr(self.obj, name)
setattr(self.obj, name, lambda self_, *args, **kwargs: fn(old, self_, *args, **kwargs))
@Monkey(toctree.TocTree)
def resolve(old_resolve, tree, docname, *args, **kwargs):
def _update_toctree_nodes(_node) -> None:
""" Make necessary changes to Docutils' nodes of the toc.
Internal structure of toc nodes:
<ul>
<li>
<p><a/></p>
<ul>
...
</ul>
</li>
<li/>
<ul/>
"""
if isinstance(_node, nodes.reference): # The node is a reference (<a/>)
_node_docname = _get_docname(_node)
_clear_reference_if_empty_page(_node, _node_docname)
_set_docname_as_class(_node, _node_docname)
elif isinstance(_node, (addnodes.compact_paragraph, nodes.bullet_list, nodes.list_item)):
for _subnode in _node.children:
_update_toctree_nodes(_subnode)
def _get_docname(_node):
"""
docname = a/b/c/the_page_being_rendered
_ref = ../../contributing/documentation
_path_parts = ['..', '..', 'contributing', 'documentation']
_res = ['a', 'contributing', 'documentation']
_docname = a/contributing/documentation
"""
_ref = _node['refuri'].replace('.html', '')
_parent_directory_occurrences = _ref.count('..')
if not _parent_directory_occurrences: # The ref is already the docname
_docname = _ref
else:
_path_parts = _ref.split('/')
_res = docname.split('/')[:-(_parent_directory_occurrences+1)] \
+ _path_parts[_parent_directory_occurrences:]
_docname = '/'.join(_res)
return _docname
def _clear_reference_if_empty_page(_reference_node, _node_docname):
""" Clear reference of 'empty' toctree pages.
Inspect parent node's siblings to determine whether the node references a toc and, if so,
clear its reference URL. (<a href="#"/>)
If the page has the `show-content` metadata, don't clear the reference.
"""
if _node_docname and any(
isinstance(_subnode, nodes.bullet_list)
for _subnode in _reference_node.parent.parent.children
): # The node references a toc
if 'show-content' not in tree.env.metadata[_node_docname]:
_reference_node['refuri'] = '#' # The page must not be accessible
def _set_docname_as_class(_reference_node, _node_docname):
_node_docname = _node_docname or docname # refuri==None <-> href="#"
_reference_node.parent.parent['classes'].append(f'o_menu_{_node_docname.replace("/", "_")}')
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
if resolved_toc: # `resolve` returns None if the depth of the TOC to resolve is too high
_update_toctree_nodes(resolved_toc)
return resolved_toc