Additional
Samples
¶
Various examples of styling applied to Sphinx constructs. You can view the source of this page to see the specific reStructuredText used to create these examples.
Subpages ¶
Suppages get bread crumbs when they are not at the top level.
Headings ¶
This is a first level heading (
h1
).
Sub-Heading ¶
This is a second level heading (
h2
).
Sub-Sub-Heading ¶
This is a third level heading (
h3
).
Code ¶
The theme uses pygments for
inline
code
text
and
multiline
code text
Here’s an included example with line numbers.
1"""Sphinx Material theme."""
2
3import hashlib
4import inspect
5import os
6import re
7import sys
8from multiprocessing import Manager
9from typing import List, Optional
10from xml.etree import ElementTree
11
12import bs4
13import slugify
14from bs4 import BeautifulSoup
15from sphinx.util import console, logging
16
17from ._version import get_versions
18
19__version__ = get_versions()["version"]
20del get_versions
21
22ROOT_SUFFIX = "--page-root"
23
24USER_TABLE_CLASSES = []
25USER_TABLE_NO_STRIP_CLASSES = ["no-sphinx-material-strip"]
26
27
28def setup(app):
29 """Setup connects events to the sitemap builder"""
30 app.connect("html-page-context", add_html_link)
31 app.connect("build-finished", create_sitemap)
32 app.connect("build-finished", reformat_pages)
33 app.connect("build-finished", minify_css)
34 app.connect("builder-inited", update_html_context)
35 app.connect("config-inited", update_table_classes)
36 manager = Manager()
37 site_pages = manager.list()
38 sitemap_links = manager.list()
39 app.multiprocess_manager = manager
40 app.sitemap_links = sitemap_links
41 app.site_pages = site_pages
42 app.add_html_theme(
43 "sphinx_symbiflow_theme", os.path.join(html_theme_path()[0], "sphinx_symbiflow_theme")
44 )
45 return {
46 "version": __version__,
47 "parallel_read_safe": True,
48 "parallel_write_safe": True,
49 }
50
51
52def add_html_link(app, pagename, templatename, context, doctree):
53 """As each page is built, collect page names for the sitemap"""
54 base_url = app.config["html_theme_options"].get("base_url", "")
55 if base_url:
56 app.sitemap_links.append(base_url + pagename + ".html")
57 minify = app.config["html_theme_options"].get("html_minify", False)
58 prettify = app.config["html_theme_options"].get("html_prettify", False)
59 if minify and prettify:
60 raise ValueError("html_minify and html_prettify cannot both be True")
61 if minify or prettify:
62 app.site_pages.append(os.path.join(app.outdir, pagename + ".html"))
63
64
65def create_sitemap(app, exception):
66 """Generates the sitemap.xml from the collected HTML page links"""
67 if (
68 not app.config["html_theme_options"].get("base_url", "")
69 or exception is not None
70 or not app.sitemap_links
71 ):
72 return
73
74 filename = app.outdir + "/sitemap.xml"
75 print(
76 "Generating sitemap for {0} pages in "
77 "{1}".format(len(app.sitemap_links), console.colorize("blue", filename))
78 )
79
80 root = ElementTree.Element("urlset")
81 root.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9")
82
83 for link in app.sitemap_links:
84 url = ElementTree.SubElement(root, "url")
85 ElementTree.SubElement(url, "loc").text = link
86 app.sitemap_links[:] = []
87
88 ElementTree.ElementTree(root).write(filename)
89
90
91def reformat_pages(app, exception):
92 if exception is not None or not app.site_pages:
93 return
94 minify = app.config["html_theme_options"].get("html_minify", False)
95 last = -1
96 npages = len(app.site_pages)
97 transform = "Minifying" if minify else "Prettifying"
98 print("{0} {1} files".format(transform, npages))
99 transform = transform.lower()
100 # TODO: Consider using parallel execution
101 for i, page in enumerate(app.site_pages):
102 if int(100 * (i / npages)) - last >= 1:
103 last = int(100 * (i / npages))
104 color_page = console.colorize("blue", page)
105 msg = "{0} files... [{1}%] {2}".format(transform, last, color_page)
106 sys.stdout.write("\033[K" + msg + "\r")
107 with open(page, "r", encoding="utf-8") as content:
108 if minify:
109 from css_html_js_minify.html_minifier import html_minify
110
111 html = html_minify(content.read())
112 else:
113 soup = BeautifulSoup(content.read(), features="lxml")
114 html = soup.prettify()
115 with open(page, "w", encoding="utf-8") as content:
116 content.write(html)
117 app.site_pages[:] = []
118 print()
119
120
121def minify_css(app, exception):
122 if exception is not None or not app.config["html_theme_options"].get(
123 "css_minify", False
124 ):
125 app.multiprocess_manager.shutdown()
126 return
127 import glob
128 from css_html_js_minify.css_minifier import css_minify
129
130 css_files = glob.glob(os.path.join(app.outdir, "**", "*.css"), recursive=True)
131 print("Minifying {0} css files".format(len(css_files)))
132 for css_file in css_files:
133 colorized = console.colorize("blue", css_file)
134 msg = "minifying css file {0}".format(colorized)
135 sys.stdout.write("\033[K" + msg + "\r")
136 with open(css_file, "r", encoding="utf-8") as content:
137 css = css_minify(content.read())
138 with open(css_file, "w", encoding="utf-8") as content:
139 content.write(css)
140 print()
141 app.multiprocess_manager.shutdown()
142
143
144def update_html_context(app):
145 config = app.config
146 config.html_context = {**get_html_context(), **config.html_context}
147
148
149def update_table_classes(app, config):
150 table_classes = config.html_theme_options.get("table_classes")
151 if table_classes:
152 USER_TABLE_CLASSES.extend(table_classes)
153
154 table_no_strip_classes = config.html_theme_options.get("table_no_strip")
155 if table_no_strip_classes:
156 USER_TABLE_NO_STRIP_CLASSES.extend(table_no_strip_classes)
157
158
159def html_theme_path():
160 return [os.path.dirname(os.path.abspath(__file__))]
161
162
163def ul_to_list(node: bs4.element.Tag, fix_root: bool, page_name: str,
164 parent_id: str) -> List[dict]:
165 out = []
166 idx = 0
167 for child in node.find_all("li", recursive=False):
168 if callable(child.isspace) and child.isspace():
169 continue
170 formatted = {}
171 formatted["nested"] = False
172 if child.a is not None:
173 formatted["href"] = child.a["href"]
174 formatted["contents"] = "".join(map(str, child.a.contents))
175 if fix_root and formatted["href"] == "#" and child.a.contents:
176 slug = slugify.slugify(page_name) + ROOT_SUFFIX
177 formatted["href"] = "#" + slug
178 formatted["current"] = "current" in child.a.get("class", [])
179 if child.ul is not None:
180 formatted["nested"] = True
181 formatted["id"] = "{}-{}".format(parent_id, idx)
182 formatted["children"] = ul_to_list(child.ul, fix_root, page_name,
183 formatted["id"])
184 else:
185 formatted["children"] = []
186 out.append(formatted)
187 idx += 1
188 return out
189
190
191class CaptionList(list):
192 _caption = ""
193
194 def __init__(self, caption=""):
195 super().__init__()
196 self._caption = caption
197
198 @property
199 def caption(self):
200 return self._caption
201
202 @caption.setter
203 def caption(self, value):
204 self._caption = value
205
206
207def derender_toc(
208 toc_text, fix_root=True, page_name: str = "md-page-root--link"
209) -> List[dict]:
210 nodes = []
211 try:
212 toc = BeautifulSoup(toc_text, features="html.parser")
213 idx = 0
214 for child in toc.children:
215 if callable(child.isspace) and child.isspace():
216 continue
217 if child.name == "p":
218 nodes.append({"caption": "".join(map(str, child.contents))})
219 elif child.name == "ul":
220 nodes.extend(ul_to_list(child, fix_root, page_name, idx))
221 else:
222 raise NotImplementedError
223 idx += 1
224 except Exception as exc:
225 logger = logging.getLogger(__name__)
226 logger.warning(
227 "Failed to process toctree_text\n" + str(exc) + "\n" + str(toc_text)
228 )
229
230 return nodes
231
232
233def walk_contents(tags):
234 out = []
235 for tag in tags.contents:
236 if hasattr(tag, "contents"):
237 out.append(walk_contents(tag))
238 else:
239 out.append(str(tag))
240 return "".join(out)
241
242
243def table_fix(body_text, page_name="md-page-root--link"):
244 # This is a hack to skip certain classes of tables
245 ignore_table_classes = {"highlighttable", "longtable", "dataframe"} | set(
246 USER_TABLE_NO_STRIP_CLASSES
247 )
248 try:
249 body = BeautifulSoup(body_text, features="html.parser")
250 for table in body.select("table"):
251 classes = set(table.get("class", tuple()))
252 if classes.intersection(ignore_table_classes):
253 continue
254 classes = [tc for tc in classes if tc in USER_TABLE_CLASSES]
255 if classes:
256 table["class"] = classes
257 else:
258 del table["class"]
259 first_h1: Optional[bs4.element.Tag] = body.find("h1")
260 headers = body.find_all(re.compile("^h[1-6]$"))
261 for i, header in enumerate(headers):
262 for a in header.select("a"):
263 if "headerlink" in a.get("class", ""):
264 header["id"] = a["href"][1:]
265 if first_h1 is not None:
266 slug = slugify.slugify(page_name) + ROOT_SUFFIX
267 first_h1["id"] = slug
268 for a in first_h1.select("a"):
269 a["href"] = "#" + slug
270
271 divs = body.find_all("div", {"class": "section"})
272 for div in divs:
273 div.unwrap()
274
275 return str(body)
276 except Exception as exc:
277 logger = logging.getLogger(__name__)
278 logger.warning("Failed to process body_text\n" + str(exc))
279 return body_text
280
281
282# These final lines exist to give sphinx a stable str representation of
283# these two functions across runs, and to ensure that the str changes
284# if the source does.
285#
286# Note that this would be better down with a metaclass factory
287table_fix_src = inspect.getsource(table_fix)
288table_fix_hash = hashlib.sha512(table_fix_src.encode()).hexdigest()
289derender_toc_src = inspect.getsource(derender_toc)
290derender_toc_hash = hashlib.sha512(derender_toc_src.encode()).hexdigest()
291
292
293class TableFixMeta(type):
294 def __repr__(self):
295 return f"table_fix, hash: {table_fix_hash}"
296
297 def __str__(self):
298 return f"table_fix, hash: {table_fix_hash}"
299
300
301class TableFix(object, metaclass=TableFixMeta):
302 def __new__(cls, *args, **kwargs):
303 return table_fix(*args, **kwargs)
304
305
306class DerenderTocMeta(type):
307 def __repr__(self):
308 return f"derender_toc, hash: {derender_toc_hash}"
309
310 def __str__(self):
311 return f"derender_toc, hash: {derender_toc_hash}"
312
313
314class DerenderToc(object, metaclass=DerenderTocMeta):
315 def __new__(cls, *args, **kwargs):
316 return derender_toc(*args, **kwargs)
317
318
319def get_html_context():
320 return {"table_fix": TableFix, "derender_toc": DerenderToc}
321
322from . import _version
323__version__ = _version.get_versions()['version']
It also works with existing Sphinx highlighting:
<html>
<body>Hello World</body>
</html>
def hello():
"""Greet."""
return "Hello World"
/**
* Greet.
*/
function hello(): {
return "Hello World";
}
Admonitions ¶
The theme uses the
admonition
classes for Sphinx admonitions.
Note ¶
Note
This is a note .
Todo ¶
Todo
It is essential to complete todo items.
Warning ¶
Warning
This is a warning .
Danger ¶
Danger
This is danger -ous.
Attention ¶
Attention
Do I have your attention ?
Caution ¶
Caution
Use caution !
Error ¶
Error
You have made a grave error .
Hint ¶
Hint
Can you take a hint ?
Important ¶
Important
It is important to correctly use admonitions.
Tip ¶
Tip
Please tip your waiter.
Custom Admonitions ¶
Custom
You can create your own admonitions with the default style.
Footnotes ¶
I have footnoted a first item 1 and second item 2 . This also references the second item 2 .
Footnotes
Icons ¶
The following template HTML:
<span style="font-size: 2rem;" class="md-icon"></span>
translates to a the site’s icon:
The material icon font provides hundreds to choose from. You can use the
<i>
tag or the
<span>
tag.
Tables ¶
Here are some examples of Sphinx
tables
. The Sphinx Material
all classes and only applies the default style to classless tables. If you want
to use a custom table class, you will need to do two thing. First, apply it
using
..
cssclass::
custom-class
and then add it to your configuration’s
table_classes
variable.
Grid ¶
A grid table:
Header1 |
Header2 |
Header3 |
Header4 |
---|---|---|---|
row1, cell1 |
cell2 |
cell3 |
cell4 |
row2 … |
… |
… |
|
… |
… |
… |
Simple ¶
A simple table:
H1 |
H2 |
H3 |
---|---|---|
cell1 |
cell2 |
cell3 |
… |
… |
… |
… |
… |
… |
User-styled Table ¶
Note
table_classes is set to [“plain”] in the site’s configuration. Only plain remains as the class of the table. Other standard classes applied by Sphinx are removed.
This is feature demonstration. There is no css for the plain class, and so this is completely unstyled.
User |
Styled |
Table |
---|---|---|
cell1 |
cell2 |
cell3 |
… |
… |
… |
… |
… |
… |
List Tables ¶
Column 1 |
Column 2 |
---|---|
Item 1 |
Item 2 |
Alignment ¶
Warning
Alignment is not currently working as expected.
Column 1 |
Column 2 |
---|---|
Item 1 |
Item 2 |
Treat |
Quantity |
Description |
---|---|---|
Albatross |
2.99 |
On a stick! |
Crunchy Frog |
1.49 |
If we took the bones out, it wouldn’t be crunchy, now would it? |
Gannet Ripple |
1.99 |
On a stick! |
Code Documentation ¶
An example Python function.
- format_exception ( etype , value , tb [ , limit=None ] ) ¶
-
Format the exception with a traceback.
- Parameters
-
-
etype – exception type
-
value – exception value
-
tb – traceback object
-
limit ( integer or None ) – maximum number of stack frames to show
-
- Return type
-
list of strings
An example JavaScript function.
- class MyAnimal ( name [ , age ] ) ¶
-
- Arguments
-
-
name (
string()
) – The name of the animal -
age (
number()
) – an optional age for the animal
-
Glossaries ¶
- environment ¶
-
A structure where information about all documents under the root is saved, and used for cross-referencing. The environment is pickled after the parsing stage, so that successive runs only need to read and parse new and changed documents.
- source directory ¶
-
The directory which, including its subdirectories, contains all source files for one Sphinx project.
Math ¶
Production Lists ¶
try_stmt ::= try1_stmt | try2_stmt try1_stmt ::= "try" ":"suite
("except" [expression
[","target
]] ":"suite
)+ ["else" ":"suite
] ["finally" ":"suite
] try2_stmt ::= "try" ":"suite
"finally" ":"suite