From eaefb883983ae37f743516787c5e73ffd12bb766 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Sun, 16 Feb 2014 13:56:54 +0100
Subject: [PATCH 01/21] extending examples
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
attention, see also and error directives
Signed-off-by: Torbjörn Klatt
---
demo/source/examples.rst | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/demo/source/examples.rst b/demo/source/examples.rst
index 06d695da..412e6be2 100644
--- a/demo/source/examples.rst
+++ b/demo/source/examples.rst
@@ -77,6 +77,18 @@ Danger
------
.. danger:: This is **danger**-ous.
+Attention
+---------
+.. attention:: This is a note.
+
+See Also
+--------
+.. seealso:: Probably a reference.
+
+Error
+-----
+.. error:: This is a error.
+
Footnotes
=========
I have footnoted a first item [#f1]_ and second item [#f2]_.
From 77d53da884b051c51b514bf80145486a9fc6852a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Sun, 16 Feb 2014 13:57:53 +0100
Subject: [PATCH 02/21] extending examples: autodoc/automodule
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
adding an example module to show of sphinx.ext.autodoc features
Signed-off-by: Torbjörn Klatt
---
demo/source/conf.py | 30 +++++++++++++----
demo/source/example_module.rst | 8 +++++
demo/source/index.rst | 1 +
sphinx_bootstrap_theme/example_module.py | 42 ++++++++++++++++++++++++
4 files changed, 75 insertions(+), 6 deletions(-)
create mode 100644 demo/source/example_module.rst
create mode 100644 sphinx_bootstrap_theme/example_module.py
diff --git a/demo/source/conf.py b/demo/source/conf.py
index 574b75d7..2da6a396 100644
--- a/demo/source/conf.py
+++ b/demo/source/conf.py
@@ -17,7 +17,12 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
# Disabled: , 'sphinx.ext.intersphinx'
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.ifconfig',
+ 'sphinx.ext.viewcode'
+]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -62,15 +67,15 @@
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@@ -81,8 +86,21 @@
# Enable todo output
todo_include_todos = True
+autoclass_content = 'class'
+autodoc_member_order = 'bysource'
+autodoc_default_flags = [
+ 'members',
+ 'undoc-members',
+ 'private-members',
+ # 'special-members',
+ # 'inherited-members',
+ 'show-inheritance'
+]
+
# -- Options for HTML output ---------------------------------------------------
+html_translator_class = 'sphinx_bootstrap_theme.BootstrapTranslator'
+
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'bootstrap'
@@ -218,10 +236,10 @@
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
diff --git a/demo/source/example_module.rst b/demo/source/example_module.rst
new file mode 100644
index 00000000..dfc86d86
--- /dev/null
+++ b/demo/source/example_module.rst
@@ -0,0 +1,8 @@
+Example Module (:mod:`ExampleModule`)
+=====================================
+
+.. automodule:: sphinx_bootstrap_theme.example_module
+ :members:
+ :undoc-members:
+ :private-members:
+ :special-members:
diff --git a/demo/source/index.rst b/demo/source/index.rst
index f7a7b20a..b6f3bd23 100644
--- a/demo/source/index.rst
+++ b/demo/source/index.rst
@@ -22,6 +22,7 @@ Setting up and using the theme.
examples
sidebar
subdir/index
+ example_module.rst
Development history and feature wish lists.
diff --git a/sphinx_bootstrap_theme/example_module.py b/sphinx_bootstrap_theme/example_module.py
new file mode 100644
index 00000000..8380466a
--- /dev/null
+++ b/sphinx_bootstrap_theme/example_module.py
@@ -0,0 +1,42 @@
+# coding=utf-8
+"""
+Example Module to show of :py:mod:`sphinx.autodoc` features with **Twitter Bootstrap**.
+
+.. moduleauthor:: Torbjörn Klatt
+"""
+
+
+class ExampleClass(object):
+ """
+ Example Class
+
+ An example class, which is documented.
+ """
+
+ def __init__(self):
+ """
+ Constructor
+
+ Constructs an instance of :py:class:`.Example`.
+ """
+ self._private_variable = None
+
+ def hello(self, greeting='Hello', name='world'):
+ """
+ Prints a welcome message
+
+ :param str greeting: Greeting text.
+ A second line.
+
+ And second paragraph.
+ :param str name: Name to greet.
+ :returns: nothing
+ :raises ValueError: if ``name`` is not a string or empty
+ """
+ if isinstance(name, str) and str != '':
+ print('%s %s!!' % (greeting, name))
+ else:
+ raise ValueError("Name must not be empty.")
+
+
+__all__ = ['ExampleClass']
From 9d4a1554dd60028940eccb58246896f197b0e850 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Sun, 16 Feb 2014 14:00:39 +0100
Subject: [PATCH 03/21] first draft of BootstrapHtmlTranslator
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Most sphinx and docutils directives are implemented to produce
bootstrap-aware html.
Some stuff needs some tweaking and cleanup (it's mostly a little too
hacky).
Signed-off-by: Torbjörn Klatt
---
sphinx_bootstrap_theme/__init__.py | 3 +
.../html_bootstrap_translator.py | 413 ++++++++++++++++++
2 files changed, 416 insertions(+)
create mode 100644 sphinx_bootstrap_theme/html_bootstrap_translator.py
diff --git a/sphinx_bootstrap_theme/__init__.py b/sphinx_bootstrap_theme/__init__.py
index 08736913..37e08524 100644
--- a/sphinx_bootstrap_theme/__init__.py
+++ b/sphinx_bootstrap_theme/__init__.py
@@ -6,7 +6,10 @@
__version__ = ".".join(str(v) for v in VERSION)
__version_full__ = __version__
+
def get_html_theme_path():
"""Return list of HTML theme paths."""
cur_dir = os.path.abspath(os.path.dirname(__file__))
return [cur_dir]
+
+from sphinx_bootstrap_theme.html_bootstrap_translator import BootstrapTranslator
diff --git a/sphinx_bootstrap_theme/html_bootstrap_translator.py b/sphinx_bootstrap_theme/html_bootstrap_translator.py
new file mode 100644
index 00000000..99a02421
--- /dev/null
+++ b/sphinx_bootstrap_theme/html_bootstrap_translator.py
@@ -0,0 +1,413 @@
+# coding=utf-8
+import sys
+import re
+
+from docutils import nodes
+from docutils.writers.html4css1 import HTMLTranslator
+from sphinx import addnodes
+from sphinx.locale import admonitionlabels, l_
+
+
+admonitionlabels['todo'] = l_('Todo')
+
+
+alert_classes = {
+ 'attention': 'primary',
+ 'caution': 'warning',
+ 'danger': 'danger',
+ 'error': 'danger',
+ 'hint': 'info',
+ 'important': 'primary',
+ 'note': 'info',
+ 'seealso': 'info',
+ 'tip': 'primary',
+ 'warning': 'warning',
+ 'todo': 'info'
+}
+
+
+member_types = {
+ 'function': 'function ',
+ 'method': 'method ',
+ 'attribute': 'attribute ',
+ 'staticmethod': 'static ',
+ 'static': 'static '
+}
+
+
+split_parameter_types = re.compile('\sor\s|,\s')
+
+
+class BootstrapTranslator(HTMLTranslator):
+ # Twitter Bootstrap is HTML5
+ doctype = '\n'
+
+ def __init__(self, builder, *args, **kwds):
+ # mostly copied from sphinx.writer.HTMLTranslator
+ HTMLTranslator.__init__(self, *args, **kwds)
+ self.highlighter = builder.highlighter
+ self.no_smarty = 0
+ self.builder = builder
+ self.highlightlang = builder.config.highlight_language
+ self.highlightlinenothreshold = sys.maxsize
+ self.protect_literal_text = 0
+ self.permalink_text = builder.config.html_add_permalinks
+ # support backwards-compatible setting to a bool
+ if not isinstance(self.permalink_text, basestring):
+ self.permalink_text = self.permalink_text and u'\u00B6' or ''
+ self.permalink_text = self.encode(self.permalink_text)
+ self.secnumber_suffix = builder.config.html_secnumber_suffix
+ self.param_separator = ''
+ self.optional_param_level = 0
+ self._table_row_index = 0
+ self.field_context = []
+ self.collapse_context = []
+ self.collapse_id_count = 0
+ self.collapse_item_count = 0
+
+ def visit_comment(self, node):
+ self.body.append(self.starttag(node, 'div', '', CLASS='comment hidden'))
+ def depart_comment(self, node):
+ self.body.append('')
+
+ def visit_compact_paragraph(self, node):
+ self.body.append(self.starttag(node, 'span', CLASS='compact-paragraph'))
+ def depart_compact_paragraph(self, node):
+ self.body.append('')
+
+ def visit_admonition(self, node, name=''):
+ if name:
+ self.body.append(self.starttag(node, 'div', CLASS='panel panel-%s' % alert_classes[name]))
+ self.body.append('%s
\n' % admonitionlabels[name])
+ self.body.append(self.starttag(node, 'div', CLASS='panel-body'))
+ else:
+ self.body.append(self.starttag(node, 'div', CLASS='panel panel-default'))
+ def depart_admonition(self, node=None, name=''):
+ if name:
+ # closing off the additional div.panel-body
+ self.body.append('\n')
+ self.body.append('\n')
+
+ def visit_note(self, node):
+ self.visit_admonition(node, 'note')
+ def depart_note(self, node):
+ self.depart_admonition(node, 'note')
+
+ def visit_attention(self, node):
+ self.visit_admonition(node, 'attention')
+ def depart_attention(self, node):
+ self.depart_admonition(node, 'attention')
+
+ def visit_warning(self, node):
+ self.visit_admonition(node, 'warning')
+ def depart_warning(self, node):
+ self.depart_admonition(node, 'warning')
+
+ def visit_danger(self, node):
+ self.visit_admonition(node, 'danger')
+ def depart_danger(self, node):
+ self.depart_admonition(node, 'danger')
+
+ def visit_error(self, node):
+ self.visit_admonition(node, 'error')
+ def depart_error(self, node):
+ self.depart_admonition(node, 'error')
+
+ def visit_seealso(self, node):
+ self.visit_admonition(node, 'seealso')
+ def depart_seealso(self, node):
+ self.depart_admonition(node, 'seealso')
+
+ def visit_todo_node(self, node):
+ node.remove(node[0]) # remove additional 'Todo' title node
+ self.visit_admonition(node, 'todo')
+ def depart_todo_node(self, node):
+ self.depart_admonition(node, 'todo')
+
+ def visit_index(self, node):
+ pass
+ def depart_index(self, node):
+ pass
+
+ def visit_bullet_list(self, node):
+ atts = {}
+ old_compact_simple = self.compact_simple
+ self.context.append((self.compact_simple, self.compact_p))
+ self.compact_p = None
+ self.compact_simple = self.is_compactable(node)
+ if self.compact_simple and not old_compact_simple:
+ atts['class'] = ' simple'
+ self.body.append(self.starttag(node, 'ul', **atts))
+
+ def visit_list_item(self, node):
+ self.body.append(self.starttag(node, 'li', '',))
+ if len(node):
+ node[0]['classes'].append('first')
+
+ def visit_field_list(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='panel-group field-list'))
+ def depart_field_list(self, node):
+ self.body.append('\n')
+
+ def visit_field(self, node):
+ _contextual_class = 'default'
+ _field_name_title = node[0][0].astext()
+ if _field_name_title == 'Raises':
+ _contextual_class = 'warning'
+ if _field_name_title == 'Return type':
+ _field_name_title = 'Returns'
+ node[0].replace(node[0][0], nodes.Text('Returns'))
+ if node[0][0].astext() in ['Raises']:
+ if node[0].__len__() == 3:
+ node[1][0].insert(0, nodes.Text(' -- '))
+ node[1][0].insert(0, nodes.strong(text=node[0][2].astext()))
+ del node[0][2]
+ del node[0][1]
+ self.field_context.append(_field_name_title)
+ self.body.append(self.starttag(node, 'div', CLASS='panel panel-%s field' % _contextual_class))
+ self.section_level += 1
+ def depart_field(self, node):
+ self.field_context.pop()
+ self.section_level -= 1
+ self.body.append('')
+
+ def visit_field_name(self, node):
+ self.body.append(self.starttag(node, 'div', '', CLASS='panel-heading field-name'))
+ self.body.append('' % self.section_level)
+ def depart_field_name(self, node):
+ self.body.append('' % self.section_level)
+
+ def visit_field_body(self, node):
+ self.body.append(self.starttag(node, 'div', '', CLASS='panel-body field-body'))
+ if self.field_context[-1] in ['Parameters']:
+ self._print_parameters(node)
+ if self.field_context[-1] in ['Raises']:
+ self._print_parameters(node)
+ def depart_field_body(self, node):
+ self.body.append('')
+
+ def visit_table(self, node):
+ self.context.append(self.compact_p)
+ self.compact_p = True
+ classes = ' '.join(['table', self.settings.table_style]).strip()
+ self.body.append(
+ self.starttag(node, 'table', CLASS=classes))
+
+ def depart_table(self, node):
+ self.compact_p = self.context.pop()
+ self.body.append('\n')
+
+
+ def visit_desc(self, node):
+ if node['objtype'] in member_types.keys() and node['objtype'] != 'staticmethod':
+ node[0].insert(0, addnodes.desc_annotation(text=member_types[node['objtype']]))
+ self.body.append(self.starttag(node, 'div',
+ CLASS='panel panel-default %s' % node['objtype']))
+ if node['objtype'] in ['class']:
+ self.collapse_context.append([node['objtype'], '%s-id%d' % (node['objtype'], self.collapse_id_count)])
+ self.collapse_id_count += 1
+ if len(self.collapse_context) > 0 and node['objtype'] in member_types.keys():
+ self.collapse_context[-1].append('%s-id%d' % (node['objtype'], self.collapse_item_count))
+ self.collapse_item_count += 1
+ self.section_level += 1
+ def depart_desc(self, node):
+ self.section_level -= 1
+ if node['objtype'] in ['class']:
+ self.collapse_context.pop()
+ if len(self.collapse_context) > 0 and node['objtype'] in member_types.keys():
+ self.collapse_context[-1].pop()
+ self.body.append('')
+
+ def visit_desc_signature(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='panel-heading desc-signature'))
+ self.body.append('' % self.section_level)
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 3:
+ self.body.append('' % (self.collapse_context[-1][1], self.collapse_context[-1][2]))
+ def depart_desc_signature(self, node):
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 3:
+ self.body.append('')
+ self.body.append('' % self.section_level)
+
+ def visit_desc_name(self, node):
+ self.body.append(self.starttag(node, 'tt', '', CLASS='desc-name'))
+ def depart_desc_name(self, node):
+ self.body.append('')
+
+ def visit_desc_addname(self, node):
+ self.body.append(self.starttag(node, 'tt', '', CLASS='desc-addname'))
+ def depart_desc_addname(self, node):
+ self.body.append('')
+
+ def visit_desc_parameterlist(self, node):
+ """
+ The list of parameters in a method signature.
+ Including the enclosing brackets.
+ """
+ self.body.append(self.starttag(node, 'tt', '', CLASS='desc-parameterlist'))
+ self.body.append('(')
+ self.first_param = 1
+ self.optional_param_level = 0
+ # How many required parameters are left.
+ self.required_params_left = sum([isinstance(c, addnodes.desc_parameter)
+ for c in node.children])
+ self.param_separator = node.child_text_separator
+ def depart_desc_parameterlist(self, node):
+ self.body.append(')')
+ self.body.append('')
+
+ def visit_desc_parameter(self, node):
+ """
+ A single parameter in a method signature
+ """
+ self.body.append(self.starttag(node, 'span', '', CLASS='desc-parameter'))
+ if self.first_param:
+ self.first_param = 0
+ elif not self.required_params_left:
+ self.body.append(self.param_separator)
+ if self.optional_param_level == 0:
+ self.required_params_left -= 1
+ if not node.hasattr('noemph'):
+ self.body.append('')
+ def depart_desc_parameter(self, node):
+ if not node.hasattr('noemph'):
+ self.body.append('')
+ if self.required_params_left:
+ self.body.append(self.param_separator)
+ self.body.append('')
+
+ def visit_desc_optional(self, node):
+ """
+ Optional parameters in a method signature.
+ """
+ self.body.append(self.starttag(node, 'span', '', CLASS='desc-optional'))
+ self.optional_param_level += 1
+ self.body.append('[')
+ def depart_desc_optional(self, node):
+ self.optional_param_level -= 1
+ self.body.append(']')
+
+ def visit_desc_returns(self, node):
+ self.body.append(self.starttag(node, 'tt', '', CLASS='desc-returns'))
+ def depart_desc_returns(self, node):
+ self.body.append('')
+
+ def visit_desc_content(self, node):
+ """
+ The content of a class, method or attribute.
+ """
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 2:
+ self.body.append('' % self.collapse_context[-1][1])
+ self.collapse_id_count += 1
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 3:
+ self.body.append('
' % (self.collapse_context[-1][2]))
+ self.body.append(self.starttag(node, 'div', CLASS='panel-body desc-content'))
+ def depart_desc_content(self, node):
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 2:
+ self.body.append('
')
+ if len(self.collapse_context) > 0 and self.collapse_context[-1][0] in ['class'] \
+ and len(self.collapse_context[-1]) == 3:
+ self.body.append('
')
+ self.body.append('')
+
+ def visit_desc_annotation(self, node):
+ """
+ The ``class``, ``method`` or ``attribute`` in a class signature or the
+ value of a class or instance attribute.
+ """
+ _cls = 'desc-annotation'
+ if node[0].astext() in member_types.values():
+ _cls += ' desc-annotation-type'
+ self.body.append(self.starttag(node, 'tt', '', CLASS=_cls))
+ def depart_desc_annotation(self, node):
+ self.body.append('')
+
+ def visit_toctree(self, node):
+ self.body.append(self.starttag(node, 'span', CLASS='toctree'))
+ def depart_toctree(self, node):
+ self.body.append('')
+
+ # the following four methods are taken from
+ # http://stackoverflow.com/a/15562804
+ def visit_displaymath(self, node):
+ import sphinx.ext.mathjax
+ sphinx.ext.mathjax.html_visit_displaymath(self, node)
+ def depart_displaymath(self, node):
+ return
+ def visit_math(self, node):
+ import sphinx.ext.mathjax
+ sphinx.ext.mathjax.html_visit_math(self, node)
+ def depart_math(self, node):
+ return
+
+ def unknown_visit(self, node):
+ raise NotImplementedError('Unknown node: %s' % node.__class__.__name__)
+
+ def _print_parameters(self, node):
+ self.body.append(''
+ ''
+ ''
+ ''
+ ''
+ ''
+ '')
+ # print("Raw: '%s'" % node.__str__())
+ if isinstance(node.children[0], nodes.paragraph):
+ self._print_single_parameter(node[0])
+ elif isinstance(node.children[0], nodes.bullet_list):
+ for _c in node.children[0]:
+ self._print_single_parameter(_c[0])
+ self.body.append('
')
+ node.clear()
+
+ def _print_single_parameter(self, node):
+ _name = None
+ _do_type = False
+ _type = None
+ _do_desc = False
+ _desc = []
+ for _c in node.children:
+ if _do_desc:
+ _desc.append(_c)
+ elif node.children.index(_c) == 0 and isinstance(_c, nodes.strong):
+ _name = _c[0].astext()
+ elif isinstance(_c, nodes.Text):
+ if _do_type is False and _c.astext() == ' (':
+ _do_type = True
+ _type = ''
+ elif _c.astext() == ')':
+ _do_type = False
+ elif _c.astext() == ' -- ':
+ _do_desc = True
+ elif isinstance(_c, nodes.emphasis) and _do_type and _type == '':
+ _type = _c[0]
+
+ self.body.append(''
+ ''
+ ' %s ' % _name +
+ ' | '
+ ''
+ ' ')
+
+ _type_str = ''
+ if isinstance(_type, nodes.Text):
+ _types = split_parameter_types.split(_type.astext())
+ _type_str += ''
+ if len(_types) > 1:
+ _type_str += ' , '.join(_types)
+ elif len(_types) == 1:
+ _type_str += _types[0]
+ _type_str += ' '
+ self.body.append('%s | ' % _type_str +
+ ''
+ ' ')
+
+ for _desc_elem in _desc:
+ _desc_elem.walkabout(self)
+ self.body.append(' '
+ ' | '
+ '
')
From d994b1b406e933853060ff6e0e0dde5d16bafc4c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Sun, 16 Feb 2014 14:01:14 +0100
Subject: [PATCH 04/21] custom javascript to patch the HTML shouldn't be needed
any more
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Torbjörn Klatt
---
sphinx_bootstrap_theme/bootstrap/layout.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/sphinx_bootstrap_theme/bootstrap/layout.html b/sphinx_bootstrap_theme/bootstrap/layout.html
index 18e16904..d2e87c4b 100644
--- a/sphinx_bootstrap_theme/bootstrap/layout.html
+++ b/sphinx_bootstrap_theme/bootstrap/layout.html
@@ -42,8 +42,7 @@
{% set script_files = script_files + [
'_static/js/jquery-1.11.0.min.js',
'_static/js/jquery-fix.js',
- '_static/bootstrap-' + bootstrap_version + '/js/bootstrap.min.js',
- '_static/bootstrap-sphinx.js'
+ '_static/bootstrap-' + bootstrap_version + '/js/bootstrap.min.js'
]
%}
From 716d59c2a7f23badb7b3cb98adec4ac5dc738649 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Tue, 18 Feb 2014 10:37:30 +0100
Subject: [PATCH 05/21] add support for Notes+Examples as admonitions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Notes and Examples can be rendered as admonitions automatically when
using the Napoleon docstring parser.
Signed-off-by: Torbjörn Klatt
---
.../html_bootstrap_translator.py | 31 +++++++++++++++++--
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/sphinx_bootstrap_theme/html_bootstrap_translator.py b/sphinx_bootstrap_theme/html_bootstrap_translator.py
index 99a02421..1f9c5812 100644
--- a/sphinx_bootstrap_theme/html_bootstrap_translator.py
+++ b/sphinx_bootstrap_theme/html_bootstrap_translator.py
@@ -9,6 +9,7 @@
admonitionlabels['todo'] = l_('Todo')
+admonitionlabels['example'] = l_('Example')
alert_classes = {
@@ -22,7 +23,8 @@
'seealso': 'info',
'tip': 'primary',
'warning': 'warning',
- 'todo': 'info'
+ 'todo': 'info',
+ 'example': 'info'
}
@@ -76,6 +78,17 @@ def depart_compact_paragraph(self, node):
self.body.append('')
def visit_admonition(self, node, name=''):
+ if isinstance(node[0], nodes.title):
+ if node[0].astext() in ['Example', 'Examples']:
+ del node['classes']
+ del node[0]
+ self.context.append('example')
+ name = 'example'
+ elif node[0].astext() in ['Note', 'Notes']:
+ del node['classes']
+ del node[0]
+ self.context.append('note')
+ name = 'note'
if name:
self.body.append(self.starttag(node, 'div', CLASS='panel panel-%s' % alert_classes[name]))
self.body.append('%s
\n' % admonitionlabels[name])
@@ -86,6 +99,9 @@ def depart_admonition(self, node=None, name=''):
if name:
# closing off the additional div.panel-body
self.body.append('\n')
+ elif self.context[-1] in alert_classes.keys():
+ self.body.append('\n')
+ self.context.pop()
self.body.append('\n')
def visit_note(self, node):
@@ -118,6 +134,11 @@ def visit_seealso(self, node):
def depart_seealso(self, node):
self.depart_admonition(node, 'seealso')
+ def visit_example(self, node):
+ self.visit_admonition(node, 'example')
+ def depart_example(self, node):
+ self.depart_admonition(node, 'example')
+
def visit_todo_node(self, node):
node.remove(node[0]) # remove additional 'Todo' title node
self.visit_admonition(node, 'todo')
@@ -365,6 +386,7 @@ def _print_parameters(self, node):
node.clear()
def _print_single_parameter(self, node):
+ _do_name = True
_name = None
_do_type = False
_type = None
@@ -373,15 +395,18 @@ def _print_single_parameter(self, node):
for _c in node.children:
if _do_desc:
_desc.append(_c)
- elif node.children.index(_c) == 0 and isinstance(_c, nodes.strong):
+ elif _do_name and node.children.index(_c) == 0 and isinstance(_c, (nodes.strong, nodes.literal)):
_name = _c[0].astext()
+ _do_name = False
elif isinstance(_c, nodes.Text):
if _do_type is False and _c.astext() == ' (':
_do_type = True
_type = ''
elif _c.astext() == ')':
_do_type = False
- elif _c.astext() == ' -- ':
+ elif _c.astext().startswith(' -- '):
+ if len(_c.replace(' -- ', '', 1)) > 0:
+ _desc.append(nodes.Text(_c.replace(' -- ', '', 1)))
_do_desc = True
elif isinstance(_c, nodes.emphasis) and _do_type and _type == '':
_type = _c[0]
From 46180c2520f25ad02bdb97d5ffa6edc11e451482 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Tue, 18 Feb 2014 11:19:33 +0100
Subject: [PATCH 06/21] fix admonition heading level; blockquote footer
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
also annotation style in member signatures
Signed-off-by: Torbjörn Klatt
---
.../html_bootstrap_translator.py | 23 +++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/sphinx_bootstrap_theme/html_bootstrap_translator.py b/sphinx_bootstrap_theme/html_bootstrap_translator.py
index 1f9c5812..0471cb79 100644
--- a/sphinx_bootstrap_theme/html_bootstrap_translator.py
+++ b/sphinx_bootstrap_theme/html_bootstrap_translator.py
@@ -77,6 +77,20 @@ def visit_compact_paragraph(self, node):
def depart_compact_paragraph(self, node):
self.body.append('')
+ def visit_attribution(self, node):
+ if isinstance(node.parent, nodes.block_quote):
+ self.body.append(self.starttag(node, 'footer'))
+ else:
+ prefix, suffix = self.attribution_formats[self.settings.attribution]
+ self.context.append(suffix)
+ self.body.append(self.starttag(node, 'p', prefix, CLASS='attribution'))
+
+ def depart_attribution(self, node):
+ if isinstance(node.parent, nodes.block_quote):
+ self.body.append("\n")
+ else:
+ self.body.append(self.context.pop() + '\n')
+
def visit_admonition(self, node, name=''):
if isinstance(node[0], nodes.title):
if node[0].astext() in ['Example', 'Examples']:
@@ -91,7 +105,12 @@ def visit_admonition(self, node, name=''):
name = 'note'
if name:
self.body.append(self.starttag(node, 'div', CLASS='panel panel-%s' % alert_classes[name]))
- self.body.append('%s
\n' % admonitionlabels[name])
+ self.section_level += 1
+ self.body.append(
+ '%s
\n' %
+ (self.section_level, admonitionlabels[name], self.section_level)
+ )
+ self.section_level -= 1
self.body.append(self.starttag(node, 'div', CLASS='panel-body'))
else:
self.body.append(self.starttag(node, 'div', CLASS='panel panel-default'))
@@ -342,7 +361,7 @@ def visit_desc_annotation(self, node):
"""
_cls = 'desc-annotation'
if node[0].astext() in member_types.values():
- _cls += ' desc-annotation-type'
+ _cls += ' desc-annotation-type text-muted'
self.body.append(self.starttag(node, 'tt', '', CLASS=_cls))
def depart_desc_annotation(self, node):
self.body.append('')
From d3e58b0bcab6e953205ec95aa39d37176f084e36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torbj=C3=B6rn=20Klatt?=
Date: Tue, 18 Feb 2014 16:25:44 +0100
Subject: [PATCH 07/21] fix display of return values for Napoleon parser output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Torbjörn Klatt
---
.../html_bootstrap_translator.py | 73 +++++++++++++------
1 file changed, 49 insertions(+), 24 deletions(-)
diff --git a/sphinx_bootstrap_theme/html_bootstrap_translator.py b/sphinx_bootstrap_theme/html_bootstrap_translator.py
index 0471cb79..a3568f89 100644
--- a/sphinx_bootstrap_theme/html_bootstrap_translator.py
+++ b/sphinx_bootstrap_theme/html_bootstrap_translator.py
@@ -38,6 +38,7 @@
split_parameter_types = re.compile('\sor\s|,\s')
+parameter_desc_start = re.compile('^[\s]--[\s]')
class BootstrapTranslator(HTMLTranslator):
@@ -189,14 +190,31 @@ def visit_field_list(self, node):
def depart_field_list(self, node):
self.body.append('\n')
+ def _fixup_return_type(self, node):
+ if node[0].astext() == 'Returns':
+ _return = node
+ _this_index = node.parent.index(node)
+
+ if len(node.parent.children) > _this_index + 1:
+ if node.parent[_this_index + 1][0].astext() == 'Return type':
+ _rtype = node.parent[_this_index + 1]
+ _rtype_new = [nodes.Text(' (')]
+ if isinstance(_rtype[1][0], nodes.paragraph):
+ for elem in _rtype[1][0]:
+ _rtype_new.append(elem.deepcopy())
+ _rtype_new.append(nodes.Text(')'))
+ _return[1][0].insert(1, _rtype_new)
+
def visit_field(self, node):
+ if node[0][0].astext() == 'Return type':
+ # return type should be handelt by _fixup_return_type on 'Returns' nodes
+ raise nodes.SkipNode
+
_contextual_class = 'default'
+ self._fixup_return_type(node)
_field_name_title = node[0][0].astext()
if _field_name_title == 'Raises':
_contextual_class = 'warning'
- if _field_name_title == 'Return type':
- _field_name_title = 'Returns'
- node[0].replace(node[0][0], nodes.Text('Returns'))
if node[0][0].astext() in ['Raises']:
if node[0].__len__() == 3:
node[1][0].insert(0, nodes.Text(' -- '))
@@ -219,9 +237,7 @@ def depart_field_name(self, node):
def visit_field_body(self, node):
self.body.append(self.starttag(node, 'div', '', CLASS='panel-body field-body'))
- if self.field_context[-1] in ['Parameters']:
- self._print_parameters(node)
- if self.field_context[-1] in ['Raises']:
+ if self.field_context[-1] in ['Parameters', 'Raises', 'Returns']:
self._print_parameters(node)
def depart_field_body(self, node):
self.body.append('')
@@ -395,7 +411,9 @@ def _print_parameters(self, node):
''
''
'')
- # print("Raw: '%s'" % node.__str__())
+
+ # if self.field_context[-1] in ['Returns']:
+ # print("\n\nRaw %s: '%s'" % (self.field_context[-1], node.__str__()))
if isinstance(node.children[0], nodes.paragraph):
self._print_single_parameter(node[0])
elif isinstance(node.children[0], nodes.bullet_list):
@@ -408,7 +426,7 @@ def _print_single_parameter(self, node):
_do_name = True
_name = None
_do_type = False
- _type = None
+ _types = []
_do_desc = False
_desc = []
for _c in node.children:
@@ -421,14 +439,19 @@ def _print_single_parameter(self, node):
if _do_type is False and _c.astext() == ' (':
_do_type = True
_type = ''
- elif _c.astext() == ')':
+ if _c.astext() == ')':
+ _c.replace(')', '', 1)
_do_type = False
- elif _c.astext().startswith(' -- '):
- if len(_c.replace(' -- ', '', 1)) > 0:
- _desc.append(nodes.Text(_c.replace(' -- ', '', 1)))
+ if parameter_desc_start.search(_c.astext()) is not None:
+ _d = parameter_desc_start.findall(_c.astext())
+ if len(re.sub(parameter_desc_start, '', _c.__str__(), count=1).strip()) > 0:
+ _desc.append(nodes.Text(re.sub(parameter_desc_start, '', _c.__str__(), count=1)))
_do_desc = True
- elif isinstance(_c, nodes.emphasis) and _do_type and _type == '':
- _type = _c[0]
+ elif isinstance(_c, (nodes.emphasis, nodes.literal, nodes.reference)) and _do_type:
+ if isinstance(_c, (nodes.emphasis, nodes.literal)):
+ _types.append(_c[0])
+ else:
+ _types.append(_c)
self.body.append(''
''
@@ -437,16 +460,18 @@ def _print_single_parameter(self, node):
' | '
' ')
- _type_str = ''
- if isinstance(_type, nodes.Text):
- _types = split_parameter_types.split(_type.astext())
- _type_str += ''
- if len(_types) > 1:
- _type_str += ' , '.join(_types)
- elif len(_types) == 1:
- _type_str += _types[0]
- _type_str += ' '
- self.body.append('%s | ' % _type_str +
+ print("Types: %s" % _types)
+ for _ti in range(0, len(_types)):
+ if len(_types) > 1 and _ti > 0:
+ self.body.append(', ')
+ self.body.append('')
+ if isinstance(_types[_ti], nodes.reference):
+ _types[_ti].walkabout(self)
+ else:
+ self.body.append(_types[_ti].astext())
+ self.body.append('
')
+
+ self.body.append(''
''
' |