Source code for pydata_sphinx_theme.translator
"""A custom Sphinx HTML Translator for Bootstrap layout."""
import types
import sphinx
from packaging.version import Version
from sphinx.application import Sphinx
from sphinx.ext.autosummary import autosummary_table
from sphinx.util import logging
[docs]
logger = logging.getLogger(__name__)
[docs]
class BootstrapHTML5TranslatorMixin:
"""Mixin HTML Translator for a Bootstrap-ified Sphinx layout.
Only a couple of functions have been overridden to produce valid HTML to be
directly styled with Bootstrap, and fulfill acessibility best practices.
"""
def __init__(self, *args, **kwds):
super().__init__(*args, **kwds)
self.settings.table_style = "table"
[docs]
def starttag(self, *args, **kwargs):
"""Ensure an aria-level is set for any heading role."""
if kwargs.get("ROLE") == "heading" and "ARIA-LEVEL" not in kwargs:
kwargs["ARIA-LEVEL"] = "2"
return super().starttag(*args, **kwargs)
[docs]
def visit_table(self, node):
"""Custom visit table method.
Copy of sphinx source to *not* add 'docutils' and 'align-default' classes but add 'table' class.
"""
# init the attributes
atts = {}
if Version(sphinx.__version__) < Version("4.3"):
self._table_row_index = 0
else:
self._table_row_indices.append(0)
# get the classes
classes = [cls.strip(" \t\n") for cls in self.settings.table_style.split(",")]
# we're looking at the 'real_table', which is wrapped by an autosummary
if isinstance(node.parent, autosummary_table):
classes += ["autosummary"]
# add the width if set in a style attribute
if "width" in node:
atts["style"] = f'width: {node["width"]}'
# add specific class if align is set
if "align" in node:
classes.append(f'table-{node["align"]}')
tag = self.starttag(node, "table", CLASS=" ".join(classes), **atts)
self.body.append(tag)
[docs]
def setup_translators(app: Sphinx):
"""Add bootstrap HTML functionality if we are using an HTML translator.
This re-uses the pre-existing Sphinx translator and adds extra functionality defined
in ``BootstrapHTML5TranslatorMixin``. This way we can retain the original translator's
behavior and configuration, and _only_ add the extra bootstrap rules.
If we don't detect an HTML-based translator, then we do nothing.
"""
if not app.registry.translators.items():
translator = types.new_class(
"BootstrapHTML5Translator",
(
BootstrapHTML5TranslatorMixin,
app.builder.default_translator_class,
),
{},
)
app.set_translator(app.builder.name, translator, override=True)
else:
for name, klass in app.registry.translators.items():
if app.builder.format != "html":
# Skip translators that are not HTML
continue
translator = types.new_class(
"BootstrapHTML5Translator",
(
BootstrapHTML5TranslatorMixin,
klass,
),
{},
)
app.set_translator(name, translator, override=True)