From f72033bb707046ef5966cb55cbf9bd9fb7bb038f Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Wed, 6 Jan 2021 14:00:52 +0100 Subject: [PATCH] docs: add bootstrap extension To get Sphinx and Bootstrap to work well together, we need to massage the output from Sphinx a bit. This adds an extension to do such changes, based on work from here: https://github.com/pydata/pydata-sphinx-theme ...However, because we don't ship as an external theme, we can't just do things as a part of __init__.py, so instead we register an extension that does the heavy lifting for us. Reviewed-by: Eric Engestrom Part-of: --- docs/_exts/bootstrap.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/_exts/bootstrap.py diff --git a/docs/_exts/bootstrap.py b/docs/_exts/bootstrap.py new file mode 100644 index 0000000..555bd6b --- /dev/null +++ b/docs/_exts/bootstrap.py @@ -0,0 +1,108 @@ +# BSD 3-Clause License +# +# Copyright (c) 2018, pandas +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Based on https://github.com/pydata/pydata-sphinx-theme + +from packaging.version import Version + +from docutils import nodes + +import sphinx +from sphinx.ext.autosummary import autosummary_table + +import types + +class BootstrapHTML5TranslatorMixin: + def __init__(self, *args, **kwds): + super().__init__(*args, **kwds) + self.settings.table_style = "table" + + 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) + + def visit_table(self, node): + # 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) + +def setup_translators(app): + 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) + +def setup(app): + app.connect("builder-inited", setup_translators) -- 2.7.4