The PyData Sphinx Theme#
A clean, Bootstrap-based Sphinx theme by and for the PyData community.
Built with Bootstrap
Use Bootstrap classes and functionality in your documentation.
Responsive Design
Site sections will change behavior and size at different screen sizes.
Light / Dark theme
Users can toggle between light and dark themes interactively.
Customizable UI and themes
Customize colors and branding with CSS variables, and build custom UIs with Sphinx Design.
Supports PyData and Jupyter
CSS and UI support for Jupyter extensions and PyData execution outputs.
Example Gallery
See our gallery of projects that use this theme.
See also
If you are looking for a Sphinx theme that puts all of its sub-pages in the sidebar, the Sphinx Book Theme has a similar look and feel, and Furo is another excellent choice. You can also see the Sphinx Themes Gallery for more ideas.
User Guide#
Information about using, configuration, and customizing this theme.
User Guide#
You can configure the behavior, look, and feel of the theme in many ways. The remaining pages in the user guide cover various ways of doing so.
Danger
This theme is still under active development, and we make no promises about the stability of any specific HTML structure, CSS variables, etc. Make these customizations at your own risk, and pin versions if you’re worried about breaking changes!
There are a number of options for configuring your site’s look and feel.
All configuration options are passed with the html_theme_options
variable in your conf.py
file.
This is a dictionary with key: val
pairs that you can configure in various ways.
Installation#
Note
Each pydata-sphinx-theme release has a minimum required Sphinx version, which should be automatically handled by your package installer. It is also tested against newer versions of Sphinx that were available prior to that release of the pydata-sphinx-theme package. If you run into issues when trying to use a more recent version of Sphinx, please open an issue here: pydata/pydata-sphinx-theme#issues
The theme is available on PyPI and conda-forge, and can thus be installed with:
$ pip install pydata-sphinx-theme
$ conda install pydata-sphinx-theme --channel conda-forge
Then, in the conf.py
of your sphinx docs, you update the html_theme
configuration option:
html_theme = "pydata_sphinx_theme"
Note
This theme may not work with the latest major versions of Sphinx, especially if they have only recently been released. Please give us a few months of time to work out any bugs and changes when new releases are made.
Development version#
If you want to track the development version of the theme, you can install it from the git repo:
$ pip install git+https://github.com/pydata/pydata-sphinx-theme.git@main
or in a conda environment yml file, you can add:
- pip:
- git+https://github.com/pydata/pydata-sphinx-theme.git@main
Theme Structure and Layout#
This section describes some basic ways to control the layout and structure of your documentation. This theme inherits its structure and section terminology from the Sphinx Basic NG theme.
Overview of theme layout#
Below is a brief overview of the major layout of this theme.
Take a look at the diagram to understand what the major sections are called.
Where you can insert component templates in html_theme_options
, we include the variable name in inline code
.
Click on section titles to learn more about them and some basic layout configuration.
article_header_start
article_header_end
Article Content
article_footer_items
Horizontal spacing#
By default the theme’s three columns have fixed widths.
The primary sidebar
will snap to the left, the secondary sidebar
will snap to the right, and the article content
will be centered in between.
If one of the sidebars is not present, then the
article content
will be centered between the other sidebar and the side of the page.If neither sidebar is present, the
article content
will be in the middle of the page.
If you’d like the article content
to take up more width than its default, use the max-width
and flex-grow
CSS variables with the .bd-content
selector.
For example, to make the content grow to fit all available width, add a custom CSS rule like:
.bd-content {
flex-grow: 1;
max-width: 100%;
}
Templates and components#
There are a few major theme sections that you can customize to add/remove components, or add your own components. Each section is configured with a list of html templates — these are snippets of HTML that are inserted into the section by Sphinx.
You can choose which templates show up in each section, as well as the order in which they appear. This page describes the major areas that you can customize.
Note
When configuring templates in each section, you may omit the .html
suffix after each template if you wish.
Article Header#
Located in sections/header-article.html
.
The article header is a narrow bar just above the article’s content. There are two sub-sections that can have component templates added to them:
article_header_start
is aligned to the beginning (left) of the article header. By default, this section has thebreadcrumbs.html
component which displays links to parent pages of the current page.article_header_end
is aligned to the end (right) of the article header. By default, this section is empty.
Built-in components to insert into sections#
Below is a list of built-in templates that you can insert into any section. Note that some of them may have CSS rules that assume a specific section (and will be named accordingly).
breadcrumbs.html
copyright.html
edit-this-page.html
footer-article/prev-next.html
icon-links.html
last-updated.html
navbar-icon-links.html
navbar-logo.html
navbar-nav.html
page-toc.html
searchbox.html
search-button.html
search-field.html
sidebar-ethical-ads.html
sidebar-nav-bs.html
sourcelink.html
sphinx-version.html
theme-switcher.html
version-switcher.html
indices.html
theme-version.html
Add your own HTML templates to theme sections#
If you’d like to add your own custom template to any of these sections, you could do so with the following steps:
Create an HTML file in a folder called
_templates
. For example, if you wanted to display the version of your documentation using a Jinja template, you could create a file:_templates/version.html
and put the following in it:<!-- This will display the version of the docs --> {{ version }}
Now add the file to your menu items for one of the sections above. For example:
html_theme_options = { ... "navbar_start": ["navbar-logo", "version"], ... }
Page Table of Contents#
Show more levels of the in-page TOC by default#
Normally only the 2nd-level headers of a page are show in the right table of contents, and deeper levels are only shown when they are part of an active section (when it is scrolled on screen).
You can show deeper levels by default by using the following configuration, indicating how many levels should be displayed:
html_theme_options = {
"show_toc_level": 2
}
All headings up to and including the level specified will now be shown regardless of what is displayed on the page.
Remove the Table of Contents#
To remove the Table of Contents, add :html_theme.sidebar_secondary.remove:
to the file-wide metadata at the top of a page.
This will remove the Table of Contents from that page only.
Header links#
The header navigation bar is at the top of each page and contains top-level navigation across pages in your documentation, as well as extra links and components that you can add. These sections cover a few things you can control with the Header Navigation Bar.
Icon links#
Icon links are a collection of images and icons that each link to a page or external site. They are helpful if you wish to display social media icons, GitHub badges, or project logos.
These links take the following form:
html_theme_options = {
...
"icon_links": [
{
# Label for this link
"name": "GitHub",
# URL where the link will redirect
"url": "https://github.com/<your-org>/<your-repo>", # required
# Icon class (if "type": "fontawesome"), or path to local image (if "type": "local")
"icon": "fa-brands fa-square-github",
# The type of image to be used (see below for details)
"type": "fontawesome",
}
]
}
Additionally, the screen-reader accessible label for this menu can be configured:
html_theme_options = {
...
"icon_links_label": "Quick Links",
...
}
There are two kinds of icons you can use, described below:
FontAwesome icons#
FontAwesome is a collection of icons that are commonly used in websites. They include both generic shape icons (e.g., “arrow-down”), as well as brand-specific icons (e.g. “github”).
You can use FontAwesome icons by specifying "type": "fontawesome"
, and
specifying a FontAwesome class in the icon
value.
The value of icon
can be any full
FontAwesome 6 Free icon.
In addition to the main icon class, e.g. fa-cat
, the “style” class must
also be provided e.g. fa-brands for branding, or fa-solid for solid.
Here are several examples:
html_theme_options = {
...
"icon_links": [
{
"name": "GitHub",
"url": "https://github.com/<your-org>/<your-repo>",
"icon": "fa-brands fa-square-github",
"type": "fontawesome",
},
{
"name": "GitLab",
"url": "https://gitlab.com/<your-org>/<your-repo>",
"icon": "fa-brands fa-square-gitlab",
"type": "fontawesome",
},
{
"name": "Twitter",
"url": "https://twitter.com/<your-handle>",
"icon": "fa-brands fa-square-twitter",
# The default for `type` is `fontawesome` so it is not actually required in any of the above examples as it is shown here
},
{
"name": "Mastodon",
"url": "https://<your-host>@<your-handle>",
"icon": "fa-brands fa-mastodon",
},
],
...
}
Hint
To get custom colors like “Twitter blue”, use the following in your CSS,
e.g. custom.css
:
i.fa-twitter-square:before {
color: #55acee;
}
This has already been added for the brands that have shortcuts (see below).
Image icons#
If you’d like to display an icon image that is not in the FontAwesome icons library, you may instead specify a URL or a path to a local image that will be used for the icon.
To display an image on the web, use "type": "url"
, and provide a URL to an image in the icon
value.
For example:
html_theme_options = {
...
"icon_links": [
{
"name": "Pandas",
"url": "https://pandas.pydata.org",
"icon": "https://raw.githubusercontent.com/pydata/pydata-sphinx-theme/main/docs/_static/pandas-square.svg",
"type": "url",
},
],
...
}
To display a local image from a file path, use "type": "local"
, and add a path to an image
relative to your documentation root in the icon
value.
For example:
html_theme_options = {
...
"icon_links": [
{
"name": "PyData",
"url": "https://pydata.org",
"icon": "_static/pydata-logo-square.png",
"type": "local",
},
],
...
}
Tip
Use .svg
images for a higher-resolution output that behaves similarly across screen sizes.
Icon Link Shortcuts#
There are a few shortcuts supported to minimize configuration for commonly-used services.
These may be removed in a future release in favor of icon_links
:
html_theme_options = {
...
"github_url": "https://github.com/<your-org>/<your-repo>",
"gitlab_url": "https://gitlab.com/<your-org>/<your-repo>",
"bitbucket_url": "https://bitbucket.org/<your-org>/<your-repo>",
"twitter_url": "https://twitter.com/<your-handle>",
...
}
Add custom attributes to icon links#
You can add custom attributes to the link element (<a>
) of your icon links.
This is helpful if you need to add custom link behavior.
To do so, use the pattern "attributes": {"attribute1": "value1"}
in a given icon link entry.
For example, to specify a custom target
and rel
attribute, and to define your own custom link classes:
html_theme_options = {
...
"icon_links": [
{
"name": "PyData",
"url": "https://pydata.org",
"icon": "_static/pydata-logo-square.png",
"type": "local",
# Add additional attributes to the href link.
# The defaults of target, rel, class, title and href may be overwritten.
"attributes": {
"target" : "_blank",
"rel" : "noopener me",
"class": "nav-link custom-fancy-css"
}
},
],
...
}
Warning
This might make your icon links behave unexpectedly and might over-ride default behavior, so make sure you know what you’re doing!
Sphinx indices#
Sphinx generates indices named genindex, modindex and py-modindex when building a documentation. More information about them can be found in the Sphinx documentation here.
Add indices links#
By design the indices pages are not linked in a documentation generated with this theme. To include them in the primary sidebar of each page, add the following configuration to your conf.py
file in html_theme_options
and the available indices will be display at the bottom.
html_theme_options = {
#[...]
"primary_sidebar_end": ["indices.html", "sidebar-ethical-ads.html"]
#[...]
}
Note
don’t forget to add back the "sidebar-ethical-ads.html"
template if you are serving your documentation using ReadTheDocs.
Version switcher dropdowns#
You can add a button to your site that allows users to
switch between versions of your documentation. The links in the version
switcher will differ depending on which page of the docs is being viewed. For
example, on the page https://mysite.org/en/v2.0/changelog.html
, the
switcher links will go to changelog.html
in the other versions of your
docs. When clicked, the switcher will check for the existence of that page, and
if it doesn’t exist, redirect to the homepage of that docs version instead.
The switcher requires the following configuration steps:
Add a JSON file containing a list of the documentation versions that the switcher should show on each page.
Add a configuration dictionary called
switcher
to thehtml_theme_options
dict inconf.py
.switcher
should have 2 keys:json_url
: the persistent location of the JSON file described above.version_match
: a string stating the version of the documentation that is currently being browsed.
Specify where to place the switcher in your page layout. For example, add the
"version-switcher"
template to one of the layout lists inhtml_theme_options
(e.g.,navbar_end
,footer_start
, etc).
Below is a more in-depth description of each of these configuration steps.
Add a JSON file to define your switcher’s versions#
First, write a JSON file stating which versions of your docs will be listed in the switcher’s dropdown menu. That file should contain a list of entries that each can have the following fields:
version
: a version string. This is checked againstswitcher['version_match']
to provide styling to the switcher.url
: the URL for this version.name
: an optional name to display in the switcher dropdown instead of the version string (e.g., “latest”, “stable”, “dev”, etc).
Here is an example JSON file:
[
{
"name": "v2.1 (stable)",
"version": "2.1",
"url": "https://mysite.org/en/2.1/index.html"
},
{
"version": "2.1rc1",
"url": "https://mysite.org/en/2.1rc1/index.html"
},
{
"version": "2.0",
"url": "https://mysite.org/en/2.0/index.html"
},
{
"version": "1.0",
"url": "https://mysite.org/en/1.0/index.html"
}
]
See the discussion of switcher['json_url']
(below) for options of where to
save the JSON file.
Configure switcher['json_url']
#
The JSON file needs to be at a stable, persistent, fully-resolved URL (i.e., not specified as a path relative to the sphinx root of the current doc build). Each version of your documentation should point to the same URL, so that as new versions are added to the JSON file all the older versions of the docs will gain switcher dropdown entries linking to the new versions. This could be done a few different ways:
The location could be one that is always associated with the most recent documentation build (i.e., if your docs server aliases “latest” to the most recent version, it could point to a location in the build tree of version “latest”). For example:
html_theme_options = { ..., "switcher": { "json_url": "https://mysite.org/en/latest/_static/switcher.json", } }
In this case the JSON is versioned alongside the rest of the docs pages but only the most recent version is ever loaded (even by older versions of the docs).
The JSON could be saved in a folder that is listed under your site’s
html_static_path
configuration. See the Sphinx static path documentation for more information.The JSON could be stored outside the doc build trees. This probably means it would be outside the software repo, and would require you to add new version entries to the JSON file manually as part of your release process. Example:
html_theme_options = { ..., "switcher": { "json_url": "https://mysite.org/switcher.json", } }
By default the theme is testing the .json
file provided and outputs warnings in the Sphinx build. If this test breaks the pipeline of your docs, the test can be disabled by configuring the check_switcher
parameter in conf.py
:
html_theme_options = {
# ...
"check_switcher": False
}
Configure switcher['version_match']
#
This configuration value tells the switcher what docs version is currently being viewed, and is used to style the switcher (i.e., to highlight the current docs version in the switcher’s dropdown menu, and to change the text displayed on the switcher button).
Typically you can re-use one of the sphinx variables version
or release
as the value of switcher['version_match']
; which one you use
depends on how granular your docs versioning is. See
the Sphinx “project info” documentation
for more information). Example:
version = my_package_name.__version__.replace("dev0", "") # may differ
html_theme_options = {
...,
"switcher": {
"version_match": version,
}
}
Specify where to display the switcher#
Finally, tell the theme where on your site’s pages you want the switcher to
appear. There are many choices here: you can add "version-switcher"
to one
of the locations in html_theme_options
(e.g., navbar_end
,
footer_start
, etc). For example:
html_theme_options = {
...,
"navbar_start": ["navbar-logo", "version-switcher"]
}
Alternatively, you could override one of the other templates to include the
version switcher in a sidebar. For example, you could define
_templates/sidebar-nav-bs.html
as:
{%- include 'version-switcher.html' -%}
{{ super() }}
to insert a version switcher at the top of the primary sidebar, while still keeping the default navigation below it. See Theme Structure and Layout for more information.
Keyboard shortcuts#
Trigger the search bar#
You can trigger the search bar pop-up with ctrl/cmd + K.
Change pages#
By default, you can move to the previous/next page using the and keys on a keyboard. To disable this behavior, use the following configuration:
html_theme_options = {
"navigation_with_keys": False
}
Internationalization#
This theme contains translatable strings. There are two kinds of strings in this theme, with different steps to translate each.
Built-in strings are hard-coded in the theme’s templates. They will automatically be translated if the language is supported. To add another language, see Add translations to translateable text.
Configurable strings are user-defined with the html_theme_options
variable in your conf.py
file (see other sections in the user guide for examples).
To translate these strings, see the section below.
Translating configurable strings#
These instructions are for translating configurable strings (those that are customizable in html_theme_options
).
These instructions assume that you store your translations in a locale
directory under your documentation directory, and that you want to use theme
as the name of the message catalog for these strings.
In your
conf.py
file:import os.path from sphinx.locale import get_translation catalog = "theme" _ = get_translation(catalog) html_theme_options = { "search_bar_text": _("Search the docs..."), # You only need to translate the following if you use these features. "icon_links_label": _("Quick Links"), "icon_links": [ { "name": _("GitHub"), "url": "https://github.com/<your-org>/<your-repo>", "icon": "fab fa-github-square", }, ], "external_links": [ { "name": _("link-one-name"), "url": "https://<link-one>", }, ], } def setup(app): locale_dir = os.path.join(os.path.abspath(os.path.dirname(__file__), "locale") app.add_message_catalog(catalog, locale_dir)
Extract the strings to translate:
pybabel extract . -o locale/theme.pot
Create a message catalog (changing the
--locale
option as desired):pybabel init --input-file=locale/theme.pot --domain=theme --output-dir=locale --locale=fr
Translate the message catalog by editing the file.
Compile the message catalog:
pybabel compile --directory=locale --domain=theme
Theme-specific elements#
There are a few elements that are unique or particularly important to this theme. Some of these are triggered with configuration or markdown syntax that is unique to the theme, and we cover them below.
Page contents
Mathematics#
Most Sphinx sites support math, but it is particularly important for scientific computing and so we illustrate support here as well.
Here is an inline equation: \(X_{0:5} = (X_0, X_1, X_2, X_3, X_4)\) and \(another\) and \(x^2 x^3 x^4\) another. And here’s one to test vertical height \(\frac{\partial^2 f}{\partial \phi^2}\).
Here is block-level equation:
And here is a really long equation with a label!
You can add a link to equations like the one above: (1) and (2).
Code blocks#
Code block styling is inspired by GitHub’s code block style and also has support for Code Block captions/titles. See the Sphinx documentation on code blocks for more information.
print("A regular code block")
print("A regular code block")
print("A regular code block")
You can also provide captions with code blocks, which will be displayed just above the code. For example, the following code:
```{code-block} python
:caption: python.py
print("A code block with a caption.")
```
results in:
You can also display line numbers. For example, the following code:
```{code-block} python
:caption: python.py
:linenos:
print("A code block with a caption and line numbers.")
print("A code block with a caption and line numbers.")
print("A code block with a caption and line numbers.")
```
results in:
Inline code#
When used directly, the code
role just displays the text without syntax highlighting, as a literal. As mentioned in the Sphinx documentation you can also enable syntax highlighting by defining a custom role. It will then use the same highligther as in the code-block
directive.
.. role:: python(code)
:language: python
In Python you can :python:`import sphinx`.
In Python you can import sphinx
.
Code execution#
This theme has support for Jupyter execution libraries so that you can programmatically update your documentation with each build. For examples, see PyData Library Styles.
Footnotes#
Here’s one footnote[1] and another footnote [2] and a named footenote[3], symbol [4].
Version changes#
This theme supports a short-hand way of making admonitions behave like sidebars. This can be a helpful way of highlighting content that lives to the side of your main text without interrupting the vertical flow as much.
For example, look to the right of an “admonition sidebar” and a traditional Sphinx sidebar.
To make an admonition behave like a sidebar, add the sidebar
class to its list of classes.
For example, the admonition sidebar was created with the following markdown:
```{admonition} A sidebar admonition!
:class: sidebar note
Some sidebar content.
```
Link shortening for git repository services#
Many projects have links back to their issues / PRs hosted on platforms like GitHub or GitLab. Instead of displaying these as raw links, this theme does some lightweight formatting for these platforms specifically.
In reStructuredText, URLs are automatically converted to links, so this works automatically.
In MyST Markdown, by default you must define a standard markdown link and duplicate the URL in the link text. You may skip the need to manually define the link text by activating the MyST Linkify extension.
For example:
reStructuredText
https://github.com/pydata/pydata-sphinx-theme/pull/1012
MyST Markdown (default)
[https://github.com/pydata/pydata-sphinx-theme/pull/1012](https://github.com/pydata/pydata-sphinx-theme/pull/1012)
MyST Markdown with MyST Linkify
https://github.com/pydata/pydata-sphinx-theme/pull/1012
There are a variety of link targets supported, here’s a table for reference:
GitHub
https://github.com
: githubhttps://github.com/pydata
: pydatahttps://github.com/pydata/pydata-sphinx-theme
: pydata/pydata-sphinx-themehttps://github.com/pydata/pydata-sphinx-theme/pull/1012
: pydata/pydata-sphinx-theme#1012https://github.com/orgs/pydata/projects/2
: pydata/projects#2
GitLab
https://gitlab.com
: gitlabhttps://gitlab.com/gitlab-org
: gitlab-orghttps://gitlab.com/gitlab-org/gitlab
: gitlab-org/gitlabhttps://gitlab.com/gitlab-org/gitlab/-/issues/375583
: gitlab-org/gitlab#375583
Links provided with a text body won’t be changed.
Blogs with ABlog
#
The ABlog extension allows you to tag pages as blog posts and additionally include them in landing pages for your blog. It also has a number of sidebar templates to show off collections of your posts.
Minimum version ABlog v0.11.0
Make sure you have ABlog>=0.11.0rc2
in your dependencies.
This theme has styling support for ABlog, and demonstrates some of its functionality here.
Example blog#
Click below to go to the blog
Example post list#
Post one with a long-ish title we can use to compare - 2022-01-01
Here’s some text for post 1!
Post title 2 with a longer title to compare in the UI - 2022-01-02
Here’s some text for post 2!
Post three with a long-ish title we can use to compare - 2022-01-03
Here’s some text for post 3!
Sphinx Design Components#
Cards and tabs provide some extra UI flexibility for your content. This theme provides custom CSS to ensure that sphinx-design elements look and feel consistent with this theme.
See also
For more about how to use these extensions, see the sphinx-design documentation.
Below you can find some examples of the components created with the sphinx-design
extension.
Cards#
Only body.
But with multiple text paragraphs.
Content of the third card.
example
panel 1 header
panel 1 content more content
panel 2 header
panel 2 content
Tabs#
int main(const int argc, const char **argv) {
return 0;
}
def main():
return
class Main {
public static void main(String[] args) {
}
}
function main()
end
PROGRAM main
END PROGRAM main
Dropdowns#
Dropdowns should look similar to admonitions, but clickable. See the Sphinx Design Dropdown documentation for more information.
An admonition for reference.
And some admonition content.
And with no title and some content!
With a title
And some content!
With a title
And some content and an icon!
A primary title and color
And some content!
A secondary title and color
And some content!
Extending the theme#
There are many extensions available for Sphinx that can enhance your site or provide powerful customization abilities. Here we describe a few customizations that are popular with pydata-sphinx-theme
users.
Collapsible admonitions#
The sphinx-togglebutton extension provides optional show/hide behavior for admonitions. Follow their installation instructions, then add it to the extentions
list in your conf.py
:
extensions = [
# [...]
"sphinx_togglebutton"
]
Then add the dropdown
class to any admonition directive (shown here on a note
admonition):
Note
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
.. note::
:class: dropdown
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Custom admonition styles#
A limited set of admonitions are built-in to docutils (the rST → HTML engine that underlies Sphinx). However, it is possible to create custom admonitions with their own default colors, icons, and titles.
Customizing the title#
Although most admonitions have a default title like note
or warning
, a generic admonition
directive is built-in to docutils/Sphinx. In this theme, its color defaults to the same color as note
admonitions, and it has a bell icon:
Custom title!
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
The title is specified on the same line as the .. admonition::
directive:
.. admonition:: Custom title!
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Styling with semantic color names#
You can re-style any admonition to match any of the built-in admonition types using any of the semantic color names as a class (this is most useful for custom-titled admonitions):
Custom title with “warning” style
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Note that it updates both the color and the icon.
.. admonition:: Custom title with "warning" style
:class: warning
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
This theme defines classes for the standard docutils admonition types (attention
, caution
, etc) and additionally supports seealso
and todo
admonitions (see Admonitions for a demo of all built-in admonition styles).
Customizing the color#
Besides the pre-defined semantic color classes (see previous section) you can also add a bespoke color to any admonition by defining your own CSS class. Example:
Admonition with custom “olive” color
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Add the new class to your custom.css file. As in the example below, be sure to use the same color for border-color
, background-color
, and color
(the transparency effect is handled automatically by the theme).
.. admonition:: Admonition with custom "olive" color
:class: admonition-olive
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
/* <your static path>/custom.css */
div.admonition.admonition-olive {
border-color: olive;
}
div.admonition.admonition-olive > .admonition-title:before {
background-color: olive;
}
div.admonition.admonition-olive > .admonition-title:after {
color: olive;
}
Using a custom icon#
Customizing the icon uses a similar process to customizing the color: create a new CSS class in your custom.css file. The theme supports fontawesome v6 icons (“free” and “brands” sets). To use an icon, copy its unicode value into your custom class as shown in the CSS tab below:
.. admonition:: Check out my custom icon
:class: admonition-icon
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
/* <your static path>/custom.css */
div.admonition.admonition-icon > .admonition-title:after {
content: "\f24e"; /* the fa-scale icon */
}
Combining all three customizations#
Here we demonstrate an admonition with a custom icon, color, and title (and also make it collapsible). Note that the multiple admonition class names are space-separated:
YouTube
.. admonition:: YouTube
:class: dropdown admonition-youtube
.. youtube:: dQw4w9WgXcQ
/* <your static path>/custom.css */
div.admonition.admonition-youtube {
border-color: #ff0000; /* YouTube red */
}
div.admonition.admonition-youtube > .admonition-title:before {
background-color: #ff0000;
}
div.admonition.admonition-youtube > .admonition-title:after {
color: #ff0000;
content: "\f26c"; /* fa-solid fa-tv */
}
Branding and logo#
Customize logo and title#
By default the theme will use the value of project
on the left side of the header navbar.
This can be replaced by a logo image, and optionally a custom html_title
as well.
Single logo for light and dark mode#
To use a local image file, use html_logo
as specified in the Sphinx documentation.
The file must be relative to conf.py
.
For example, if your documentation had a logo in _static/logo.png
:
html_logo = "_static/logo.png"
To use an external link to an image, make sure the html_logo
begins with http
.
For example:
html_logo = "https://pydata.org/wp-content/uploads/2019/06/pydata-logo-final.png"
Different logos for light and dark mode#
You may specify a different version of your logo image for “light” and “dark” modes. This is useful if your logo image is not adapted to a dark mode (light background, not enough contrast, etc…).
To do so, use the logo["image_light"]
and logo["image_dark"]
options in html_theme_options
.
For each, provide a path relative to conf.py
like so:
# Assuming your `conf.py` has a sibling folder called `_static` with these files
html_theme_options = {
"logo": {
"image_light": "_static/logo-light.png",
"image_dark": "_static/logo-dark.png",
}
}
Note
image_light
and image_dark
will override the html_logo
setting.
If you only specify one of the light or dark variants, the un-specified variant will fall back to the value of html_logo
.
Customize logo link#
The logo links to root_doc
(usually the first page of your documentation) by default.
You can instead link to a local document or an external website.
To do so, use the html_theme_options["logo"]["link"]
option and provide a new link.
For example, to reference another local page:
html_theme_options = {
"logo": {
"link": "some/other-page",
}
}
To reference an external website, make sure your link starts with http
:
html_theme_options = {
"logo": {
"link": "https://pydata.org",
}
}
Customize logo alternative text#
You may set a custom alt text
to use with your logo to replace the default (“logo image”).
This can make the logo more accessible to those using screen readers or other assistive tech.
To do so, use html_teme_options["logo"]["alt_text"]
as in the following example:
html_theme_options = {
"logo": {
"alt_text": "foo",
}
}
Add a logo title#
To add a title in the brand section of your documentation, define a value for html_theme_options.logo["text"]
This will appear just after your logo image if it is set.
html_theme_options = {
"logo": {
"text": "My awesome documentation",
}
}
Note
The html_title
field will work as well if no logo images are specified.
Add favicons#
Deprecated since version 0.15: Support for complex and multiple favicons will be dropped in version 0.15. Instead use the sphinx-favicon extension. It provides the same functionality using more flexible parameters.
pydata_sphinx_theme
supports the standard sphinx favicon configuration, using html_favicon
.
Additionally you may add any number of browser- or device-specific favicons of any size.
To do so, use the html_theme_options["favicons"]
configuration key.
The only required argument is href
, which can be either an absolute URL (beginning with http
) or a local path relative to your html_static_path
.
In addition, you may specify a size with sizes
, specify a rel
value, and specify a color
.
See this blog post on SVG favicons for more information.
For example, below we define three extra favicons of different sizes and rel
types, and one with a specific color.
html_theme_options = {
"favicons": [
{
"rel": "icon",
"sizes": "16x16",
"href": "https://secure.example.com/favicon/favicon-16x16.png",
},
{
"rel": "icon",
"sizes": "32x32",
"href": "favicon-32x32.png",
},
{
"rel": "apple-touch-icon",
"sizes": "180x180",
"href": "apple-touch-icon-180x180.png",
"color": "#000000",
},
]
}
pydata_sphinx_theme
will add link
tags to your document’s head
section, following this pattern:
<link rel="{{ favicon.rel }}" sizes="{{ favicon.sizes }}" href="{{ favicon.href }}" color="{{ favicon.color }}">
Theme variables and CSS#
This section covers a few ways that you can control the look and feel of your theme via your own CSS and theme variables.
Custom CSS Stylesheets#
You may customize the theme’s CSS by creating a custom stylesheet that Sphinx uses to build your site. Any rules in this style-sheet will over-ride the default theme rules.
See also
For a more in-depth guide in linking static CSS and JS assets in your site, see {doc}`static_assets`.
To add a custom stylesheet, follow these steps:
Create a CSS stylesheet in
_static/css/custom.css
, and add the CSS rules you wish.Attach the stylesheet to your Sphinx build. Add the following to
conf.py
html_static_path = ['_static'] html_css_files = [ 'css/custom.css', ]
When you build your documentation, this stylesheet should now be activated.
CSS theme variables#
This theme defines several CSS variables that can be used to quickly control behavior and display across your documentation.
These are based on top of the basic Bootstrap CSS variables extended with some theme specific variables.
base variables#
In order to change a variable, follow these steps:
Add a custom CSS stylesheet. This is where we’ll configure the variables.
Underneath a
html
section, add the variables you wish to update. For example, to update the base font size, you might add this tocustom.css
:html { --pst-font-size-base: 17px; }
Important
Note that these are CSS variables and not SASS variables. The theme is defined with CSS variables, not SASS variables! Refer to the previous section if you desire a different behavior between the light and dark theme.
For a complete list of the theme variables that you may override, see the theme variables defaults CSS file:
html {
/*****************************************************************************
* Overall Layout Variables
*/
// Header height will impact the top offset for many sections
// Article header is 66% of Header
--pst-header-height: 4rem;
--pst-header-article-height: calc(var(--pst-header-height) * 2 / 3);
--pst-sidebar-secondary: 17rem;
}
/*******************************************************************************
* Breakpoints that trigger UI changes
*
* Note that media-breakpoint-down begins at the next highest level!
* So we should choose a media-breakpoint-down one *lower* than when we want to start
* example: media-breakpoint-up(md) and media-breakpoint-down(sm) trigger at the same time
* ref: https://github.com/twbs/bootstrap/issues/31214
*/
$breakpoint-sidebar-primary: lg; // When we collapse the primary sidebar
$breakpoint-sidebar-secondary: xl; // When we collapse the secondary sidebar
$breakpoint-page-width: 88rem; // taken from sphinx-basic-ng, which we are ultimately going to inherit
/*******************************************************************************
* Define the animation behaviour
*/
$animation-time: 200ms;
/*******************************************************************************
* UI shaping and padding
*/
$admonition-border-radius: 0.25rem;
html {
/*****************************************************************************
* Font features used in this theme
*/
// base font size - applied at body/html level
--pst-font-size-base: 1rem;
// heading font sizes based on bootstrap sizing
--pst-font-size-h1: 2.5rem;
--pst-font-size-h2: 2rem;
--pst-font-size-h3: 1.75rem;
--pst-font-size-h4: 1.5rem;
--pst-font-size-h5: 1.25rem;
--pst-font-size-h6: 1.1rem;
// smaller than heading font sizes
--pst-font-size-milli: 0.9rem;
// Sidebar styles
--pst-sidebar-font-size: 0.9rem;
--pst-sidebar-font-size-mobile: 1.1rem;
--pst-sidebar-header-font-size: 1.2rem;
--pst-sidebar-header-font-weight: 600;
// Admonition styles
--pst-admonition-font-weight-heading: 600;
// Font weights
--pst-font-weight-caption: 300;
--pst-font-weight-heading: 400;
// Font family
// These are adapted from https://systemfontstack.com/ */
--pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI,
"Helvetica Neue", Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji,
Segoe UI Symbol;
--pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco,
Liberation Mono, Lucida Console, monospace;
--pst-font-family-base: var(--pst-font-family-base-system);
--pst-font-family-heading: var(--pst-font-family-base-system);
--pst-font-family-monospace: var(--pst-font-family-monospace-system);
}
html {
/*****************************************************************************
* Icon
*/
// Font size across all icons
--pst-font-size-icon: 1.5rem;
// Font Awesome default icons
--pst-icon-check-circle: "\f058"; // fa-solid fa-circle-check
--pst-icon-info-circle: "\f05a"; // fa-solid fa-circle-info
--pst-icon-exclamation-triangle: "\f071"; // fa-solid fa-triangle-exclamation
--pst-icon-exclamation-circle: "\f06a"; // fa-solid fa-circle-exclamation
--pst-icon-times-circle: "\f057"; // fa-solid fa-circle-xmark
--pst-icon-lightbulb: "\f0eb"; // fa-solid fa-lightbulb
--pst-icon-download: "\f019"; // fa-solid fa-download
--pst-icon-angle-left: "\f104"; // fa-solid fa-angle-left
--pst-icon-angle-right: "\f105"; // fa-solid fa-angle-right
--pst-icon-external-link: "\f35d"; // fa-solid fa-up-right-from-square
--pst-icon-search-minus: "\f010"; // fa-solid fa-magnifying-glass-minus
--pst-icon-github: "\f09b"; // fa-brands fa-github
--pst-icon-gitlab: "\f296"; // fa-brands fa-gitlab
--pst-icon-share: "\f064"; // fa-solid fa-share
--pst-icon-bell: "\f0f3"; // fa-solid fa-bell
--pst-icon-pencil: "\f303"; // fa-solid fa-pencil
// Bootstrap icons
--pst-breadcrumb-divider: "\f105";
}
html {
/*****************************************************************************
* Admonitions
**/
--pst-icon-admonition-default: var(--pst-icon-bell);
--pst-icon-admonition-note: var(--pst-icon-info-circle);
--pst-icon-admonition-attention: var(--pst-icon-exclamation-circle);
--pst-icon-admonition-caution: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-warning: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-danger: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-error: var(--pst-icon-times-circle);
--pst-icon-admonition-hint: var(--pst-icon-lightbulb);
--pst-icon-admonition-tip: var(--pst-icon-lightbulb);
--pst-icon-admonition-important: var(--pst-icon-exclamation-circle);
--pst-icon-admonition-seealso: var(--pst-icon-share);
--pst-icon-admonition-todo: var(--pst-icon-pencil);
}
html {
/*****************************************************************************
* versionmodified
**/
--pst-icon-versionmodified-default: var(--pst-icon-exclamation-circle);
--pst-icon-versionmodified-added: var(--pst-icon-exclamation-circle);
--pst-icon-versionmodified-changed: var(--pst-icon-exclamation-circle);
--pst-icon-versionmodified-deprecated: var(--pst-icon-exclamation-circle);
}
Color variables#
There are two special color variables for primary and secondary theme colors (--pst-color-primary
and --pst-color-secondary
, respectively).
These are meant to complement one another visually across the theme, if you modify these, choose colors that look good when paired with one another.
There are also several other color variables that control color for admonitions, links, menu items, etc.
Each color variable has two values, one corresponding to the “light” and one for the “dark” theme. These are used throughout many of the theme elements to define text color, background color, etc.
Here is an overview of the colors available in the theme (change theme mode to switch from light to dark versions).
primary secondary success info warning danger background on-background surface on-surface target
To modify the colors for these variables for light and dark themes, add a custom CSS stylesheet with a structure like so:
html[data-theme="light"] {
--pst-color-primary: black;
}
html[data-theme="dark"] {
--pst-color-primary: white;
}
This theme uses shadows to convey depth in the light theme mode and opacity in the dark one. It defines 4 color variables that help build overlays in your documentation.
background
: color of the back-most surface of the documentationon-background
elements that are set on top of this background (e.g. the header navbar on dark mode).surface
elements set on the background with a light-grey color in the light theme mode. this color has been kept in the dark theme (e.g. code-block directives).on-surface
elements that are on top ofsurface
elements (e.g. sidebar directives).
The following image should help you understand these overlays:
For a complete list of the theme colors that you may override, see the PyData theme CSS colors stylesheet
.
Configure pygments theme#
As the Sphinx theme supports multiple modes, the code highlighting colors can be modified for each one of them by modifying the pygment_light_style
and pygment_dark_style
. You can check available Pygments colors on this page.
html_theme_options = {
...
"pygment_light_style": "tango",
"pygment_dark_style": "monokai"
}
Danger
The native Sphinx option pygments_style
will be overwritten by this theme.
Fonts and FontAwesome#
The theme includes the FontAwesome 6 Free
icon font (the .fa-solid, .fa-regular, .fa-brands
styles, which are used for
icon links and admonitions).
This is the only vendored font, and otherwise the theme by default relies on
available system fonts for normal body text and headers.
The default body and header fonts can be changed as follows:
Using Custom CSS Stylesheets, you can specify which fonts to use for body, header and monospace text. For example, the following can be added to a custom css file:
html { --pst-font-family-base: Verdana, var(--pst-font-family-base-system); --pst-font-family-heading: Cambria, Georgia, Times, var(--pst-font-family-base-system); --pst-font-family-monospace: Courier, var(--pst-font-family-monospace-system); }
The
*-system
variables are available to use as fallback to the default fonts.If the font you want to specify in the section above is not generally available by default, you will additionally need to ensure the font is loaded. For example, you could download and vendor the font in the
_static
directory of your Sphinx site, and then update the base template to load the font resources:Configure the template_path in your
conf.py
Create a custom
layout.html
Jinja2 template which overloads thefonts
block (example for loading the Lato font that is included in the_static/vendor
directory):{% extends "pydata_sphinx_theme/layout.html" %} {% block fonts %} <!-- add `style` or `link` tags with your CSS `@font-face` declarations here --> <!-- ... and optionally preload the `woff2` for snappier page loads --> <link rel="stylesheet" href="{{ pathto('_static/vendor/lato_latin-ext/1.44.1/index.css', 1) }}"> {% endblock %}
Your text may quickly show up as “unstyled” before the fonts are loaded. To reduce this, you may wish to explore options for preloading content, specifically the binary font files. This ensures the files will be loaded before the CSS is parsed, but should be used with care.
Light and dark themes#
You can change the major background / foreground colors of this theme according to “dark” and “light” modes. These are controlled by a button in the navigation header, with the following options:
A
light
theme with a bright background and dark text / UI elementsA
dark
theme with a dark background and light text / UI elementsauto
: the documentation theme will follow the system default that you have set
Configure default theme mode#
By default, visitors to your documentation will use the theme mode auto
.
This will choose a theme based on the user’s system settings, and default to light
if no system setting is found.
If you wish to use a different default theme mode, set the default_mode
configuration to one of auto
, dark
, light
.
For example:
html_context = {
# ...
"default_mode": "light"
}
For more information, see Light and dark themes.
Tip
To completely remove the theme management, configure default_mode
to the value you want in your documentation (light
or dark
) and then remove the theme-switcher from the navbar_end
section of the header navbar configuration:
html_theme_options {
...
# Note we have omitted `theme-switcher` below
"navbar_end": ["navbar-icon-links"]
}
Customize the CSS of light and dark themes#
Danger
Theming is still a beta feature so the variables related to color theming are likely to change in the future. No backward compatibility is guaranteed when customization is done.
To customize the CSS of page elements in a theme-dependent manner, use the html[data-theme='<THEME>']
CSS selector.
For example to define a different background color for both the light and dark themes:
/* anything related to the light theme */
html[data-theme="light"] {
/* whatever you want to change */
background: white;
}
/* anything related to the dark theme */
html[data-theme="dark"] {
/* whatever you want to change */
background: black;
}
A complete list of the colors used in this theme can be found in the CSS style section.
Theme-dependent images and content#
It is possible to use different content for light and dark mode, so that the content only shows up when a particular theme is active. This is useful if your content depends on the theme’s style, such as a PNG image with a light or a dark background.
There are two CSS helper classes to specify items on the page as theme-specific. These are:
only-dark
: Only display an element when the dark theme is active.only-light
Only display an element when the light theme is active.
For example, the following page content defines two images, each of which uses a different one of the classes above. Change the theme and a new image should be displayed.
.. image:: https://source.unsplash.com/200x200/daily?cute+cat
:class: only-dark
.. image:: https://source.unsplash.com/200x200/daily?cute+dog
:class: only-light
Images and content that work in both themes#
When the dark theme is activated, images that do not support dark mode will automatically have a white background added to ensure the image contents are visible, and their brightness will be reduced by a filter.
If your image is suitable for the dark theme, add the CSS class
only-dark
as noted above. If your image is suitable for both light and
dark theme, add the CSS class dark-light
to make your image
theme-agnostic.
For example, here’s an image without adding this helper class. Change to the dark theme and a grey background will be present.
.. image:: https://source.unsplash.com/200x200/daily?cute+cat
:class: p-2
Here’s the same image with this class added:
.. image:: https://source.unsplash.com/200x200/daily?cute+cat
:class: dark-light p-2
Define custom JavaScript to react to theme changes#
You can define a JavaScript event hook that will run your code any time the theme changes.
This is useful if you need to change elements of your page that cannot be defined by CSS rules.
For example, to change an image source (e.g., logo) whenever the data-theme
changes, a snippet like this can be used:
.. raw:: html
<script type="text/javascript">
var observer = new MutationObserver(function(mutations) {
const dark = document.documentElement.dataset.theme == 'dark';
document.getElementsByClassName('mainlogo')[0].src = dark ? '_static/my_logo_dark.svg' : "_static/my_logo_light.svg";
})
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['data-theme']});
</script>
<link rel="preload" href="_static/my_logo_dark.svg" as="image">
.. image:: _static/my_logo_light.svg
:alt: My Logo
:class: logo, mainlogo
:align: center
The JavaScript reacts to data-theme
changes to alter img
, and the link
is used to preload the dark image.
See the MutationObserver documentation for more information.
Accessibility#
Creating and publishing content that does not exclude audiences with limited abilities of various kinds is challenging, but also important, to achieve and then maintain.
While there is no one-size-fits-all solution to maintaining accessible content, this theme and documentation site use some techniques to avoid common content shortcomings.
Note
Issues and pull requests to identify or fix accessibility issues on this theme or site are heartily welcomed!
What We’ve Done#
Metadata#
Several of our documentation pages contain metadata (i.e., .. meta::
directives
in reStructuredText) giving brief summaries of the page contents. If you notice a
page that lacks metadata, please open a pull request to add it!
Colors#
Our default code highlighting styles are a11y-high-contrast-light
and
a11y-high-contrast-dark
from Quansight-Labs/accessible-pygments.
These styles are designed to be more accessible to users with limited visual abilities.
If you don’t like the look of our default code highlighting styles, there are several more
to choose from at Quansight-Labs/accessible-pygments.
What You Can Do#
In Configuration#
Some minor configuration options in a site’s conf.py
can impact the
accessibility of content generated by this theme, and Sphinx in general.
Natural Language#
If not using a more robust internationalization approach, specifying at least the baseline natural language will help assistive technology identify if the content is in a language the reader understands.
Hint
Specifying a language
will propagate to the top-level html tag.
language = "en"
Add a Site Map#
Site maps, usually served from a file called sitemap.xml are a broadly-employed approach to telling programs like search engines and assistive technologies where different content appears on a website.
If using a service like ReadTheDocs, these files will be created for you automatically, but for some of the other approaches below, it’s handy to generate a sitemap.xml locally or in CI with a tool like sphinx-sitemap.
Hint
For a simple site (no extra languages or versions), ensure sphinx-sitemap
is installed in your documentation environment, and modify your conf.py
:
extensions += ["sphinx_sitemap"]
html_baseurl = os.environ.get("SPHINX_HTML_BASE_URL", "http://127.0.0.1:8000/")
sitemap_locales = [None]
sitemap_url_scheme = "{link}"
In Your Source#
Note
Stay tuned for more ideas here as we learn more working on this site!
In the Browser#
A number of in-browser tools exist for interactively debugging the accessibility of a single page at a time, and can be useful during the content development cycle.
Built-in tools#
Most major browsers, including Firefox and Chrome include significant accessibility tooling in their development experience. Exploring these, and the modes they offer, can help to quickly pinpoint issues, and often include links to standards.
tota11y#
tota11y is an open source “bookmarklet” which modifies the currently-loaded page in-place, and highlights a number of accessibility issues.
WAVE#
WAVE is a proprietary (but gratis) browser extension which can highlight a large number of issues.
In Continuous Integration#
A number of automated tools are available for assessing glaring accessibility issues across a number of pages at once, usually with many configurable options.
Lighthouse#
Lighthouse, which provides automated assessment of basic accessibility issues in addition to search engine automation, page performance, and other best practices.
Hint
Specifically, foo-software/lighthouse-check-action is run on selected pages from the generated documentation site.
Analytics and usage services#
The theme supports several web analytics services via the analytics
option. It is configured
by passing a dictionary with options. See the sections below for relevant
options depending on the analytics provider that you want to use.
html_theme_options = {
# See below for options for each service
"analytics": analytics_options,
}
Generally speaking we recommend using Plausible over Google Analytics because it has a better story around user security and privacy. In addition, it is more open-source and transparent. In fact, you can self-host a Plausible server.
Get a self-hosted Plausible server at scientific-python.org
If your documentation is for a package that is part of the SciPy / PyData
ecosystem, they might be able to host a Plausible server for you at
https://views.scientific-python.org/<your-package>
.
To ask about this, contact them on the social media platform of your choice
and learn more at scientific-python.org.
Plausible Analytics#
plausible.io can be used to gather simple and privacy-friendly analytics for the site. To configure, you will need to provide two things:
A URL pointing to the JavaScript analytics script that is served by your Plausible server
A domain that reflects where your documentation lives
Plausible’s javascript will be included in all html pages to gather metrics.
The dashboard with analytics results will be accessible at https://<plausible-url>/<my-domain>
.
html_theme_options["analytics"] = {
# The domain you'd like to use for this analytics instance
"plausible_analytics_domain": "my-domain",
# The analytics script that is served by Plausible
"plausible_analytics_url": "https://.../script.js",
}
See also
See the Plausible Documentation for more information about this script.
Google Analytics#
If the google_analytics_id
config option is specified (like G-XXXXXXXXXX
),
Google Analytics’ javascript is included in the html pages.
html_theme_options["analytics"] = {
"google_analytics_id": "G-XXXXXXXXXX",
}
Add custom CSS and JS assets#
If you’d like to modify this theme or sections on the page, you’ll need to add custom CSS or JavaScript to your theme. Since this is a common operation we cover a few ways to do this here.
Sample site structure
In all examples below, assume we have a site structure like this:
mysphinxsite/
├── _static
│ ├── mycss.css
│ └── myjs.js
└── conf.py
First: define your html_static_path
#
Any folders that are listed in html_static_path
will be treated as containing static assets for your build.
All files within these folders will be copied to your build’s _static
folder at build time.
For example, with an html
builder, files will be copied to _build/html/_static
.
These files are flattened when they are copied, so any folder hierarchies will be lost.
Listing folders with your static assets must be done before any of the methods describe below.
When you define asset names in the methods described below, they generally assume paths that are relative to this _static
output folder.
Define a list of assets in conf.py
#
The simplest way to add JS and CSS assets is to use html_css_files
and html_js_files
in your conf.py
file.
Each can be a list of paths, relative to your html_static_path
.
They will be added to the end of the <head>
of your site.
For example:
This will cause each to be linked in your <head>
.
Add assets in your setup function#
Additionally you may add assets manually, to do so, use the app
object in the Sphinx setup()
function.
The app
object has two relevant methods here:
app.add_css_file
allows you to add CSS files directly.
app.add_js_file
allows you to add JS files directly.
Both of them expect you to add files relative to the html_static_path
.
In addition, app.add_js_file
allows you to add raw JavaScript in addition to linking files (see example below).
For example:
Use an event to add it to specific pages#
If you’d like to use logic to only add a script to certain pages, or to trigger different behavior depending on the page, use a Sphinx event hook.
This involves defining a function that runs when a particular event is emitted in the Sphinx build, and using app.connect()
to connect it to your build.
The event you’ll likely want to use is html-page-context
.
This is triggered just before the HTML for an individual page is created.
If you run app.add_js_file
or app.add_css_file
, it will only be added for that page.
For example:
Add it directly to the page content#
Finally, you can add CSS or JS directly to a page’s content.
If you’re using reStructuredText or MyST Markdown, you can use the raw
directive:
.. raw:: html
<style>
.myclass {
font-size: 2rem;
}
</style>
<script>
console.log("hi!")
</script>
If you’re using MyST Markdown, you may also simply directly include any HTML / style / script blocks in your content without using a directive.
For example:
# My title
Some text
<style>
/* Make h2 bigger */
h2 {
font-size: 3rem;
}
</style>
## A bigger title
Some other text
Build performance and size#
By default this theme includes all of your documentation links in a collapsible sidebar.
However, this may slow down your documentation builds considerably if you have a lot of documentation pages.
This is most common with documentation for projects with a large API, which use the .. autosummary::
directive to generate API documentation.
To improve the performance of your builds in these cases, first try modifying the navigation depth in the sidebar (see Navigation depth and collapsing sidebars). If that doesn’t work, try the fix in the section below.
Read the Docs functionality#
This theme comes with support for Read the Docs, a popular service for hosting documentation in the scientific Python community.
Community and contribution guide#
Information about the community behind this theme and how you can contribute.
Contributor Guide#
These pages contain information about the community that leads, supports, and develops this theme, including how you can contribute to the project.
Get started with development#
This section covers the simplest way to get started developing this theme locally so that you can contribute. It uses automation and as few steps as possible to get things done. If you’d like to do more operations manually, see Set up a manual development environment.
Workflow for contributing changes#
We follow a typical GitHub workflow of:
create a personal fork of this repo
create a branch
open a pull request
fix findings of various linters and checks
work through code review
For each pull request, the documentation is built and deployed to make it easier to review the changes in the PR. To access this, click on the Read the Docs preview in the CI/CD jobs.
The sections below cover the steps to do this in more detail.
Clone the repository#
First off you’ll need your own copy of the pydata-sphinx-theme
codebase.
You can clone it for local development like so:
Fork the repository so you have your own copy on GitHub. See the GitHub forking guide for more information.
Clone the repository locally so that you have a local copy to work from:
$ git clone https://github.com/{{ YOUR USERNAME }}/pydata-sphinx-theme $ cd pydata-sphinx-theme
Install your tools#
Building a Sphinx site uses a combination of Python and Jinja to manage HTML, SCSS, and Javascript. To simplify this process, we use a few helper tools:
The Sphinx Theme Builder to automatically perform compilation of web assets.
pre-commit for automatically enforcing code standards and quality checks before commits.
nox, for automating common development tasks.
In particular, nox
can be used to automatically create isolated local development environments with all of the correct packages installed to work on the theme.
The rest of this guide focuses on using nox
to start with a basic environment.
See also
The information on this page covers the basics to get you started, for information about manually compiling assets, see Set up a manual development environment.
Setup nox
#
To start, install nox
:
$ pip install nox
You can call nox
from the command line in order to perform common actions that are needed in building the theme.
nox
operates with isolated environments, so each action has its own packages installed in a local directory (.nox
).
For common development actions, you’ll simply need to use nox
and won’t need to set up any other packages.
Setup pre-commit
#
pre-commit
allows us to run several checks on the codebase every time a new Git commit is made.
This ensures standards and basic quality control for our code.
Install pre-commit
with the following command:
$ pip install pre-commit
then navigate to this repository’s folder and activate it like so:
$ pre-commit install
This will install the necessary dependencies to run pre-commit
every time you make a commit with Git.
Note
Your pre-commit
dependencies will be installed in the environment from which you’re calling pre-commit
, nox
, etc.
They will not be installed in the isolated environments used by nox
.
Build the documentation#
Now that you have nox
installed and cloned the repository, you should be able to build the documentation locally.
To build the documentation with nox
, run the following command:
$ nox -s docs
This will install the necessary dependencies and build the documentation located in the docs/
folder.
They will be placed in a docs/_build/html
folder.
If the docs have already been built, it will only build new pages that have been updated.
You can open one of the HTML files there to preview the documentation locally.
Alternatively, you can invoke the built-in Python http.server with:
$ python -m http.server -d docs/_build/html/
This will print a local URL that you can open in a browser to explore the HTML files.
Change content and re-build#
Now that you’ve built the documentation, edit one of the source files to see how the documentation updates with new builds.
Make an edit to a page. For example, add a word or fix a typo on any page.
Rebuild the documentation with
nox -s docs
It should go much faster this time, because nox
is re-using the old environment, and because Sphinx has cached the pages that you didn’t change.
Compile the CSS/JS assets#
The source files for CSS and JS assets are in src/pydata_sphinx_theme/assets
.
These are then built and bundled with the theme (e.g., scss
is turned into css
).
To compile the CSS/JS assets with nox
, run the following command:
$ nox -s compile
This will compile all assets and place them in the appropriate folder to be used with documentation builds.
Note
Compiled assets are not committed to git.
The sphinx-theme-builder
will bundle these assets automatically when we make a new release, but we do not manually commit these compiled assets to git history.
Run a development server#
You can combine the above two actions and run a development server so that changes to src/
are automatically bundled with the package, and the documentation is immediately reloaded in a live preview window.
To run the development server with nox
, run the following command:
$ nox -s docs-live
When working on the theme, saving changes to any of these directories:
src/js/index.js
src/scss/index.scss
docs/**/*.rst
docs/**/*.py
will cause the development server to do the following:
bundle/copy the CSS, JS, and vendored fonts
regenerate the Jinja2 macros
re-run Sphinx
Run the tests#
This theme uses pytest
for its testing, with a lightweight fixture defined
in the test_build.py
script that makes it easy to run a Sphinx build using
this theme and inspect the results.
In addition, we use pytest-regressions
to ensure that the HTML generated by the theme is what we’d expect. This module
provides a file_regression
fixture that will check the contents of an object
against a reference file on disk. If the structure of the two differs, then the
test will fail. If we expect the structure to differ, then delete the file on
disk and run the test. A new file will be created, and subsequent tests will pass.
To run the tests with nox
, run the following command:
$ nox -s test
Structure of this theme#
Location and structure of documentation#
The documentation for this theme is in the docs/
folder.
It is structured as a Sphinx documentation site.
The content is written in a combination of reStructuredText and MyST Markdown.
Location and structure of CSS/JS assets#
The CSS and JS for this theme are built for the browser from src/pydata_sphinx_theme/assets/*
with
webpack. The main entrypoints are:
CSS:
src/pydata_sphinx_theme/assets/styles/index.scss
JS:
src/pydata_sphinx_theme/assets/scripts/index.js
provides add-on Bootstrap features, as well as some custom navigation behavior
webpack:
webpack.config.js
captures the techniques for transforming the JS and CSS source files in
src/pydata_sphinx_theme/assets/*
into the production assets insrc/theme/pydata_sphinx_theme/static/
Topic guides#
These guides cover specific topics that are relevant to contributing to this theme.
Accessibility checks#
The accessibility checking tools can find a number of common HTML patterns which assistive technology can’t help users understand. We run a Lighthouse job in our CI/CD, which generates a “score” for all pages in our Kitchen Sink example documentation. The configuration for Lighthouse is in:
.github/workflows/lighthouserc.json
For more information about configuring lighthouse, see the lighthouse documentation. For more information about Accessibility in general, see Accessibility.
Web assets (CSS/JS/Fonts)#
This theme includes several web assets to ease development and design.
The configuration for our asset compilation is in webpack.config.js
.
Compile and bundle assets#
When assets are compiled, static versions are placed in various places in the theme’s static folder:
src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static
For many assets, a <hash>
is generated and appended to the end of its reference in the HTML templates of the theme.
This ensures the correct asset versions are served when viewers return to your
site after upgrading the theme.
To compile the assets and bundle them with the theme, run this command:
$ nox -s compile
Styles (SCSS) and Scripts (JS)#
There are two relevant places for CSS/JS assets:
src/pydata_sphinx_theme/assets/styles
has source files for SCSS assets. These will be compiled to CSS.src/pydata_sphinx_theme/assets/scripts
has source files for JS assets. These will be compiled to JS and import several vendored libraries (like Bootstrap).src/pydata_sphinx_theme/theme/pydata_sphinx_theme/static
has compiled versions of these assets (e.g. CSS files). This folder is not tracked in.git
history, but it is bundled with the theme’s distribution.
Vendored scripts#
We vendor several packages in addition to our own CSS and JS.
For example, Bootstrap, JQuery, and Popper.
This is configured in the webpack.config.js
file, and imported in the respective SCSS
or JS
file in our assets folder.
FontAwesome icons#
Three “styles” of the FontAwesome 6 Free
icon font are used for icon links and admonitions, and is
the only vendored
font.
It is managed as a dependency in
package.json
Copied directly into the site statics at compilation, including licenses
Partially preloaded to reduce flicker and artifacts of early icon renders
Configured in
webpack.config.js
Jinja macros#
Our Webpack build generates a collection of Jinja macros in the static/webpack-macros.html
file.
These macros are imported in the main layout.html
file, and then inserted at various places in the page to link the static assets.
Some of the assets are “preloaded”, meaning that the browser begins requesting these resources before they’re actually needed.
In particular, our JavaScript assets are preloaded in <head>
, and the scripts are actually loaded at the end of <body>
.
Ignore formatting commits with git blame
#
When making commits that are strictly formatting/style changes (e.g., after running a new version of black or running pyupgrade after dropping an old Python version), add the commit hash to .git-blame-ignore-revs
, so git blame
can ignore the change.
For more details, see:
Upgrade to bootstrap 5#
Since v0.13, pydata-sphinx-theme
has moved from Bootstrap 4 to Bootstrap 5.
This documentation will guide you through the changes we made and how you could follow the same steps in your existing documentation.
Dropping JQuery#
Bootstrap Dropped its JQuery dependency and rewrote plugins to be in regular JavaScript.
Sphinx v6 will do the same (sphinx-doc/sphinx#10070).
As a consequence, we also rewrote all our javascript to only use vanilla JavaScript.
Any documentation relying on JQuery in their custom.js
files will need to rewrite it or specifically import JQuery.
Breaking changes#
‼️ Relevant for those using a custom.css
and/or a custom.js
file!
Bootstrap changed a number of CSS classes, so if you wrote custom rules of JS logic that depended on them, it may have changed.
All of the changes from v4 to v5 are listed in their documentation.
Below list the ones that had consequences on pydata-sphinx-theme
components.
Sass#
Media query mixins parameters have changed for a more logical approach.
media-breakpoint-down()
uses the breakpoint itself instead of the next breakpoint (e.g.,media-breakpoint-down(lg)
instead ofmedia-breakpoint-down(md)
targets viewports smaller than lg).Similarly, the second parameter in
media-breakpoint-between()
also uses the breakpoint itself instead of the next breakpoint (e.g.,media-between(sm, lg)
instead ofmedia-breakpoint-between(sm, md)
targets viewports between sm and lg).
box-shadow
mixins now allownull
values and dropnone
from multiple arguments.
Content, Reboot, etc#
Nested tables do not inherit styles anymore.
.thead-light
and.thead-dark
are dropped in favor of the.table-*
variant classes which can be used for all table elements (thead
,tbody
,tfoot
,tr
,th
andtd
).Dropped
.text-justify
class. See twbs/bootstrap#29793
Utilities#
Renamed several utilities to use logical property names instead of directional names with the addition of RTL support:
Renamed
.left-*
and.right-*
to.start-*
and.end-*
.Renamed
.float-left
and.float-right
to.float-start
and.float-end
.Renamed
.border-left
and.border-right
to.border-start
and.border-end
.Renamed
.rounded-left
and.rounded-right
to.rounded-start
and.rounded-end
.Renamed
.ml-*
and.mr-*
to.ms-*
and.me-*
.Renamed
.pl-*
and.pr-*
to.ps-*
and.pe-*
.Renamed
.text-left
and.text-right
to.text-start
and.text-end
.
JavaScript#
Data attributes for all JavaScript plugins are now namespaced to help distinguish Bootstrap functionality from third parties and your own code. For example, we use
data-bs-toggle
instead ofdata-toggle
.
Update Sphinx configuration during the build#
Sometimes you want to update configuration values during a build. For example, if you want to set a default if the user hasn’t provided a value, or if you want to move the value from one keyword to another for a deprecation.
Here are some tips to do this the “right” way in Sphinx.
Update config: use app.config
#
For example, app.config.foo = "bar"
.
For some reason, when Sphinx sets things it directly uses __dict__
but this doesn’t seem to be different from the pattern described here.
Update theme options: use app.builder.theme_options
#
For example, app.builder.theme_options["logo"] = {"text": "Foo"}
.
Check if a user has provided a default: app.config._raw_config
#
The app.config._raw_config
attribute contains all of the user-provided values.
Use this if you want to check whether somebody has manually specified something.
For example, "somekey" in app.config._raw_config
will be False
if a user has not provided that option.
You can also check app.config.overrides
for any CLI-provided overrides.
We bundle both checks in a helper function called _config_provided_by_user
.
Avoid the config-inited
event#
This theme is activated after config-inited
is triggered, so if you write an event that depends on it in this theme, then it will never occur.
The earliest event you can use is builder-inited
.
Update JavaScript dependencies and their versions#
There are two kinds of dependency definitions in this theme:
package.json
contains the base dependencies for this theme. They are broken down into a few categories likedependencies
anddevDependencies
. It is edited by the maintainers.package-lock.json
contains the complete frozen dependency chain for this theme, including all sub-dependencies of our base dependencies. It is automatically generated.
To update or add a JS dependency, follow these steps:
Edit
package.json
by adding or modifying a dependency.Re-generate
package-lock.json
in order to create a new set of frozen dependencies for the theme. To do this, run the following command from the Sphinx Theme Builder.stb npm install --include=dev
Commit both files to the repository. When new people pull in the latest commits, their
npm
environment will automatically update according to the new lockfile.
Galleries and the gallery-grid
directive#
There are a few places where we use sphinx-design
to generate “galleries” of grids with structured text and images.
We’ve created a little Sphinx directive to make it easier to repeat this process in our documentation and to avoid repeating ourselves too much.
It is located in the docs/scripts/
folder in a dedicated module, and re-used throughout our documentation.
The example gallery#
This theme’s documentation contains a gallery of sites that use this theme for their documentation. The images are automatically generated during ReadTheDocs builds, but are not automatically generated on local or test builds (to save time).
If you build the documentation locally without first generating these images you may get Sphinx warnings or errors, but this should be fine as long as the images build on ReadTheDocs tests.
Download gallery images locally#
If you’d like to build these images locally to preview in the theme, follow these steps:
Install playwright and the Chromium browser add-on:
$ pip install playwright $ playwright install chromium
Execute the gallery generation script from the repository root:
$ python ./docs/scripts/generate_gallery_images.py
Note
The newly generated images will be pushed to the distant repository.
Internationalization#
Warning
This theme is still in the process of setting up internationalization.
Some of the text below may not yet be correct (for example, we do not yet have a locales/
directory).
Follow these issues to track progress:
Internationalization (I18N) and localization (L10N) is performed using Gettext.
Types of files#
Gettext reads a program’s source and extracts text that has been marked as translatable, known as “source strings. Gettext uses three types of files:
- PO file (
.po
) A Portable Object (PO) file is made up of many entries. Each entry holds the relation between a source string and its translation.
msgid
contains the source string, andmsgstr
contains the translation. In a given PO file, all translations are expressed in a single target language. PO files are also known as “message catalogs”.Entries begin with comments, on lines starting with the character
#
. Comments are created and maintained by Gettext. Comment lines starting with#:
contain references to the program’s source. These references allow a human translator to find the source strings in their original context. Comment lines starting with#,
contain flags likepython-format
, which indicates that the source string contains placeholders like%(copyright)s
.- POT file (
.pot
) A Portable Object Template (POT) file is the same as a PO file, except the translations are empty, so that it can be used as a template for new languages.
- MO file (
.mo
) A Machine Object (MO) file is a binary version of a PO file. PO files are compiled to MO files, which are required by Gettext.
Mark natural language text as translateable#
All natural language text must be marked as translatable, so that it can be extracted by Gettext and translated by humans.
Jinja2 provides a trans
block and a _()
function to mark text as translatable.
Please refer to the Jinja2 documentation.
Remember to manually escape variables if needed.
Any text that is marked in this way will be discoverable by gettext
and used to generate .po
files (see below for information).
Once you’ve marked text as translateable, complete the steps for Add or change natural language text.
Add or change natural language text#
These steps cover how to add or change text that has been marked as translateable.
Edit the natural language text as desired. Ensure that it is {ref}`marked as translateable <adding-natural-language-text>`.
Generate/update the message catalog template (
POT
file) with the PyBabel extract command:pybabel extract . -F babel.cfg -o src/pydata_sphinx_theme/locale/sphinx.pot -k '_ __ l_ lazy_gettext'
To run this in ``.nox``:
nox -s translate -- extract
.Update the message catalogs (
PO
files) with the PyBabel update command:pybabel update -i src/pydata_sphinx_theme/locale/sphinx.pot -d src/pydata_sphinx_theme/locale -D sphinx
To run this in ``.nox``:
nox -s translate -- update
.
This will update these files with new information about the position and text of the language you have modified.
If you only change non-translatable text (like HTML markup), the extract and update commands will only update the positions (line numbers) of the translatable strings. Updating positions is optional - the line numbers are to inform the human translator, not to perform the translation.
If you change translatable strings, the extract command will extract the new or updated strings to the POT file, and the update command will try to fuzzy match the new or updated strings with existing translations in the PO files. If there is a fuzzy match, a comment like #, fuzzy is added before the matched entry. Otherwise, a new entry is added and needs to be translated.
Add translations to translateable text#
Once text has been marked as translateable, and PO
files have been generated for it, we may add translations for new languages for the phrase.
This section covers how to do so.
Note
These steps use the Spanish language as an example.
To translate the theme to another language, replace es
with the language’s two-letter lowercase ISO 639-1 code.
If the language’s code matches no sub-directory of the pydata_sphinx_theme/locale directory, initialize the language’s message catalog (PO file) with PyBabel init:
pybabel init -i src/pydata_sphinx_theme/locale/sphinx.pot -d src/pydata_sphinx_theme/locale -D sphinx -l es
To run this in ``.nox``:
nox -s translate -- init es
Edit the language’s message catalog at
pydata_sphinx_theme/locale/es/LC_MESSAGES/sphinx.po
. For each source string introduced by themsgid
keyword, add its translation after themsgstr
keyword.Compile the message catalogs of every language. This creates or updates the MO files with PyBabel compile:
pybabel compile -d src/pydata_sphinx_theme/locale -D sphinx
To run this in ``.nox``:
nox -s translate -- compile
.
Translation tips#
Translate phrases, not words#
Full sentences and clauses must always be a single translatable string.
Otherwise, you can get next page
translated as suivant page
instead of as page suivante
, etc.
Deal with variables and markup in translations#
If a variable (like the edit_page_provider_name
theme option) is used as part of a phrase, it must be included within the translatable string.
Otherwise, the word order in other languages can be incorrect.
In a Jinja template, simply surround the translatable string with {% trans variable=variable %}
and {% endtrans %}}`.
For example: ``{% trans provider=provider %}Edit on {{ provider }}{% endtrans %}
The translatable string is extracted as the Python format string Edit on %(provider)s
.
This is so that the same translatable string can be used in both Python code and Jinja templates.
It is the translator’s responsibility to use %(provider)s
verbatim in the translation.
If a non-translatable word or token (like HTML markup) is used as part of a phrase, it must also be included within the translatable string.
For example: {% trans theme_version=theme_version|e %}Built with the <a href="https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html">PyData Sphinx Theme</a> {{ theme_version }}.{% endtrans %}
It is the translator’s responsibility to use the HTML markup verbatim in the translation.
References#
I18N and L10N are deep topics. Here, we only cover the bare minimum needed to fulfill basics technical tasks. You might like:
Internationalis(z)ing Code by Computerphile on YouTube
Falsehoods Programmers Believe About Language by Ben Hamill
Update our kitchen sink#
The kitchen sink reference is for demonstrating as much syntax and style for Sphinx builds as possible.
It is copied directly from the sphinx-themes.org
documentation so that we use standardized reference docs compared with other communities.
The source files for these pages are stored in the sphinx-themes.org
repository.
To update the kitchen sink source files, there is a helper Python script that will loop through the known kitchen sink files and copy over the latest text. To use it, run the following from the root of the repository:
Set up a manual development environment#
If you prefer not to use automation tools like nox
, or want to have more control over the specific version of packages that you’d like installed, you may also manually set up a development environment locally.
To do so, follow the instructions on this page.
Create a new development environment#
This is optional, but it’s best to start with a fresh development environment so that you’ve isolated the packages that you’re using for this repository.
To do so, use a tool like conda, mamba, or virtualenv.
Clone the repository locally#
First clone this repository from the pydata
organization, or from a fork that you have created:
$ git clone https://github.com/pydata/pydata-sphinx-theme
$ cd pydata-sphinx-theme
Install the sphinx-theme-builder
#
We use the sphinx-theme-builder
to install nodejs
locally and to compile all CSS and JS assets needed for the theme.
Install it like so (note the cli
option so that we can run it from the command line):
$ pip install sphinx-theme-builder[cli]
Install this theme locally#
Next, install this theme locally so that we have the necessary dependencies to build the documentation and testing suite:
$ pip install -e .[dev]
Note that the sphinx-theme-builder
will automatically install a local copy of nodejs
for building the theme’s assets.
This will be placed in a .nodeenv
folder.
Build the documentation#
To manually build the documentation, run the following command:
$ sphinx-build docs docs/_build/html
Compile web assets (JS/CSS)#
To compile the javascript and CSS assets for the theme, run the following command:
$ stb compile
This will compile everything in the src/pydata_sphinx_theme/assets
folder and place them in the appropriate places in our theme’s folder structure.
Start a live-server to build and serve your documentation#
To manually open a server to watch your documentation for changes, build them, and display them locally in a browser, run this command:
$ stb serve docs --open-browser
Run the tests#
To manually run the tests for this theme, first set up your environment locally, and then run:
$ pytest
Using nox
#
Here are a few extra tips for using nox
.
See also
The nox
command line documentation has a lot of helpful tips for extra functionality you can enable with the CLI.
Re-install dependencies#
To re-execute the installation commands, use this pattern:
$ nox -s docs -- reinstall
Or to completely remove the environment generated by nox
and start from scratch:
$ rm -rf .nox/docs
Use nox
with your global environment#
If you’d like to use nox
with your global environment (the one from which you are calling nox
), you can do so with:
$ nox --force-venv-backend none
# alternatively:
$ nox --no-venv
Using none
will re-use your current global environment.
See
the nox documentation for more details.
Page-level configuration#
In some areas we support page-level configuration to control behavior on a per-page basis.
Try to make this configuration follow the html_theme_options
structure of our configuration as much as possibl.
Begin them with html_theme
, and separate “nested” configuration sections with periods (.
).
This is similar to how the TOML language defines nested configuration.
For example, to remove the secondary sidebar, we use a page metadata key like this:
:html_theme.sidebar_secondary.remove: true
Note how the period naturally separates nested sections, and looks very similar to what we’d expect if we put this in a Python dictionary in conf.py
:
html_theme_options = {
"sidebar_secondary": {"remove": "true"}
}
Using pre-commit
#
Here are a few tips for using pre-commit
:
Skip the pre-commit checks#
Run the following command:
$ git commit --no-verify
Run pre-commit on all files#
By default, pre-commit
will run its checks on files that have been modified in a commit.
To instead run it on all files, use this command:
$ pre-commit run --all-files
# Alternatively
$ pre-commit run -a
PyData package support#
This theme is designed by and for the PyData community, and so there are a few places where we special-case support for packages in this community.
We define CSS rules that ensure PyData content in Sphinx looks reasonable on both light and dark themes. If we hear reports from maintainers that we could change something in this theme that would make their documentation look better, and if this change is sustainable for us, then we should do so.
We store our PyData-specific SCSS in two relevant files, both in the src/pydata_sphinx_theme/assets/styles/
folder:
extensions/_execution.scss
- styles for Sphinx libraries that execute and insert code into the documentation. For example, MyST-NB, Jupyter Sphinx, and the Matplotlibplot
directive. Most PyData support should go here via generic improvements that all packages benefit from.extensions/_pydata.scss
- styles for specific libraries in the PyData ecosystem. In general we should try to keep this minimal because it is mostly special-casing single library quirks.
Expected build warnings#
In our CI workflow, we use a script to check for any warnings raised by Sphinx to assert that the only warnings are expected ones. The list of expected warnings can be found in :code:tests/warning_list.txt
. To add a new entry, copy/paste the warning message (the message beginning with :code:WARNING:
) to the bottom of the file.
For example if you get:
Unexpected warning: C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\site-packages\pandas\core\frame.py:docstring of pandas.core.frame.DataFrame.groupby:42: WARNING: undefined label: 'groupby.transform'
Add the following to the txt file:
WARNING: undefined label: 'groupby.transform'
Merge and review policy#
Our policy for merging and reviewing describes how we review one another’s work, and when we allow others to merge changes into our main codebase. It tries to balance between a few goals:
Iterate on PRs and merge them relatively quickly, so that we reduce the debt associated with long-lasting PRs.
Give enough time for others to provide their input and guide the PR itself, and to ensure that we aren’t creating a problem with the PR.
Value iterative improvement over creating the perfect Pull Request, so that we do not burden contributors with cumbersome discussion and minor revision requests.
Recognize that we all have limited time and resources, and so cannot guarantee a full quality assurance process each time.
Give general preference to the opinions from maintainers of projects in the PyData ecosystem, as a key stakeholder community of this theme.
We follow these guidelines in order to achieve these goals:
Assume that all maintainers are acting in good faith and will use their best judgment to make decisions in the best interests of the repository.
We can and will make mistakes, and so encourage best practices in testing and documentation to guard against this.
It’s important to share information, so give a best effort at telling others about work that you’re doing.
It’s best to discuss and agree on important decisions at a high level before implementation, so give a best effort at providing time and invitation for others to provide feedback.
Policy for moderate changes#
These are changes that make modest changes to new or existing functionality, but that aren’t going to significantly change the default behavior of the theme, user configuration, etc. This is the majority of changes that we make.
PRs should:
Refer to (and ideally close) an issue that describes the problem this change is solving.
Have relevant testing and documentation coverage.
They can be merged when the above conditions are met, and one of these things happens:
The PR has at least one approval from a core maintainer that isn’t the PR author
The PR author has signaled their intent to merge unless there are objections, and 48 hours have passed since that comment.
Policy for major new features and breaking changes#
These are changes that significantly alter the experience of the user, or that add significant complexity to the codebase.
All of the above, but PRs must have approval from at least one other core maintainer before merging. In addition, the PR author should put extra effort into ensuring that the relevant stakeholders are notified about a change, so they can gauge its impact and provide feedback.
Policy For minor changes and bugfixes#
These are small changes that might be noticeable to the user, but in a way that is clearly an improvement. They generally shouldn’t touch too many lines of code.
Update the relevant tests and documentation, but PR authors are welcome to self-merge whenever they like without PR approval.
Making releases#
Our goals#
Our release policy describes how we decide when to make a new public release of the theme so that other projects may use new features and improvements. It tries to balance between these goals:
Release relatively frequently, so that we provide a continuous stream of improvement to projects that use the theme, and minimize the effort needed to upgrade.
Do not surprise people (especially with negative surprises) and provide time for projects to provide feedback about upcoming features.
Minimize the toil and complexity associated with releases, and reduce information silos and bottlenecks associated with them.
When to make a release#
Anybody is encouraged to make a new release if:
It has been more than a month since the last release.
OR a significant change has been made to our code that warrants a release.
AND there are no open issues with a block-release label.
Release candidates#
If a release includes complex or many changes (especially in JavaScript), make a release candidate
and ask for feedback from users.
This is important because we do not test much of the CSS and JavaScript-based functionality in our testing infrastructure.
After a week or so, if there are no blocking issues that have been opened since the Release Candidate, we can make a full release.
Process for making a release#
This theme uses GitHub tags and releases to automatically push new releases to PyPI. Follow these steps to make a release:
(optionally) Create a GitHub milestones to organize the issues that should be resolved as part of a new release.
Decide if it’s time to make a release be reading When to make a release and decide if it is time for a release.
Copy the release checklist into a new issue. We have a release checklist in our wiki.
Complete the checklist. That’s it!
Choosing a version increment#
We use semantic versioning to decide whether it’s a major, minor, or patch bump. Before we have released 1.0
, treat minor versions as breaking releases, and patch versions as feature / patch releases. If this is a release candidate, tag it like 0.1rc1
.
Supported Python and Sphinx versions#
Python and Sphinx are the two primary dependencies of this theme. We have special practices for deciding which versions of these we support (especially Sphinx, which tends to release breaking changes).
Supported Python versions#
For releases of Python, we aim to follow this approach[1]:
For a new major/minor release of this theme, we support any minor Python versions released in the last 3.5 years (42 months), as defined in the EOL schedule for Python[2].
We define “support” as testing against each of these versions, so that users can be assured they will not trigger any bugs.
For example, if we made a minor release tomorrow, we’d look at the EOL schedule for Python and support all of the versions that fall within a 3.5 year window.
Supported Sphinx versions#
For supporting versions of Sphinx, we aim to follow this approach:
We support the latest released version of Sphinx that is older than 6 months. We unofficially support earlier released versions of Sphinx, but may increase the lower-bound in our dependency pin without warning if needed[2].
When a new pre-release of Sphinx is released, we should follow these steps:
Ensure that our tests are passing. We run our tests with any pre-releases of Sphinx, so we can test major errors quickly and make the necessary changes.
Look at the Sphinx Changelog and make sure there are no changes that might break things that aren’t captured by our tests.
Look at the deprecated API changes and make sure there are no changes that might break things that aren’t captured by our tests.
Look at the docutils changelog in case there’s a new docutils version supported that breaks something.
Note
This theme does not pin the upper version of Sphinx that it supports. If a Sphinx release causes major breaking changes for our users, and we do not have the capacity to update our code and release a fix, we may temporarily pin the upper bound of Sphinx we support until this is fixed.
Our support for Python versions is inspired by NEP 029.
These policies are goals, but not promises. We are a volunteer-led community with limited time. Consider these sections to be our intention, but we recognize that we may not always be able to meet these criteria if we do not have capacity to do so. We welcome contributions from others to help us more sustainably meet these goals!
Contributors to this theme#
This theme is supported and developed by various members of the PyData community.
Collaborators on the repository#
Here is a grid of collaborators on our GitHub repository. We don’t yet have formal “team definitions” so this is mostly a reflection of who has commit rights to the repository.
Financial support#
Support and development for this theme has been funded by one NumFocus Small Development Grant on behalf of several communities in the PyData ecosystem.
Theme logo#
Thanks to @drammock for initial design of the theme logo.
Inspiration for design and UX#
To build this theme we drew inspiration from other great projects on the web that we would like to acknowledge here. When making new decisions about design and UI/UX, we often consult these themes to see what they’re doing.
Examples#
Several example pages to demonstrate the functionality of this theme when used alongside other Sphinx extensions.
Examples#
These sections show off various features and styles of this theme. They should help you understand how this theme behaves and how others are using it. See the sections in the primary sidebar and below to explore.
Gallery of sites using this theme#
This is a gallery of documentation built on top of the pydata-sphinx-theme
.
If you’d like to add your documentation to this list, simply add an entry to this gallery.yaml file and open a Pull Request to add it.
Indexing and selecting data with Pandas#
Note
This is an example page with excerpts from the pandas docs, for some “real world” content. But including it here apart from the rest of the pandas docs will mean that some of the links won’t work, and not all code examples are shown with their complete outputs.
The axis labeling information in pandas objects serves many purposes:
Identifies data (i.e. provides metadata) using known indicators, important for analysis, visualization, and interactive console display.
Enables automatic and explicit data alignment.
Allows intuitive getting and setting of subsets of the data set.
In this section, we will focus on the final point: namely, how to slice, dice, and generally get and set subsets of pandas objects. The primary focus will be on Series and DataFrame as they have received more development attention in this area.
Note
The Python and NumPy indexing operators []
and attribute operator .
provide quick and easy access to pandas data structures across a wide range
of use cases. This makes interactive work intuitive, as there’s little new
to learn if you already know how to deal with Python dictionaries and NumPy
arrays. However, since the type of the data to be accessed isn’t known in
advance, directly using standard operators has some optimization limits. For
production code, we recommended that you take advantage of the optimized
pandas data access methods exposed in this chapter.
Warning
Whether a copy or a reference is returned for a setting operation, may
depend on the context. This is sometimes called chained assignment
and
should be avoided. See Returning a View versus Copy.
Different choices for indexing#
Object selection has had a number of user-requested additions in order to support more explicit location based indexing. Pandas now supports three types of multi-axis indexing.
.loc
is primarily label based, but may also be used with a boolean array..loc
will raiseKeyError
when the items are not found. Allowed inputs are:A single label, e.g.
5
or'a'
(Note that5
is interpreted as a label of the index. This use is not an integer position along the index.).A list or array of labels
['a', 'b', 'c']
.A slice object with labels
'a':'f'
(Note that contrary to usual python slices, both the start and the stop are included, when present in the index!)A boolean array
A
callable
function with one argument (the calling Series or DataFrame) and that returns valid output for indexing (one of the above).
See more at Selection by Label.
.iloc
is primarily integer position based (from0
tolength-1
of the axis), but may also be used with a boolean array..iloc
will raiseIndexError
if a requested indexer is out-of-bounds, except slice indexers which allow out-of-bounds indexing. (this conforms with Python/NumPy slice semantics). Allowed inputs are:An integer e.g.
5
.A list or array of integers
[4, 3, 0]
.A slice object with ints
1:7
.A boolean array.
A
callable
function with one argument (the calling Series or DataFrame) and that returns valid output for indexing (one of the above).
.loc
,.iloc
, and also[]
indexing can accept acallable
as indexer. See more at Selection By Callable.
Getting values from an object with multi-axes selection uses the following
notation (using .loc
as an example, but the following applies to .iloc
as
well). Any of the axes accessors may be the null slice :
. Axes left out of
the specification are assumed to be :
, e.g. p.loc['a']
is equivalent to
p.loc['a', :, :]
.
Object Type |
Indexers |
---|---|
Series |
|
DataFrame |
|
Basics#
As mentioned when introducing the data structures in the last section,
the primary function of indexing with []
(a.k.a. __getitem__
for those familiar with implementing class behavior in Python) is selecting out
lower-dimensional slices. The following table shows return type values when
indexing pandas objects with []
:
Object Type |
Selection |
Return Value Type |
---|---|---|
Series |
|
scalar value |
DataFrame |
|
|
Here we construct a simple time series data set to use for illustrating the indexing functionality:
>>> dates = pd.date_range('1/1/2000', periods=8)
>>> df = pd.DataFrame(np.random.randn(8, 4),
... index=dates, columns=['A', 'B', 'C', 'D'])
...
>>> df
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
Note
None of the indexing functionality is time series specific unless specifically stated.
Thus, as per above, we have the most basic indexing using []
:
>>> s = df['A']
>>> s[dates[5]]
-0.6736897080883706
You can pass a list of columns to []
to select columns in that order.
If a column is not contained in the DataFrame, an exception will be
raised. Multiple columns can also be set in this manner:
>>> df
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
>>> df[['B', 'A']] = df[['A', 'B']]
>>> df
A B C D
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
You may find this useful for applying a transform (in-place) to a subset of the columns.
Warning
pandas aligns all AXES when setting Series
and DataFrame
from .loc
, and .iloc
.
This will not modify df
because the column alignment is before value assignment.
>>> df[['A', 'B']]
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
>>> df.loc[:, ['B', 'A']] = df[['A', 'B']]
>>> df[['A', 'B']]
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
The correct way to swap column values is by using raw values:
>>> df.loc[:, ['B', 'A']] = df[['A', 'B']].to_numpy()
>>> df[['A', 'B']]
A B
2000-01-01 0.469112 -0.282863
2000-01-02 1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04 0.721555 -0.706771
2000-01-05 -0.424972 0.567020
2000-01-06 -0.673690 0.113648
2000-01-07 0.404705 0.577046
2000-01-08 -0.370647 -1.157892
Attribute access#
You may access an index on a Series
or column on a DataFrame
directly
as an attribute:
sa = pd.Series([1, 2, 3], index=list('abc'))
dfa = df.copy()
sa.b
dfa.A
>>> sa.a = 5
>>> sa
a 5
b 2
c 3
dtype: int64
>>> dfa.A = list(range(len(dfa.index))) # ok if A already exists
>>> dfa
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
>>> dfa['A'] = list(range(len(dfa.index))) # use this form to create a new column
>>> dfa
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
Warning
You can use this access only if the index element is a valid Python identifier, e.g.
s.1
is not allowed. See here for an explanation of valid identifiers.The attribute will not be available if it conflicts with an existing method name, e.g.
s.min
is not allowed, buts['min']
is possible.Similarly, the attribute will not be available if it conflicts with any of the following list:
index
,major_axis
,minor_axis
,items
.In any of these cases, standard indexing will still work, e.g.
s['1']
,s['min']
, ands['index']
will access the corresponding element or column.
If you are using the IPython environment, you may also use tab-completion to see these accessible attributes.
You can also assign a dict
to a row of a DataFrame
:
>>> x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
>>> x.iloc[1] = {'x': 9, 'y': 99}
>>> x
x y
0 1 3
1 9 99
2 3 5
You can use attribute access to modify an existing element of a Series or column of a DataFrame, but be careful;
if you try to use attribute access to create a new column, it creates a new attribute rather than a
new column. In 0.21.0 and later, this will raise a UserWarning
:
>>> df = pd.DataFrame({'one': [1., 2., 3.]})
>>> df.two = [4, 5, 6]
UserWarning: Pandas doesn't allow Series to be assigned into nonexistent columns - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute_access
>>> df
one
0 1.0
1 2.0
2 3.0
Selection by label#
Warning
Whether a copy or a reference is returned for a setting operation, may depend on the context.
This is sometimes called chained assignment
and should be avoided.
See Returning a View versus Copy.
Warning
.loc
is strict when you present slicers that are not compatible (or convertible) with the index type. For example using integers in aDatetimeIndex
. These will raise aTypeError
.
dfl = pd.DataFrame(np.random.randn(5, 4),
columns=list('ABCD'),
index=pd.date_range('20130101', periods=5))
dfl
>>> dfl.loc[2:3]
TypeError: cannot do slice indexing on <class 'pandas.tseries.index.DatetimeIndex'> with these indexers [2] of <type 'int'>
String likes in slicing can be convertible to the type of the index and lead to natural slicing.
dfl.loc['20130102':'20130104']
pandas provides a suite of methods in order to have purely label based indexing. This is a strict inclusion based protocol.
Every label asked for must be in the index, or a KeyError
will be raised.
When slicing, both the start bound AND the stop bound are included, if present in the index.
Integers are valid labels, but they refer to the label and not the position.
The .loc
attribute is the primary access method. The following are valid inputs:
A single label, e.g.
5
or'a'
(Note that5
is interpreted as a label of the index. This use is not an integer position along the index.).A list or array of labels
['a', 'b', 'c']
.A slice object with labels
'a':'f'
(Note that contrary to usual python slices, both the start and the stop are included, when present in the index! See Slicing with labels.A boolean array.
A
callable
, see Selection By Callable.
>>> s1 = pd.Series(np.random.randn(6), index=list('abcdef'))
>>> s1
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
>>> s1.loc['c':]
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
>>> s1.loc['b']
1.3403088497993827
Note that setting works as well:
>>> s1.loc['c':] = 0
>>> s1
a 1.431256
b 1.340309
c 0.000000
d 0.000000
e 0.000000
f 0.000000
dtype: float64
With a DataFrame:
>>> df1 = pd.DataFrame(np.random.randn(6, 4),
.... index=list('abcdef'),
.... columns=list('ABCD'))
....
>>> df1
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
c 1.024180 0.569605 0.875906 -2.211372
d 0.974466 -2.006747 -0.410001 -0.078638
e 0.545952 -1.219217 -1.226825 0.769804
f -1.281247 -0.727707 -0.121306 -0.097883
>>> df1.loc[['a', 'b', 'd'], :]
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
Slicing with labels#
When using .loc
with slices, if both the start and the stop labels are
present in the index, then elements located between the two (including them)
are returned:
>>> s = pd.Series(list('abcde'), index=[0, 3, 2, 5, 4])
>>> s.loc[3:5]
3 b
2 c
5 d
dtype: object
If at least one of the two is absent, but the index is sorted, and can be compared against start and stop labels, then slicing will still work as expected, by selecting labels which rank between the two:
>>> s.sort_index()
0 a
2 c
3 b
4 e
5 d
dtype: object
>>> s.sort_index().loc[1:6]
2 c
3 b
4 e
5 d
dtype: object
However, if at least one of the two is absent and the index is not sorted, an
error will be raised (since doing otherwise would be computationally expensive,
as well as potentially ambiguous for mixed type indexes). For instance, in the
above example, s.loc[1:6]
would raise KeyError
.
Selection by position#
Warning
Whether a copy or a reference is returned for a setting operation, may depend on the context.
This is sometimes called chained assignment
and should be avoided.
See Returning a View versus Copy.
Pandas provides a suite of methods in order to get purely integer based indexing. The semantics follow closely Python and NumPy slicing. These are 0-based
indexing. When slicing, the start bound is included, while the upper bound is excluded. Trying to use a non-integer, even a valid label will raise an IndexError
.
The .iloc
attribute is the primary access method. The following are valid inputs:
An integer e.g.
5
.A list or array of integers
[4, 3, 0]
.A slice object with ints
1:7
.A boolean array.
A
callable
, see Selection By Callable.
>>> s1 = pd.Series(np.random.randn(5), index=list(range(0, 10, 2)))
>>> s1
0 0.695775
2 0.341734
4 0.959726
6 -1.110336
8 -0.619976
dtype: float64
>>> s1.iloc[:3]
0 0.695775
2 0.341734
4 0.959726
dtype: float64
>>> s1.iloc[3]
-1.110336102891167
Note that setting works as well:
s1.iloc[:3] = 0
s1
With a DataFrame:
df1 = pd.DataFrame(np.random.randn(6, 4),
index=list(range(0, 12, 2)),
columns=list(range(0, 8, 2)))
df1
Select via integer slicing:
df1.iloc[:3]
df1.iloc[1:5, 2:4]
Select via integer list:
df1.iloc[[1, 3, 5], [1, 3]]
df1.iloc[1:3, :]
df1.iloc[:, 1:3]
# this is also equivalent to ``df1.iat[1,1]``
df1.iloc[1, 1]
For getting a cross section using an integer position (equiv to df.xs(1)
):
df1.iloc[1]
Out of range slice indexes are handled gracefully just as in Python/Numpy.
# these are allowed in python/numpy.
x = list('abcdef')
x
x[4:10]
x[8:10]
s = pd.Series(x)
s
s.iloc[4:10]
s.iloc[8:10]
Note that using slices that go out of bounds can result in an empty axis (e.g. an empty DataFrame being returned).
dfl = pd.DataFrame(np.random.randn(5, 2), columns=list('AB'))
dfl
dfl.iloc[:, 2:3]
dfl.iloc[:, 1:3]
dfl.iloc[4:6]
A single indexer that is out of bounds will raise an IndexError
.
A list of indexers where any element is out of bounds will raise an
IndexError
.
>>> dfl.iloc[[4, 5, 6]]
IndexError: positional indexers are out-of-bounds
>>> dfl.iloc[:, 4]
IndexError: single positional indexer is out-of-bounds
Selection by callable#
.loc
, .iloc
, and also []
indexing can accept a callable
as indexer.
The callable
must be a function with one argument (the calling Series or DataFrame) that returns valid output for indexing.
>>> df1 = pd.DataFrame(np.random.randn(6, 4),
.... index=list('abcdef'),
.... columns=list('ABCD'))
....
>>> df1
A B C D
a -0.023688 2.410179 1.450520 0.206053
b -0.251905 -2.213588 1.063327 1.266143
c 0.299368 -0.863838 0.408204 -1.048089
d -0.025747 -0.988387 0.094055 1.262731
e 1.289997 0.082423 -0.055758 0.536580
f -0.489682 0.369374 -0.034571 -2.484478
>>> df1.loc[lambda df: df['A'] > 0, :]
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
>>> df1.loc[:, lambda df: ['A', 'B']]
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
>>> df1.iloc[:, lambda df: [0, 1]]
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
>>> df1[lambda df: df.columns[0]]
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
You can use callable indexing in Series
.
df1['A'].loc[lambda s: s > 0]
Using these methods / indexers, you can chain data selection operations without using a temporary variable.
bb = pd.read_csv('data/baseball.csv', index_col='id')
(bb.groupby(['year', 'team']).sum()
.loc[lambda df: df['r'] > 100])
Boolean indexing#
Another common operation is the use of boolean vectors to filter the data.
The operators are: |
for or
, &
for and
, and ~
for not
.
These must be grouped by using parentheses, since by default Python will
evaluate an expression such as df['A'] > 2 & df['B'] < 3
as
df['A'] > (2 & df['B']) < 3
, while the desired evaluation order is
(df['A > 2) & (df['B'] < 3)
.
Using a boolean vector to index a Series works exactly as in a NumPy ndarray:
>>> s = pd.Series(range(-3, 4))
>>> s
0 -3
1 -2
2 -1
3 0
4 1
5 2
6 3
dtype: int64
>>> s[s > 0]
4 1
5 2
6 3
dtype: int64
>>> s[(s < -1) | (s > 0.5)]
0 -3
1 -2
4 1
5 2
6 3
dtype: int64
>>> s[~(s < 0)]
3 0
4 1
5 2
6 3
dtype: int64
You may select rows from a DataFrame using a boolean vector the same length as the DataFrame’s index (for example, something derived from one of the columns of the DataFrame):
df[df['A'] > 0]
List comprehensions and the map
method of Series can also be used to produce
more complex criteria:
df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
'b': ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
'c': np.random.randn(7)})
# only want 'two' or 'three'
criterion = df2['a'].map(lambda x: x.startswith('t'))
df2[criterion]
# equivalent but slower
df2[[x.startswith('t') for x in df2['a']]]
# Multiple criteria
df2[criterion & (df2['b'] == 'x')]
With the choice methods Selection by Label, Selection by Position you may select along more than one axis using boolean vectors combined with other indexing expressions.
df2.loc[criterion & (df2['b'] == 'x'), 'b':'c']
The query()
Method#
DataFrame
objects have a query()
method that allows selection using an expression.
You can get the value of the frame where column b
has values
between the values of columns a
and c
. For example:
n = 10
df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
df
# pure python
df[(df['a'] < df['b']) & (df['b'] < df['c'])]
# query
df.query('(a < b) & (b < c)')
Do the same thing but fall back on a named index if there is no column
with the name a
.
df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
df.index.name = 'a'
df
df.query('a < b and b < c')
If instead you don’t want to or cannot name your index, you can use the name
index
in your query expression:
df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
df
df.query('index < b < c')
Note
If the name of your index overlaps with a column name, the column name is given precedence. For example,
df = pd.DataFrame({'a': np.random.randint(5, size=5)})
df.index.name = 'a'
df.query('a > 2') # uses the column 'a', not the index
You can still use the index in a query expression by using the special identifier ‘index’:
df.query('index > 2')
If for some reason you have a column named index
, then you can refer to
the index as ilevel_0
as well, but at this point you should consider
renaming your columns to something less ambiguous.
MultiIndex
query()
Syntax#
You can also use the levels of a DataFrame
with a
MultiIndex
as if they were columns in the frame:
n = 10
colors = np.random.choice(['red', 'green'], size=n)
foods = np.random.choice(['eggs', 'ham'], size=n)
colors
foods
index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
df = pd.DataFrame(np.random.randn(n, 2), index=index)
df
df.query('color == "red"')
If the levels of the MultiIndex
are unnamed, you can refer to them using
special names:
df.index.names = [None, None]
df
df.query('ilevel_0 == "red"')
The convention is ilevel_0
, which means “index level 0” for the 0th level
of the index
.
query()
Use Cases#
A use case for query()
is when you have a collection of
DataFrame
objects that have a subset of column names (or index
levels/names) in common. You can pass the same query to both frames without
having to specify which frame you’re interested in querying
df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
df
df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
df2
expr = '0.0 <= a <= c <= 0.5'
map(lambda frame: frame.query(expr), [df, df2])
query()
Python versus pandas Syntax Comparison#
Full numpy-like syntax:
df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
df
df.query('(a < b) & (b < c)')
df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Slightly nicer by removing the parentheses (by binding making comparison
operators bind tighter than &
and |
).
df.query('a < b & b < c')
Use English instead of symbols:
df.query('a < b and b < c')
Pretty close to how you might write it on paper:
df.query('a < b < c')
The in
and not in
operators#
query()
also supports special use of Python’s in
and
not in
comparison operators, providing a succinct syntax for calling the
isin
method of a Series
or DataFrame
.
# get all rows where columns "a" and "b" have overlapping values
df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
'c': np.random.randint(5, size=12),
'd': np.random.randint(9, size=12)})
df
df.query('a in b')
# How you'd do it in pure Python
df[df['a'].isin(df['b'])]
df.query('a not in b')
# pure Python
df[~df['a'].isin(df['b'])]
You can combine this with other expressions for very succinct queries:
# rows where cols a and b have overlapping values
# and col c's values are less than col d's
df.query('a in b and c < d')
# pure Python
df[df['b'].isin(df['a']) & (df['c'] < df['d'])]
Note
Note that in
and not in
are evaluated in Python, since numexpr
has no equivalent of this operation. However, only the in
/not in
expression itself is evaluated in vanilla Python. For example, in the
expression
df.query('a in b + c + d')
(b + c + d)
is evaluated by numexpr
and then the in
operation is evaluated in plain Python. In general, any operations that can
be evaluated using numexpr
will be.
Special use of the ==
operator with list
objects#
Comparing a list
of values to a column using ==
/!=
works similarly
to in
/not in
.
df.query('b == ["a", "b", "c"]')
# pure Python
df[df['b'].isin(["a", "b", "c"])]
df.query('c == [1, 2]')
df.query('c != [1, 2]')
# using in/not in
df.query('[1, 2] in c')
df.query('[1, 2] not in c')
# pure Python
df[df['c'].isin([1, 2])]
Returning a view versus a copy#
When setting values in a pandas object, care must be taken to avoid what is called
chained indexing
. Here is an example.
dfmi = pd.DataFrame([list('abcd'),
list('efgh'),
list('ijkl'),
list('mnop')],
columns=pd.MultiIndex.from_product([['one', 'two'],
['first', 'second']]))
dfmi
Compare these two access methods:
dfmi['one']['second']
dfmi.loc[:, ('one', 'second')]
These both yield the same results, so which should you use? It is instructive to understand the order
of operations on these and why method 2 (.loc
) is much preferred over method 1 (chained []
).
dfmi['one']
selects the first level of the columns and returns a DataFrame that is singly-indexed.
Then another Python operation dfmi_with_one['second']
selects the series indexed by 'second'
.
This is indicated by the variable dfmi_with_one
because pandas sees these operations as separate events.
e.g. separate calls to __getitem__
, so it has to treat them as linear operations, they happen one after another.
Contrast this to df.loc[:,('one','second')]
which passes a nested tuple of (slice(None),('one','second'))
to a single call to
__getitem__
. This allows pandas to deal with this as a single entity. Furthermore this order of operations can be significantly
faster, and allows one to index both axes if so desired.
Why does assignment fail when using chained indexing?#
The problem in the previous section is just a performance issue. What’s up with
the SettingWithCopy
warning? We don’t usually throw warnings around when
you do something that might cost a few extra milliseconds!
But it turns out that assigning to the product of chained indexing has inherently unpredictable results. To see this, think about how the Python interpreter executes this code:
value = None
dfmi.loc[:, ('one', 'second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
But this code is handled differently:
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
See that __getitem__
in there? Outside of simple cases, it’s very hard to
predict whether it will return a view or a copy (it depends on the memory layout
of the array, about which pandas makes no guarantees), and therefore whether
the __setitem__
will modify dfmi
or a temporary object that gets thrown
out immediately afterward. That’s what SettingWithCopy
is warning you
about!
Note
You may be wondering whether we should be concerned about the loc
property in the first example. But dfmi.loc
is guaranteed to be dfmi
itself with modified indexing behavior, so dfmi.loc.__getitem__
/
dfmi.loc.__setitem__
operate on dfmi
directly. Of course,
dfmi.loc.__getitem__(idx)
may be a view or a copy of dfmi
.
Sometimes a SettingWithCopy
warning will arise at times when there’s no
obvious chained indexing going on. These are the bugs that
SettingWithCopy
is designed to catch! Pandas is probably trying to warn you
that you’ve done this:
def do_something(df):
foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows!
# ... many lines here ...
# We don't know whether this will modify df or not!
foo['quux'] = value
return foo
Yikes!
Evaluation order matters#
When you use chained indexing, the order and type of the indexing operation partially determine whether the result is a slice into the original object, or a copy of the slice.
Pandas has the SettingWithCopyWarning
because assigning to a copy of a
slice is frequently not intentional, but a mistake caused by chained indexing
returning a copy where a slice was expected.
If you would like pandas to be more or less trusting about assignment to a
chained indexing expression, you can set the option
mode.chained_assignment
to one of these values:
'warn'
, the default, means aSettingWithCopyWarning
is printed.'raise'
means pandas will raise aSettingWithCopyException
you have to deal with.None
will suppress the warnings entirely.
dfb = pd.DataFrame({'a': ['one', 'one', 'two',
'three', 'two', 'one', 'six'],
'c': np.arange(7)})
# This will show the SettingWithCopyWarning
# but the frame values will be set
dfb['c'][dfb['a'].str.startswith('o')] = 42
This however is operating on a copy and will not work.
>>> pd.set_option('mode.chained_assignment','warn')
>>> dfb[dfb['a'].str.startswith('o')]['c'] = 42
Traceback (most recent call last)
...
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
A chained assignment can also crop up in setting in a mixed dtype frame.
Note
These setting rules apply to all of .loc/.iloc
.
This is the correct access method:
dfc = pd.DataFrame({'A': ['aaa', 'bbb', 'ccc'], 'B': [1, 2, 3]})
dfc.loc[0, 'A'] = 11
dfc
This can work at times, but it is not guaranteed to, and therefore should be avoided:
dfc = dfc.copy()
dfc['A'][0] = 111
dfc
This will not work at all, and so should be avoided:
>>> pd.set_option('mode.chained_assignment','raise')
>>> dfc.loc[0]['A'] = 1111
Traceback (most recent call last)
...
SettingWithCopyException:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
Warning
The chained assignment warnings / exceptions are aiming to inform the user of a possibly invalid assignment. There may be false positives; situations where a chained assignment is inadvertently reported.
Kitchen Sink#
Note
The Kitchen Sink was generated from the Sphinx Themes website, a community-supported showcase of themes for Sphinx. Check it out to see other great themes.
This section exists as a dump of all the things that Sphinx has.
It has exactly one goal: to be a good checklist of things to stylise within Sphinx. This also means that it is a complete showcase of everything that vanilla Sphinx provides and includes all sorts of edge cases.
Admonitions#
Sphinx provides several different types of admonitions.
topic
#
This is a topic.
This is what admonitions are a special case of, according to the docutils documentation.
admonition
#
The one with the custom titles
It’s got a certain charm to it.
attention
#
Attention
Climate change is real.
caution
#
Caution
Cliff ahead: Don’t drive off it.
danger
#
Danger
Mad scientist at work!
error
#
Error
Does not compute.
hint
#
Hint
Insulators insulate, until they are subject to ______ voltage.
important
#
Important
Tech is not neutral, nor is it apolitical.
note
#
Note
This is a note.
seealso
#
See also
Other relevant information.
tip
#
Tip
25% if the service is good.
todo
#
Todo
This needs the sphinx.ext.todo
extension.
warning
#
Warning
Reader discretion is strongly advised.
versionadded
#
New in version v0.1.1: Here’s a version added message.
versionchanged
#
Changed in version v0.1.1: Here’s a version changed message.
deprecated
#
Deprecated since version v0.1.1: Here’s a deprecation message.
API documentation#
Using Sphinx’s sphinx.ext.autodoc
plugin, it is possible to auto-generate documentation of a Python module.
Tip
Avoid having in-function-signature type annotations with autodoc, by setting the following options:
# -- Options for autodoc ----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration
# Automatically extract typehints when specified and place them in
# descriptions of the relevant function/method.
autodoc_typehints = "description"
# Don't show class signature with the class' name.
autodoc_class_signature = "separated"
Parse (absolute and relative) URLs.
urlparse module is based upon the following RFC specifications.
RFC 3986 (STD66): “Uniform Resource Identifiers” by T. Berners-Lee, R. Fielding and L. Masinter, January 2005.
RFC 2732 : “Format for Literal IPv6 Addresses in URL’s by R.Hinden, B.Carpenter and L.Masinter, December 1999.
RFC 2396: “Uniform Resource Identifiers (URI)”: Generic Syntax by T. Berners-Lee, R. Fielding, and L. Masinter, August 1998.
RFC 2368: “The mailto URL scheme”, by P.Hoffman , L Masinter, J. Zawinski, July 1998.
RFC 1808: “Relative Uniform Resource Locators”, by R. Fielding, UC Irvine, June 1995.
RFC 1738: “Uniform Resource Locators (URL)” by T. Berners-Lee, L. Masinter, M. McCahill, December 1994
RFC 3986 is considered the current standard and any future changes to urlparse module should conform with it. The urlparse module is currently not entirely compliant with this RFC due to defacto scenarios for parsing, and for backward compatibility purposes, some parsing quirks from older RFCs are retained. The testcases in test_urlparse.py provides a good indicator of parsing behavior.
- class urllib.parse.DefragResult(url, fragment)[source]#
- Attributes:
- fragment
Fragment identifier separated from URL, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- url
The URL with no fragment identifier.
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
encode
geturl
- class urllib.parse.DefragResultBytes(url, fragment)[source]#
- Attributes:
- fragment
Fragment identifier separated from URL, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- url
The URL with no fragment identifier.
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
decode
geturl
- class urllib.parse.ParseResult(scheme, netloc, path, params, query, fragment)[source]#
- Attributes:
- fragment
Fragment identifier, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- hostname
- netloc
Network location where the request is made to.
- params
Parameters for last path element used to dereference the URI in order to provide access to perform some operation on the resource.
- password
- path
The hierarchical path, such as the path to a file to download.
- port
- query
The query component, that contains non-hierarchical data, that along with data in path component, identifies a resource in the scope of URI’s scheme and network location.
- scheme
Specifies URL scheme for the request.
- username
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
encode
geturl
- class urllib.parse.ParseResultBytes(scheme, netloc, path, params, query, fragment)[source]#
- Attributes:
- fragment
Fragment identifier, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- hostname
- netloc
Network location where the request is made to.
- params
Parameters for last path element used to dereference the URI in order to provide access to perform some operation on the resource.
- password
- path
The hierarchical path, such as the path to a file to download.
- port
- query
The query component, that contains non-hierarchical data, that along with data in path component, identifies a resource in the scope of URI’s scheme and network location.
- scheme
Specifies URL scheme for the request.
- username
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
decode
geturl
- class urllib.parse.SplitResult(scheme, netloc, path, query, fragment)[source]#
- Attributes:
- fragment
Fragment identifier, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- hostname
- netloc
Network location where the request is made to.
- password
- path
The hierarchical path, such as the path to a file to download.
- port
- query
The query component, that contains non-hierarchical data, that along with data in path component, identifies a resource in the scope of URI’s scheme and network location.
- scheme
Specifies URL scheme for the request.
- username
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
encode
geturl
- class urllib.parse.SplitResultBytes(scheme, netloc, path, query, fragment)[source]#
- Attributes:
- fragment
Fragment identifier, that allows indirect identification of a secondary resource by reference to a primary resource and additional identifying information.
- hostname
- netloc
Network location where the request is made to.
- password
- path
The hierarchical path, such as the path to a file to download.
- port
- query
The query component, that contains non-hierarchical data, that along with data in path component, identifies a resource in the scope of URI’s scheme and network location.
- scheme
Specifies URL scheme for the request.
- username
Methods
count
(value, /)Return number of occurrences of value.
index
(value[, start, stop])Return first index of value.
decode
geturl
- urllib.parse.parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&')[source]#
Parse a query given as a string argument.
Arguments:
qs: percent-encoded query string to be parsed
- keep_blank_values: flag indicating whether blank values in
percent-encoded queries should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included.
- strict_parsing: flag indicating what to do with parsing errors.
If false (the default), errors are silently ignored. If true, errors raise a ValueError exception.
- encoding and errors: specify how to decode percent-encoded sequences
into Unicode characters, as accepted by the bytes.decode() method.
- max_num_fields: int. If set, then throws a ValueError if there
are more than n fields read by parse_qsl().
- separator: str. The symbol to use for separating the query arguments.
Defaults to &.
Returns a dictionary.
- urllib.parse.parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&')[source]#
Parse a query given as a string argument.
Arguments:
qs: percent-encoded query string to be parsed
- keep_blank_values: flag indicating whether blank values in
percent-encoded queries should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included.
- strict_parsing: flag indicating what to do with parsing errors. If
false (the default), errors are silently ignored. If true, errors raise a ValueError exception.
- encoding and errors: specify how to decode percent-encoded sequences
into Unicode characters, as accepted by the bytes.decode() method.
- max_num_fields: int. If set, then throws a ValueError
if there are more than n fields read by parse_qsl().
- separator: str. The symbol to use for separating the query arguments.
Defaults to &.
Returns a list, as G-d intended.
- urllib.parse.quote('abc def') 'abc%20def' [source]#
Each part of a URL, e.g. the path info, the query, etc., has a different set of reserved characters that must be quoted. The quote function offers a cautious (not minimal) way to quote a string for most of these parts.
RFC 3986 Uniform Resource Identifier (URI): Generic Syntax lists the following (un)reserved characters.
unreserved = ALPHA / DIGIT / “-” / “.” / “_” / “~” reserved = gen-delims / sub-delims gen-delims = “:” / “/” / “?” / “#” / “[” / “]” / “@” sub-delims = “!” / “$” / “&” / “’” / “(” / “)”
/ “*” / “+” / “,” / “;” / “=”
Each of the reserved characters is reserved in some component of a URL, but not necessarily in all of them.
The quote function %-escapes all characters that are neither in the unreserved chars (“always safe”) nor the additional chars set via the safe arg.
The default for the safe arg is ‘/’. The character is reserved, but in typical usage the quote function is being called on a path where the existing slash characters are to be preserved.
Python 3.7 updates from using RFC 2396 to RFC 3986 to quote URL strings. Now, “~” is included in the set of unreserved characters.
string and safe may be either str or bytes objects. encoding and errors must not be specified if string is a bytes object.
The optional encoding and errors parameters specify how to deal with non-ASCII characters, as accepted by the str.encode method. By default, encoding=’utf-8’ (characters are encoded with UTF-8), and errors=’strict’ (unsupported characters raise a UnicodeEncodeError).
- urllib.parse.quote_from_bytes(bs, safe='/')[source]#
Like quote(), but accepts a bytes object rather than a str, and does not perform string-to-bytes encoding. It always returns an ASCII string. quote_from_bytes(b’abc def?’) -> ‘abc%20def%3f’
- urllib.parse.quote_plus(string, safe='', encoding=None, errors=None)[source]#
Like quote(), but also replace ‘ ‘ with ‘+’, as required for quoting HTML form values. Plus signs in the original string are escaped unless they are included in safe. It also does not have safe default to ‘/’.
- urllib.parse.unquote(string, encoding='utf-8', errors='replace')[source]#
Replace %xx escapes by their single-character equivalent. The optional encoding and errors parameters specify how to decode percent-encoded sequences into Unicode characters, as accepted by the bytes.decode() method. By default, percent-encoded sequences are decoded with UTF-8, and invalid sequences are replaced by a placeholder character.
unquote(‘abc%20def’) -> ‘abc def’.
- urllib.parse.unquote_plus(string, encoding='utf-8', errors='replace')[source]#
Like unquote(), but also replace plus signs by spaces, as required for unquoting HTML form values.
unquote_plus(‘%7e/abc+def’) -> ‘~/abc def’
- urllib.parse.urldefrag(url)[source]#
Removes any existing fragment from URL.
Returns a tuple of the defragmented URL and the fragment. If the URL contained no fragments, the second element is the empty string.
- urllib.parse.urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=<function quote_plus>)[source]#
Encode a dict or sequence of two-element tuples into a URL query string.
If any values in the query arg are sequences and doseq is true, each sequence element is converted to a separate parameter.
If the query arg is a sequence of two-element tuples, the order of the parameters in the output will match the order of parameters in the input.
The components of a query arg may each be either a string or a bytes type.
The safe, encoding, and errors parameters are passed down to the function specified by quote_via (encoding and errors only if a component is a str).
- urllib.parse.urljoin(base, url, allow_fragments=True)[source]#
Join a base URL and a possibly relative URL to form an absolute interpretation of the latter.
- urllib.parse.urlparse(url, scheme='', allow_fragments=True)[source]#
Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<fragment>
The result is a named 6-tuple with fields corresponding to the above. It is either a ParseResult or ParseResultBytes object, depending on the type of the url parameter.
The username, password, hostname, and port sub-components of netloc can also be accessed as attributes of the returned object.
The scheme argument provides the default value of the scheme component when no scheme is found in url.
If allow_fragments is False, no attempt is made to separate the fragment component from the previous component, which can be either path or query.
Note that % escapes are not expanded.
- urllib.parse.urlsplit(url, scheme='', allow_fragments=True)[source]#
Parse a URL into 5 components: <scheme>://<netloc>/<path>?<query>#<fragment>
The result is a named 5-tuple with fields corresponding to the above. It is either a SplitResult or SplitResultBytes object, depending on the type of the url parameter.
The username, password, hostname, and port sub-components of netloc can also be accessed as attributes of the returned object.
The scheme argument provides the default value of the scheme component when no scheme is found in url.
If allow_fragments is False, no attempt is made to separate the fragment component from the previous component, which can be either path or query.
Note that % escapes are not expanded.
- urllib.parse.urlunparse(components)[source]#
Put a parsed URL back together again. This may result in a slightly different, but equivalent URL, if the URL that was parsed originally had redundant delimiters, e.g. a ? with an empty query (the draft states that these are equivalent).
- urllib.parse.urlunsplit(components)[source]#
Combine the elements of a tuple as returned by urlsplit() into a complete URL as a string. The data argument can be any five-item iterable. This may result in a slightly different, but equivalent URL, if the URL that was parsed originally had unnecessary delimiters (for example, a ? with an empty query; the RFC states that these are equivalent).
Blocks#
Block Quotes#
Block quotes consist of indented body elements:
My theory by A. Elk. Brackets Miss, brackets. This theory goes as follows and begins now. All brontosauruses are thin at one end, much much thicker in the middle and then thin again at the far end. That is my theory, it is mine, and belongs to me and I own it, and what it is too.
—Anne Elk (Miss)
Epigraph#
https://docutils.sourceforge.io/docs/ref/rst/directives.html#epigraph
My theory by A. Elk. Brackets Miss, brackets. This theory goes as follows and begins now. All brontosauruses are thin at one end, much much thicker in the middle and then thin again at the far end. That is my theory, it is mine, and belongs to me and I own it, and what it is too.
—Anne Elk (Miss)
Pull quotes#
https://docutils.sourceforge.io/docs/ref/rst/directives.html#pull-quote
My theory by A. Elk. Brackets Miss, brackets. This theory goes as follows and begins now. All brontosauruses are thin at one end, much much thicker in the middle and then thin again at the far end. That is my theory, it is mine, and belongs to me and I own it, and what it is too.
—Anne Elk (Miss)
Highlights#
https://docutils.sourceforge.io/docs/ref/rst/directives.html#highlights
My theory by A. Elk. Brackets Miss, brackets. This theory goes as follows and begins now. All brontosauruses are thin at one end, much much thicker in the middle and then thin again at the far end. That is my theory, it is mine, and belongs to me and I own it, and what it is too.
—Anne Elk (Miss)
Line Blocks#
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#line-blocks
This is a normal text paragraph.
This is a normal text paragraph again.
Monospace Blocks#
Sphinx supports many kinds of monospace blocks. This section is meant to showcase all of them that as known to the author of this page, at the time of writing.
Production List#
https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-productionlist
This directive is used to enclose a group of productions. Each production is given on a single line and consists of a name, separated by a colon from the following definition.
This just shows up as a vanilla <pre>
, which is… both nice and a bit
annoying.
try_stmt ::= try1_stmt | try2_stmt try1_stmt ::= "try" ":"suite
("except" [expression
[","target
]] ":"suite
)+ ["else" ":"suite
] ["finally" ":"suite
] try2_stmt ::= "try" ":"suite
"finally" ":"suite
"this-is-intentionally-very-stupidly-long-to-make-sure-that-this-has-a-proper-scrollbar"
Literal Blocks#
https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#literal-blocks
contains a block of text where line breaks and whitespace are significant and must be preserved
This is a normal text paragraph. The next paragraph is a code sample:
It is not processed in any way, except
that the indentation is removed.
It can span multiple lines.
This is a normal text paragraph again.
They can be quoted without indentation:
>> Great idea!
>
> Why didn't I think of that?
1
2import jinja2
3from bs4 import BeautifulSoup as bs
4from docutils import nodes
5from sphinx import addnodes
6from sphinx.application import Sphinx
7from sphinx.environment.adapters.toctree import TocTree
8from sphinx.addnodes import toctree as toctree_node
9from sphinx.transforms.post_transforms import SphinxPostTransform
10from sphinx.util.nodes import NodeMatcher
11from sphinx.errors import ExtensionError
Doctest Blocks#
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#doctest-blocks
Doctest blocks are interactive Python sessions cut-and-pasted into docstrings. They are meant to illustrate usage by example, and provide an elegant and powerful testing environment via the doctest module in the Python standard library.
Note
This is fine.
>>> print('Python-specific usage examples; begun with ">>>"')
Python-specific usage examples; begun with ">>>"
>>> print("(cut and pasted from interactive Python sessions)")
(cut and pasted from interactive Python sessions)
>>> print("This is an intentionally very long line because I want to make sure that we are handling scrollable code blocks correctly.")
This is an intentionally very long line because I want to make sure that we are handling scrollable code blocks correctly.
Parsed Literals#
https://docutils.sourceforge.io/docs/ref/rst/directives.html#parsed-literal-block
It is equivalent to a line block with different rendering: typically in a typewriter/monospaced typeface, like an ordinary literal block. Parsed literal blocks are useful for adding hyperlinks to code examples.
# parsed-literal test
curl -O http://someurl/release-0.1.0.tar-gz
echo "This is an intentionally very long line because I want to make sure that we are handling scrollable code blocks correctly."
Code Block#
https://docutils.sourceforge.io/docs/ref/rst/directives.html#code
The “code” directive constructs a literal block [containing code].
This has an alias of code-block
.
1from typing import Iterator
2
3# This is an example
4class Math:
5 @staticmethod
6 def fib(n: int) -> Iterator[int]:
7 """Fibonacci series up to n"""
8 a, b = 0, 1
9 while a < n:
10 yield a
11 a, b = b, a + b
12
13
14result = sum(Math.fib(42))
15print("The answer is {}".format(result))
With caption#
{
"session_name": "shorthands",
"windows": [
{
"panes": [
{
"shell_command": "echo 'This is an intentionally very long line because I want to make sure that we are handling scrollable code blocks correctly.'"
}
],
"window_name": "long form"
}
]
}
With line numbers#
1def some_function():
2 interesting = False
3 print("This line is highlighted.")
4 print("This one is not...")
5 print("...but this one is.")
6 print(
7 "This is an intentionally very long line because I want to make sure that we are handling scrollable code blocks correctly."
8 )
Without highlighting#
# Taken from https://en.wikipedia.org/wiki/Pseudocode#Example
algorithm ford-fulkerson is
input: Graph G with flow capacity c,
source node s,
sink node t
output: Flow f such that f is maximal from s to t
(Note that f(u,v) is the flow from node u to node v, and c(u,v) is the flow capacity from node u to node v)
for each edge (u, v) in GE do
f(u, v) ← 0
f(v, u) ← 0
while there exists a path p from s to t in the residual network Gf do
let cf be the flow capacity of the residual network Gf
cf(p) ← min{cf(u, v) | (u, v) in p}
for each edge (u, v) in p do
f(u, v) ← f(u, v) + cf(p)
f(v, u) ← −f(u, v)
return f
Generic items#
These are the things that don’t quite fit into other groupings.
Inline Markup#
One of the nice things about markup languages is the ability to have inline
markup. This makes the presentation much nicer. The bold text shouldn’t
be too overbearing. It is very common to have inline code
as well. It is
important to ensure that the inline code doesn’t have a line height that is
greater than the regular prose; otherwise the spacing looks weird.
It is also possible to use explicit roles to do things like a subscript,
a superscript, emphasis, strong, and
literal
.
Hyperlinks#
It is a website, so it’ll have hyperlinks like http://www.python.org (inline),
or Python [4] (external reference), example (internal reference),
Python web site (external hyperlinks with embedded
URI), footnote references (manually numbered [1], anonymous auto-numbered [3],
labeled auto-numbered [2], or symbolic [*]), citation references ([12]),
substitution references (), and inline hyperlink targets
(see Targets below for a reference back to here).
reStructuredText has character-level inline markup too. It’s ugly to write, but
someone might be using it, so here’s an example: reStructured
Text.
It is also possible to link to documented items, such as
api_sample.RandomNumberGenerator
.
Interpreted text#
The default role for “interpreted text” (AKA single backticks) is Title Reference. There are other reference syntaxes as well: PEP 287 and RFC 2822.
If the --pep-references
option was supplied, there should be a live link to
PEP 258 here.
GUI labels#
According to the RST demo, GUI labels (like this label) are a way to indicate that some action is to be taken by the user. Like inline code literals, GUI labels should not run over line height.
Long inline code wrapping#
Let’s test wrapping and whitespace significance in inline literals:
This is an example of --inline-literal --text, --including some--
strangely--hyphenated-words. Adjust-the-width-of-your-browser-window
to see how the text is wrapped. -- ---- -------- Now note the
spacing between the words of this sentence (words
should be grouped in pairs).
Math#
This is a test. Here is an equation: \(X_{0:5} = (X_0, X_1, X_2, X_3, X_4)\). Here is another:
You can add a link to equations like the one above (1) by using
:eq:
.
References#
Footnotes#
A footnote contains body elements, consistently indented by at least 3 spaces.
This is the footnote’s second paragraph.
Footnotes may be numbered, either manually (as in [1]) or automatically using a “#”-prefixed label. This footnote has a label so it can be referred to from multiple places, both as a footnote reference ([2]) and as a hyperlink reference (label).
This footnote is numbered automatically and anonymously using a label of “#” only.
Footnotes may also use symbols, specified with a “*” label. Here’s a reference to the next footnote: [†].
This footnote shows the next symbol in the sequence.
Citations#
This citation has some code blocks
in it, maybe some bold and
italics too. Heck, lets put a link to a meta citation [13] too.
This citation will have one backlink.
Here’s a reference to the above, [12] citation.
Here is another type of citation: citation
Targets#
This paragraph is pointed to by the explicit “example” target. A reference can be found under Inline Markup, above. Inline hyperlink targets are also possible.
Section headers are implicit targets, referred to by name. See Targets.
Explicit external targets are interpolated into references such as “Python [4]”.
Targets may be indirect and anonymous. Thus this phrase may also refer to the Targets section.
Target Footnotes#
Centered text#
You can create a statement with centered text with .. centered::
This is centered text!
Rubric#
A rubric is like an informal heading that doesn’t correspond to the document’s structure.
—https://docutils.sourceforge.io/docs/ref/rst/directives.html#rubric
Wikipedia says it is something different:
A rubric is a word or section of text that is traditionally written or printed in red ink for emphasis.
This is stylized as docutils tells us to stylize it, since it is used for footnote headers (see end of https://docs.python.org/3/reference/lexical_analysis.html)
This is a rubric
Download Links#
Images & Figures#
Images#
An image:
A clickable image:
This is a lot of text to go along with a right-aligned image, that is helping make this content feel less linear. It is important to have such a body of text, since the image is meant to be “floated” to the right, which would interfere with the rest of the document otherwise.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis sapiente veritatis doloribus accusantium molestiae modi recusandae excepturi facere, corrupti expedita sit nihil temporibus eius sequi animi, illo libero labore fuga.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis sapiente veritatis doloribus accusantium molestiae modi recusandae excepturi facere, corrupti expedita sit nihil temporibus eius sequi animi, illo libero labore fuga.
This is a lot of text to go along with a left-aligned image, that is helping make this content feel less linear. It is important to have such a body of text, since the image is meant to be “floated” to the right, which would interfere with the rest of the document otherwise.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis sapiente veritatis doloribus accusantium molestiae modi recusandae excepturi facere, corrupti expedita sit nihil temporibus eius sequi animi, illo libero labore fuga.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis sapiente veritatis doloribus accusantium molestiae modi recusandae excepturi facere, corrupti expedita sit nihil temporibus eius sequi animi, illo libero labore fuga.
Figures#
A figure is an image with a caption and/or a legend:#
re |
Revised, revisited, based on ‘re’ module. |
Structured |
Structure-enhanced text, structuredtext. |
Text |
Well it is, isn’t it? |
This paragraph is also part of the legend.
A figure directive with center alignment
This caption should be centered.#
Lists#
Enumerated Lists#
Arabic numerals.
lower alpha)
(lower roman)
upper alpha.
upper roman)
Lists that don’t start at 1:
Three
Four
C
D
iii
iv
List items may also be auto-enumerated.
Definition Lists#
- Term
Definition
- Termclassifier
Definition paragraph 1.
Definition paragraph 2.
- Term
Definition
I have no clue why the definition list below is classified as a different style of definition list than the one above.
- Is it the spaces in the term?
Maybe it was the multiple line paragraph in the line below that caused this?
- Is it the paragraph above the list maybe?
I guess a lot of these lists don’t have leading paragraphs?
- Is it everything all at once?
Who knows?!
Option Lists#
For listing command-line options:
- -a
command-line option “a”
- -b file
options can have arguments and long descriptions
- --long
options can be long also
- --input=file
long options can also have arguments
- --very-long-option
The description can also start on the next line.
The description may contain multiple body elements, regardless of where it starts.
- -x, -y, -z
Multiple options are an “option group”.
- -v, --verbose
Commonly-seen: short & long options.
- -1 file, --one=file, --two file
Multiple options with arguments.
- /V
DOS/VMS-style options too
There must be at least two spaces between the option and the description.
Field list#
- Address:
123 Example Street Example, EX Canada A1B 2C3
- Organization:
humankind
- Status:
This is a “work in progress”
- Version:
1
- Field name:
This is a generic bibliographic field.
- Field name 2:
Generic bibliographic fields may contain multiple body elements.
Like this.
Glossary#
This is a glossary with definition terms for thing like Writing:
- Documentation#
Provides users with the knowledge they need to use something.
- Reading#
The process of taking information into ones mind through the use of eyes.
- Writing#
The process of putting thoughts into a medium for other people to read.
Here’s another glossary, with more detail. The important bit here is that the contents of the definition are multi-paragraph.
- Import Package#
A Python module which can contain other modules or recursively, other packages.
An import package is more commonly referred to with the single word “package”, but this guide will use the expanded term when more clarity is needed to prevent confusion with a Distribution Package which is also commonly called a “package”.
- Package Index#
A repository of distributions with a web interface to automate package discovery and consumption.
Bullet Lists#
Simple#
A simple list.
There are no margins between list items.
Simple lists do not contain multiple paragraphs. That’s a complex list.
In the case of a nested list
There are no margins between elements
Still no margins
Still no margins
Complex#
A bullet list
Nested bullet list.
Nested item 2.
Item 2.
Paragraph 2 of item 2.
Nested bullet list.
Nested item 2.
Third level.
Item 2.
Nested item 3.
inline literall
inline literall
inline literall
This item has multiple paragraphs.
This item has multiple paragraphs.
This item has multiple paragraphs.
This item has multiple paragraphs.
Second list level#
here is a list in a second-level section.
-
here is an inner bullet
oh
one more
with an inline literally
. yahooheh heh. child. try to beat this embed:
1 2import jinja2 3from bs4 import BeautifulSoup as bs 4from docutils import nodes 5from sphinx import addnodes 6from sphinx.application import Sphinx 7from sphinx.environment.adapters.toctree import TocTree 8from sphinx.addnodes import toctree as toctree_node 9from sphinx.transforms.post_transforms import SphinxPostTransform 10from sphinx.util.nodes import NodeMatcher 11from sphinx.errors import ExtensionError
and another. yahoo
hi
how about an admonition?
Note
This is a note nested in a list.
and hehe
But deeper down the rabbit hole#
Hlists#
|
|
Hlist with images
|
|
Numbered List#
One,
Two.
Three with long text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed feugiat sagittis neque quis eleifend. Duis rutrum lectus sit amet mattis suscipit.
Using bullets and letters. (A)
Using bullets and letters. (B)
Using bullets and letters. (C)
Mimicking ReadTheDocs’ PR previews
This is simulating how ReadTheDocs puts admonitions on top of previews.
Structural Elements#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Etiam turpis ante, luctus sed velit tristique, finibus volutpat dui. Nam sagittis vel ante nec malesuada. Praesent dignissim mi nec ornare elementum. Nunc eu augue vel sem dignissim cursus sed et nulla. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque dictum dui sem, non placerat tortor rhoncus in. Sed placerat nulla at rhoncus iaculis.
Document Section#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed condimentum nulla vel neque venenatis, nec placerat lorem placerat. Cras purus eros, gravida vitae tincidunt id, vehicula nec nulla. Fusce aliquet auctor cursus. Phasellus ex neque, vestibulum non est vitae, viverra fringilla tortor. Donec vestibulum convallis justo, a faucibus lorem vulputate vel. Aliquam cursus odio eu felis sodales aliquet. Aliquam erat volutpat. Maecenas eget dictum mauris. Suspendisse arcu eros, condimentum eget risus sed, luctus efficitur arcu. Cras ut dictum mi. Nulla congue interdum lorem, semper semper enim commodo nec.
Document Subsection#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam efficitur in eros et blandit. Nunc maximus, nisl at auctor vestibulum, justo ex sollicitudin ligula, id faucibus urna orci tristique nisl. Duis auctor rutrum orci, in ornare lacus condimentum quis. Quisque arcu velit, facilisis quis interdum ac, hendrerit auctor mauris. Curabitur urna nibh, porttitor at ante sit amet, vestibulum interdum dolor. Duis dictum elit orci, tincidunt imperdiet sem pellentesque et. In vehicula pellentesque varius. Phasellus a turpis sollicitudin, bibendum massa et, imperdiet neque. Integer quis sapien in magna rutrum bibendum. Integer cursus ex sed magna vehicula finibus. Proin tempus orci quis dolor tempus, nec condimentum odio vestibulum. Etiam efficitur sollicitudin libero, tincidunt volutpat ligula interdum sed.
Document Subsubsection#
Donec non rutrum lorem. Aenean sagittis metus at pharetra fringilla. Nunc sapien dolor, cursus sed nisi at, pretium tristique lectus. Sed pellentesque leo lectus, et convallis ipsum euismod a. Integer at leo vitae felis pretium aliquam fringilla quis odio. Sed pharetra enim accumsan feugiat pretium. Maecenas at pharetra tortor. Morbi semper eget mi vel finibus. Cras rutrum nulla eros, id feugiat arcu pellentesque ut. Sed finibus tortor ac nisi ultrices viverra. Duis feugiat malesuada sapien, at commodo ante porttitor ac. Curabitur posuere mauris mi, vel ornare orci scelerisque sit amet. Suspendisse nec fringilla dui.
Document Paragraph#
Pellentesque nec est in odio ultrices elementum. Vestibulum et hendrerit sapien, quis vulputate turpis. Suspendisse potenti. Curabitur tristique sit amet lectus non viverra. Phasellus rutrum dapibus turpis sed imperdiet. Mauris maximus viverra ante. Donec eu egestas mauris. Morbi vulputate tincidunt euismod. Integer vel porttitor neque. Donec at lacus suscipit, lacinia lectus vel, sagittis lectus.
Structural Elements 2#
Etiam turpis ante, luctus sed velit tristique, finibus volutpat dui. Nam sagittis vel ante nec malesuada. Praesent dignissim mi nec ornare elementum. Nunc eu augue vel sem dignissim cursus sed et nulla. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque dictum dui sem, non placerat tortor rhoncus in. Sed placerat nulla at rhoncus iaculis.
Document Section#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed condimentum nulla vel neque venenatis, nec placerat lorem placerat. Cras purus eros, gravida vitae tincidunt id, vehicula nec nulla. Fusce aliquet auctor cursus. Phasellus ex neque, vestibulum non est vitae, viverra fringilla tortor. Donec vestibulum convallis justo, a faucibus lorem vulputate vel. Aliquam cursus odio eu felis sodales aliquet. Aliquam erat volutpat. Maecenas eget dictum mauris. Suspendisse arcu eros, condimentum eget risus sed, luctus efficitur arcu. Cras ut dictum mi. Nulla congue interdum lorem, semper semper enim commodo nec.
Document Subsection#
This is a caption for a figure. Text should wrap around the caption.#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam efficitur in eros et blandit. Nunc maximus, nisl at auctor vestibulum, justo ex sollicitudin ligula, id faucibus urna orci tristique nisl. Duis auctor rutrum orci, in ornare lacus condimentum quis. Quisque arcu velit, facilisis quis interdum ac, hendrerit auctor mauris. Curabitur urna nibh, porttitor at ante sit amet, vestibulum interdum dolor. Duis dictum elit orci, tincidunt imperdiet sem pellentesque et. In vehicula pellentesque varius. Phasellus a turpis sollicitudin, bibendum massa et, imperdiet neque. Integer quis sapien in magna rutrum bibendum. Integer cursus ex sed magna vehicula finibus. Proin tempus orci quis dolor tempus, nec condimentum odio vestibulum. Etiam efficitur sollicitudin libero, tincidunt volutpat ligula interdum sed. Praesent congue sagittis nisl et suscipit. Vivamus sagittis risus et egestas commodo.Cras venenatis arcu in pharetra interdum. Donec quis metus porttitor tellus cursus lobortis. Quisque et orci magna. Fusce rhoncus mi mi, at vehicula massa rhoncus quis. Mauris augue leo, pretium eget molestie vitae, efficitur nec nulla. In hac habitasse platea dictumst. Sed sit amet imperdiet purus.
Tables#
Grid Tables#
Here’s a grid table followed by a simple table:
Header row, column 1 (header rows optional) |
Header 2 |
Header 3 |
Header 4 |
---|---|---|---|
body row 1, column 1 |
column 2 |
column 3 |
column 4 |
body row 2 |
Cells may span columns. |
||
body row 3 |
Cells may span rows. |
|
|
body row 4 |
|||
body row 5 |
Cells may also be
empty: |
Inputs |
Output |
|
---|---|---|
A |
B |
A or B |
False |
False |
False |
True |
False |
True |
False |
True |
True |
True |
True |
True |
Giant Tables#
Header 1 |
Header 2 |
Header 3 |
Header 1 |
Header 2 |
Header 3 |
Header 1 |
Header 2 |
Header 3 |
Header 1 |
Header 2 |
Header 3 |
---|---|---|---|---|---|---|---|---|---|---|---|
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
body row 1 |
column 2 |
column 3 |
Table containing code#
Version |
Installing |
---|---|
Pradyun’s pip fork and installer |
pip install "pip @ git+https://github.com/pradyunsg/pip#20.3.3" "installer @ git+https://github.com/pradyunsg/installer"
|
PyPI |
pip install pip installer
|
List Tables#
List table |
Header 1 |
Header 2 |
Header 3 long. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet mauris arcu. |
---|---|---|---|
Stub Row 1 |
Row 1 |
Column 2 |
Column 3 long. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet mauris arcu. |
Stub Row 2 |
Row 2 |
Column 2 |
Column 3 long. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet mauris arcu. |
Stub Row 3 |
Row 3 |
Column 2 |
Column 3 long. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet mauris arcu. |
This is a short caption for a figure.# |
This is a long caption for a figure. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec porttitor dolor in odio posuere, vitae ornare libero mattis. In lobortis justo vestibulum nibh aliquet, non.# |
Typography#
This is quite important, for a website where the majority of the content is going to be prose.
Notice the font family being used for the prose, as well as the font family being used for the heading. Think about the spacing between the lines, as well as the spacing between various paragraphs. Also keep the font weight in mind, and consider if/how you want antialiasing and font-smoothing to work.
Multiple paragraphs are a common occurance, because you often need more than a single paragraph to describe a thing. The rest of this paragraph is gonna be the famous lorem-ipsum: Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
Headings#
This next bit will explore how the various headings look. Think about how the content separation should work, and how the various headings should interact with the main content.
Heading 3#
Heading 4#
Heading 5#
Heading 6#
Heading 2 with content#
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
Heading 3 with content#
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
Heading 4 with content#
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
Heading 5 with content#
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
Heading 6 with content#
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Accusamus, sunt voluptatum tenetur libero nulla esse veritatis accusantium earum commodi hic voluptatem officia culpa optio atque. Quaerat sed quibusdam ratione nam.
PyData Library Styles#
This theme has built-in support and special styling for several major visualization libraries in the PyData ecosystem. This ensures that the images and output generated by these libraries looks good for both light and dark modes. Below are examples of each that we use as a benchmark for reference.
Pandas#
import string
import numpy as np
import pandas as pd
rng = np.random.default_rng()
data = rng.standard_normal((100, 26))
df = pd.DataFrame(data, columns=list(string.ascii_lowercase))
df
a | b | c | d | e | f | g | h | i | j | ... | q | r | s | t | u | v | w | x | y | z | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | -1.012324 | 0.478654 | 0.504386 | 1.502022 | 0.363253 | -1.914303 | 0.510831 | 0.459759 | -0.988264 | -0.446091 | ... | -0.281426 | -0.896563 | -1.602812 | -1.008632 | -1.468771 | 1.744396 | -1.166082 | -1.293475 | -0.043016 | -1.015513 |
1 | -0.522561 | -0.433435 | 0.982725 | 0.687714 | 0.871144 | 0.532871 | 0.835102 | -0.423085 | 0.537544 | -0.661163 | ... | 0.040937 | -1.527319 | -0.519145 | 0.035562 | 0.029804 | 0.839497 | -1.793734 | -0.029748 | 2.064892 | -1.264519 |
2 | 0.745534 | -2.267913 | -0.629330 | -0.675804 | 0.503004 | -1.048645 | -1.245557 | 0.729645 | -1.754690 | 0.715262 | ... | 1.179722 | -0.653818 | -1.333219 | -1.299535 | 0.368167 | 1.637710 | 0.622285 | 0.282278 | 0.427014 | 1.227416 |
3 | -0.551975 | -1.321145 | 0.402331 | 1.102444 | 1.329986 | -0.621191 | 1.302620 | -1.129645 | 0.152683 | -0.119374 | ... | 1.329716 | 0.093990 | -0.936060 | 0.968413 | -0.292063 | 0.527280 | -0.170660 | -0.270475 | -2.267983 | -1.341164 |
4 | -1.774458 | -2.114471 | -1.260948 | -0.653709 | -2.198096 | -0.073967 | -2.248274 | 1.198138 | -1.557866 | 0.654930 | ... | 0.151051 | 0.784811 | -0.495279 | 1.310365 | 0.254892 | 0.277478 | 1.165335 | 1.891679 | -0.325870 | -0.319133 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
95 | -0.080598 | -0.199094 | 1.217275 | 0.593356 | -1.101757 | 0.599001 | 0.475757 | 1.068236 | 0.667263 | 1.845290 | ... | 0.082082 | -0.482049 | 0.454580 | -0.385627 | 1.480666 | -0.018229 | -0.569673 | 2.713353 | 0.332052 | 0.264942 |
96 | 0.594208 | -1.884403 | -0.796796 | 1.181394 | 0.467123 | -0.206016 | 0.247404 | 1.206178 | 0.407940 | 0.259583 | ... | 1.419259 | -0.771202 | -1.144857 | -0.426638 | -0.576821 | 1.386845 | -0.531451 | 0.411439 | -0.445206 | 0.434931 |
97 | -1.052279 | -0.434028 | 0.471627 | 0.873932 | -0.095732 | -0.790471 | 0.986049 | -1.836783 | -1.393275 | -1.994928 | ... | -0.573361 | -0.369830 | 1.658681 | -0.861737 | -0.808341 | 1.280567 | -0.152595 | 0.353611 | 0.949825 | 0.998634 |
98 | -1.612467 | 0.485550 | 0.916085 | -0.417651 | 0.554868 | -1.059210 | -0.574241 | -0.573145 | 0.172974 | 0.302878 | ... | 1.196182 | -1.909894 | 0.021073 | 0.500691 | 0.285681 | 1.673079 | -0.886268 | -0.784215 | 1.394438 | 0.073945 |
99 | 0.863355 | 1.157259 | 1.452463 | -1.045960 | 0.455901 | 0.081169 | -0.578732 | -0.167773 | -1.072090 | 1.298488 | ... | -0.265689 | 0.332365 | -0.121449 | -0.560227 | 0.968394 | -0.487514 | 1.434941 | -0.084556 | 1.186354 | 1.078795 |
100 rows × 26 columns
Matplotlib#
Here’s a sidebar to test that the code cells behave as we’d expect when there is content to the right. The code cell should be displayed to the left and with no overlap.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(df["a"], df["b"], c=df["b"], s=3)
<matplotlib.collections.PathCollection at 0x7fb9f155e970>

and with the Matplotlib plot
directive:
(Source code
, png
, hires.png
, pdf
)

Plotly#
The HTML below shouldn’t display, but it uses RequireJS to make sure that all works as expected. If the widgets don’t show up, RequireJS may be broken.
import plotly.io as pio
import plotly.express as px
import plotly.offline as py
pio.renderers.default = "notebook"
df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species", size="sepal_length")
fig
Xarray#
Here we demonstrate xarray
to ensure that it shows up properly.
import xarray as xr
data = xr.DataArray(
np.random.randn(2, 3),
dims=("x", "y"),
coords={"x": [10, 20]}, attrs={"foo": "bar"}
)
data
<xarray.DataArray (x: 2, y: 3)> array([[ 1.11491166, 1.52585013, 1.11087228], [-0.09727049, 0.23974278, 0.38291869]]) Coordinates: * x (x) int64 10 20 Dimensions without coordinates: y Attributes: foo: bar
ipyleaflet#
ipyleaflet
is a Jupyter/Leaflet bridge enabling interactive maps in the Jupyter notebook environment. this demonstrate how you can integrate maps in your documentation.
from ipyleaflet import Map, basemaps
from IPython.display import display
# display a map centered on France
m = Map(basemap=basemaps.Esri.WorldImagery, zoom=5, center=[46.21, 2.21])
display(m)
jupyter-sphinx#
This theme has styling for jupyter-sphinx
, which is often used for executing and displaying widgets with a Jupyter kernel.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng()
data = rng.standard_normal((3, 100))
fig, ax = plt.subplots()
ax.scatter(data[0], data[1], c=data[2], s=3)
<matplotlib.collections.PathCollection at 0x7f0b3926ed90>

Advanced API documentation and generated content#
This page contains general code elements that are common for package documentation.
Autosummary table and API stub pages#
Drop specified labels from rows or columns. |
|
|
Group DataFrame using a mapper or by a Series of columns. |
The ExtensionArray of the data backing this Series or Index. |
Inline module documentation#
numpy.linalg
#
numpy.linalg
#
The NumPy linear algebra functions rely on BLAS and LAPACK to provide efficient low level implementations of standard linear algebra algorithms. Those libraries may be provided by NumPy itself using C versions of a subset of their reference implementations but, when possible, highly optimized libraries that take advantage of specialized processor functionality are preferred. Examples of such libraries are OpenBLAS, MKL (TM), and ATLAS. Because those libraries are multithreaded and processor dependent, environmental variables and external packages such as threadpoolctl may be needed to control the number of threads or specify the processor architecture.
OpenBLAS: https://www.openblas.net/
threadpoolctl: joblib/threadpoolctl
Please note that the most-used linear algebra functions in NumPy are present in
the main numpy
namespace rather than in numpy.linalg
. There are:
dot
, vdot
, inner
, outer
, matmul
, tensordot
, einsum
,
einsum_path
and kron
.
Functions present in numpy.linalg are listed below.
Matrix and vector products#
multi_dot matrix_power
Decompositions#
cholesky qr svd
Matrix eigenvalues#
eig eigh eigvals eigvalsh
Norms and other numbers#
norm cond det matrix_rank slogdet
Solving equations and inverting matrices#
solve tensorsolve lstsq inv pinv tensorinv
Exceptions#
LinAlgError
- numpy.linalg.eig(a)#
Compute the eigenvalues and right eigenvectors of a square array.
- Parameters:
- a(…, M, M) array
Matrices for which the eigenvalues and right eigenvectors will be computed
- Returns:
- w(…, M) array
The eigenvalues, each repeated according to its multiplicity. The eigenvalues are not necessarily ordered. The resulting array will be of complex type, unless the imaginary part is zero in which case it will be cast to a real type. When a is real the resulting eigenvalues will be real (0 imaginary part) or occur in conjugate pairs
- v(…, M, M) array
The normalized (unit “length”) eigenvectors, such that the column
v[:,i]
is the eigenvector corresponding to the eigenvaluew[i]
.
- Raises:
- LinAlgError
If the eigenvalue computation does not converge.
See also
eigvals
eigenvalues of a non-symmetric array.
eigh
eigenvalues and eigenvectors of a real symmetric or complex Hermitian (conjugate symmetric) array.
eigvalsh
eigenvalues of a real symmetric or complex Hermitian (conjugate symmetric) array.
scipy.linalg.eig
Similar function in SciPy that also solves the generalized eigenvalue problem.
scipy.linalg.schur
Best choice for unitary and other non-Hermitian normal matrices.
Notes
New in version 1.8.0.
Broadcasting rules apply, see the numpy.linalg documentation for details.
This is implemented using the
_geev
LAPACK routines which compute the eigenvalues and eigenvectors of general square arrays.The number w is an eigenvalue of a if there exists a vector v such that
a @ v = w * v
. Thus, the arrays a, w, and v satisfy the equationsa @ v[:,i] = w[i] * v[:,i]
for \(i \in \{0,...,M-1\}\).The array v of eigenvectors may not be of maximum rank, that is, some of the columns may be linearly dependent, although round-off error may obscure that fact. If the eigenvalues are all different, then theoretically the eigenvectors are linearly independent and a can be diagonalized by a similarity transformation using v, i.e,
inv(v) @ a @ v
is diagonal.For non-Hermitian normal matrices the SciPy function scipy.linalg.schur is preferred because the matrix v is guaranteed to be unitary, which is not the case when using eig. The Schur factorization produces an upper triangular matrix rather than a diagonal matrix, but for normal matrices only the diagonal of the upper triangular matrix is needed, the rest is roundoff error.
Finally, it is emphasized that v consists of the right (as in right-hand side) eigenvectors of a. A vector y satisfying
y.T @ a = z * y.T
for some number z is called a left eigenvector of a, and, in general, the left and right eigenvectors of a matrix are not necessarily the (perhaps conjugate) transposes of each other.References
G. Strang, Linear Algebra and Its Applications, 2nd Ed., Orlando, FL, Academic Press, Inc., 1980, Various pp.
Examples
>>> from numpy import linalg as LA
(Almost) trivial example with real e-values and e-vectors.
>>> w, v = LA.eig(np.diag((1, 2, 3))) >>> w; v array([1., 2., 3.]) array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
Real matrix possessing complex e-values and e-vectors; note that the e-values are complex conjugates of each other.
>>> w, v = LA.eig(np.array([[1, -1], [1, 1]])) >>> w; v array([1.+1.j, 1.-1.j]) array([[0.70710678+0.j , 0.70710678-0.j ], [0. -0.70710678j, 0. +0.70710678j]])
Complex-valued matrix with real e-values (but complex-valued e-vectors); note that
a.conj().T == a
, i.e., a is Hermitian.>>> a = np.array([[1, 1j], [-1j, 1]]) >>> w, v = LA.eig(a) >>> w; v array([2.+0.j, 0.+0.j]) array([[ 0. +0.70710678j, 0.70710678+0.j ], # may vary [ 0.70710678+0.j , -0. +0.70710678j]])
Be careful about round-off error!
>>> a = np.array([[1 + 1e-9, 0], [0, 1 - 1e-9]]) >>> # Theor. e-values are 1 +/- 1e-9 >>> w, v = LA.eig(a) >>> w; v array([1., 1.]) array([[1., 0.], [0., 1.]])
- numpy.linalg.matrix_power(a, n)#
Raise a square matrix to the (integer) power n.
For positive integers n, the power is computed by repeated matrix squarings and matrix multiplications. If
n == 0
, the identity matrix of the same shape as M is returned. Ifn < 0
, the inverse is computed and then raised to theabs(n)
.Note
Stacks of object matrices are not currently supported.
- Parameters:
- a(…, M, M) array_like
Matrix to be “powered”.
- nint
The exponent can be any integer or long integer, positive, negative, or zero.
- Returns:
- a**n(…, M, M) ndarray or matrix object
The return value is the same shape and type as M; if the exponent is positive or zero then the type of the elements is the same as those of M. If the exponent is negative the elements are floating-point.
- Raises:
- LinAlgError
For matrices that are not square or that (for negative powers) cannot be inverted numerically.
Examples
>>> from numpy.linalg import matrix_power >>> i = np.array([[0, 1], [-1, 0]]) # matrix equiv. of the imaginary unit >>> matrix_power(i, 3) # should = -i array([[ 0, -1], [ 1, 0]]) >>> matrix_power(i, 0) array([[1, 0], [0, 1]]) >>> matrix_power(i, -3) # should = 1/(-i) = i, but w/ f.p. elements array([[ 0., 1.], [-1., 0.]])
Somewhat more sophisticated example
>>> q = np.zeros((4, 4)) >>> q[0:2, 0:2] = -i >>> q[2:4, 2:4] = i >>> q # one of the three quaternion units not equal to 1 array([[ 0., -1., 0., 0.], [ 1., 0., 0., 0.], [ 0., 0., 0., 1.], [ 0., 0., -1., 0.]]) >>> matrix_power(q, 2) # = -np.eye(4) array([[-1., 0., 0., 0.], [ 0., -1., 0., 0.], [ 0., 0., -1., 0.], [ 0., 0., 0., -1.]])
- numpy.linalg.norm(x, ord=None, axis=None, keepdims=False)#
Matrix or vector norm.
This function is able to return one of eight different matrix norms, or one of an infinite number of vector norms (described below), depending on the value of the
ord
parameter.- Parameters:
- xarray_like
Input array. If axis is None, x must be 1-D or 2-D, unless ord is None. If both axis and ord are None, the 2-norm of
x.ravel
will be returned.- ord{non-zero int, inf, -inf, ‘fro’, ‘nuc’}, optional
Order of the norm (see table under
Notes
). inf means numpy’s inf object. The default is None.- axis{None, int, 2-tuple of ints}, optional.
If axis is an integer, it specifies the axis of x along which to compute the vector norms. If axis is a 2-tuple, it specifies the axes that hold 2-D matrices, and the matrix norms of these matrices are computed. If axis is None then either a vector norm (when x is 1-D) or a matrix norm (when x is 2-D) is returned. The default is None.
New in version 1.8.0.
- keepdimsbool, optional
If this is set to True, the axes which are normed over are left in the result as dimensions with size one. With this option the result will broadcast correctly against the original x.
New in version 1.10.0.
- Returns:
- nfloat or ndarray
Norm of the matrix or vector(s).
See also
scipy.linalg.norm
Similar function in SciPy.
Notes
For values of
ord < 1
, the result is, strictly speaking, not a mathematical ‘norm’, but it may still be useful for various numerical purposes.The following norms can be calculated:
ord
norm for matrices
norm for vectors
None
Frobenius norm
2-norm
‘fro’
Frobenius norm
–
‘nuc’
nuclear norm
–
inf
max(sum(abs(x), axis=1))
max(abs(x))
-inf
min(sum(abs(x), axis=1))
min(abs(x))
0
–
sum(x != 0)
1
max(sum(abs(x), axis=0))
as below
-1
min(sum(abs(x), axis=0))
as below
2
2-norm (largest sing. value)
as below
-2
smallest singular value
as below
other
–
sum(abs(x)**ord)**(1./ord)
The Frobenius norm is given by [1]:
\(||A||_F = [\sum_{i,j} abs(a_{i,j})^2]^{1/2}\)
The nuclear norm is the sum of the singular values.
Both the Frobenius and nuclear norm orders are only defined for matrices and raise a ValueError when
x.ndim != 2
.References
[1]G. H. Golub and C. F. Van Loan, Matrix Computations, Baltimore, MD, Johns Hopkins University Press, 1985, pg. 15
Examples
>>> from numpy import linalg as LA >>> a = np.arange(9) - 4 >>> a array([-4, -3, -2, ..., 2, 3, 4]) >>> b = a.reshape((3, 3)) >>> b array([[-4, -3, -2], [-1, 0, 1], [ 2, 3, 4]])
>>> LA.norm(a) 7.745966692414834 >>> LA.norm(b) 7.745966692414834 >>> LA.norm(b, 'fro') 7.745966692414834 >>> LA.norm(a, np.inf) 4.0 >>> LA.norm(b, np.inf) 9.0 >>> LA.norm(a, -np.inf) 0.0 >>> LA.norm(b, -np.inf) 2.0
>>> LA.norm(a, 1) 20.0 >>> LA.norm(b, 1) 7.0 >>> LA.norm(a, -1) -4.6566128774142013e-010 >>> LA.norm(b, -1) 6.0 >>> LA.norm(a, 2) 7.745966692414834 >>> LA.norm(b, 2) 7.3484692283495345
>>> LA.norm(a, -2) 0.0 >>> LA.norm(b, -2) 1.8570331885190563e-016 # may vary >>> LA.norm(a, 3) 5.8480354764257312 # may vary >>> LA.norm(a, -3) 0.0
Using the axis argument to compute vector norms:
>>> c = np.array([[ 1, 2, 3], ... [-1, 1, 4]]) >>> LA.norm(c, axis=0) array([ 1.41421356, 2.23606798, 5. ]) >>> LA.norm(c, axis=1) array([ 3.74165739, 4.24264069]) >>> LA.norm(c, ord=1, axis=1) array([ 6., 6.])
Using the axis argument to compute matrix norms:
>>> m = np.arange(8).reshape(2,2,2) >>> LA.norm(m, axis=(1,2)) array([ 3.74165739, 11.22497216]) >>> LA.norm(m[0, :, :]), LA.norm(m[1, :, :]) (3.7416573867739413, 11.224972160321824)
- numpy.linalg.tensorinv(a, ind=2)#
Compute the ‘inverse’ of an N-dimensional array.
The result is an inverse for a relative to the tensordot operation
tensordot(a, b, ind)
, i. e., up to floating-point accuracy,tensordot(tensorinv(a), a, ind)
is the “identity” tensor for the tensordot operation.- Parameters:
- aarray_like
Tensor to ‘invert’. Its shape must be ‘square’, i. e.,
prod(a.shape[:ind]) == prod(a.shape[ind:])
.- indint, optional
Number of first indices that are involved in the inverse sum. Must be a positive integer, default is 2.
- Returns:
- bndarray
a’s tensordot inverse, shape
a.shape[ind:] + a.shape[:ind]
.
- Raises:
- LinAlgError
If a is singular or not ‘square’ (in the above sense).
See also
numpy.tensordot
,tensorsolve
Examples
>>> a = np.eye(4*6) >>> a.shape = (4, 6, 8, 3) >>> ainv = np.linalg.tensorinv(a, ind=2) >>> ainv.shape (8, 3, 4, 6) >>> b = np.random.randn(4, 6) >>> np.allclose(np.tensordot(ainv, b), np.linalg.tensorsolve(a, b)) True
>>> a = np.eye(4*6) >>> a.shape = (24, 8, 3) >>> ainv = np.linalg.tensorinv(a, ind=1) >>> ainv.shape (8, 3, 24) >>> b = np.random.randn(24) >>> np.allclose(np.tensordot(ainv, b, 1), np.linalg.tensorsolve(a, b)) True
C++ API#
-
type MyType#
Some type
-
template<typename T, std::size_t N>
class std::array# Some cpp class
-
float Sphinx::version#
The description of Sphinx::version.
-
int version#
The description of version.
-
typedef std::vector<int> List#
The description of List type.
JavaScript API#
Link to
ModTopLevel()
- class module_a.submodule.ModTopLevel()#
Link to
mod_child_1()
Link to
ModTopLevel.mod_child_1()
- ModTopLevel.mod_child_1()#
Link to
mod_child_2()
- ModTopLevel.mod_child_2()#
Link to
ModTopLevel()
- class module_b.submodule.ModNested()#
- ModNested.nested_child_1()#
Link to
nested_child_2()
- ModNested.nested_child_2()#
Link to
nested_child_1()
Generated Index#
Part of the sphinx build process in generate and index file: Index.
Optional parameter args#
At this point optional parameters cannot be generated from code. However, some projects will manually do it, like so:
This example comes from django-payments module docs.
- class payments.dotpay.DotpayProvider(seller_id, pin[, channel=0[, lock=False], lang='pl'])#
This backend implements payments using a popular Polish gateway, Dotpay.pl.
Due to API limitations there is no support for transferring purchased items.
- Parameters:
seller_id – Seller ID assigned by Dotpay
pin – PIN assigned by Dotpay
channel – Default payment channel (consult reference guide)
lang – UI language
lock – Whether to disable channels other than the default selected above
Data#
- numpy.linalg.Data_item_1#
- numpy.linalg.Data_item_2#
- numpy.linalg.Data_item_3#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce congue elit eu hendrerit mattis.
Some data link Data_item_1
.
Test of persistent search field#
There should also be a persistent (non-hidden) search field in the primary sidebar of this page, and if you use the keyboard shortcut it should focus the persistent field, not overlay the hidden one.
Section to show off pages with many sub pages#
To create an additional level of nesting in the sidebar, construct a
nested toctree
:
Sub page 1#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Sub page 2#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Section with sub sub pages#
To create an additional level of nesting in the sidebar, construct a
nested toctree
:
Sub sub page 1#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Sub sub sub page 1#
Test.
Sub sub page 2#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Sub sub page 3#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem neque, interdum in ipsum nec, finibus dictum velit. Ut eu efficitur arcu, id aliquam erat. In sit amet diam gravida, imperdiet tellus eu, gravida nisl. Praesent aliquet odio eget libero elementum, quis rhoncus tellus tincidunt. Suspendisse quis volutpat ipsum. Sed lobortis scelerisque tristique. Aenean condimentum risus tellus, quis accumsan ipsum laoreet ut. Integer porttitor maximus suscipit. Mauris in posuere sapien. Aliquam accumsan feugiat ligula, nec fringilla libero commodo sed. Proin et erat pharetra.
Top-level headers and the TOC#
Your right table of contents will behave slightly differently depending on whether your page has one top-level header, or multiple top-level headers. See below for more information.
An example with multiple top-level headers#
If a page has multiple top-level headers on it, then the in-page Table of Contents will show each top-level header. On this page, there are multiple top-level headers. As a result, the top-level headers all appear in the right Table of Contents. Here’s an example of a page structure with multiple top-level headers:
My first header
===============
My sub-header
-------------
My second header
================
My second sub-header
--------------------
And here’s a second-level header#
Notice how it is nested underneath “Top-level header 2” in the TOC.
An example with a single top-level header#
If the page only has a single top-level header, it is assumed to be the page title, and only the headers underneath the top-level header will be used for the right Table of Contents.
On most pages in this documentation, only a single top-level header is used. For example, they have a page structure like:
My title
========
My header
---------
My second header
----------------