1# BSD 3-Clause License 2# 3# Copyright (c) 2018, pandas 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31# Based on https://github.com/pydata/pydata-sphinx-theme 32 33from docutils import nodes 34 35import sphinx 36from sphinx.ext.autosummary import autosummary_table 37from sphinx.locale import admonitionlabels 38 39import types 40 41class BootstrapHTML5TranslatorMixin: 42 def __init__(self, *args, **kwds): 43 super().__init__(*args, **kwds) 44 self.settings.table_style = "table" 45 46 def starttag(self, *args, **kwargs): 47 """ensure an aria-level is set for any heading role""" 48 if kwargs.get("ROLE") == "heading" and "ARIA-LEVEL" not in kwargs: 49 kwargs["ARIA-LEVEL"] = "2" 50 return super().starttag(*args, **kwargs) 51 52 def visit_admonition(self, node, name: str = '') -> None: 53 admonitionclasses = { 54 'attention': 'alert-primary', 55 'caution': 'alert-secondary', 56 'danger': 'alert-danger', 57 'error': 'alert-danger', 58 'hint': 'alert-secondary', 59 'important': 'alert-primary', 60 'note': 'alert-info', 61 'seealso': 'alert-info', 62 'tip': 'alert-info', 63 'warning': 'alert-warning', 64 } 65 66 self.body.append(self.starttag( 67 node, 'div', CLASS=('alert ' + admonitionclasses[name]))) 68 if name: 69 self.body.append( 70 self.starttag(node, 'div', '', CLASS='h5')) 71 self.body.append(str(admonitionlabels[name])) 72 self.body.append('</div>') 73 74 def visit_table(self, node): 75 # init the attributes 76 atts = {} 77 78 self._table_row_indices.append(0) 79 80 # get the classes 81 classes = [cls.strip(" \t\n") for cls in self.settings.table_style.split(",")] 82 83 # we're looking at the 'real_table', which is wrapped by an autosummary 84 if isinstance(node.parent, autosummary_table): 85 classes += ["autosummary"] 86 87 # add the width if set in a style attribute 88 if "width" in node: 89 atts["style"] = f'width: {node["width"]}' 90 91 # add specific class if align is set 92 if "align" in node: 93 classes.append(f'table-{node["align"]}') 94 95 tag = self.starttag(node, "table", CLASS=" ".join(classes), **atts) 96 self.body.append(tag) 97 98def setup_translators(app): 99 if app.builder.format != "html": 100 return 101 102 if not app.registry.translators.items(): 103 translator = types.new_class( 104 "BootstrapHTML5Translator", 105 ( 106 BootstrapHTML5TranslatorMixin, 107 app.builder.default_translator_class, 108 ), 109 {}, 110 ) 111 app.set_translator(app.builder.name, translator, override=True) 112 else: 113 for name, klass in app.registry.translators.items(): 114 translator = types.new_class( 115 "BootstrapHTML5Translator", 116 ( 117 BootstrapHTML5TranslatorMixin, 118 klass, 119 ), 120 {}, 121 ) 122 app.set_translator(name, translator, override=True) 123 124def setup(app): 125 app.connect("builder-inited", setup_translators) 126