:py:`mcss_settings` Dict containing all m.css settings
:py:`jinja_environment` Jinja2 environment. Useful for adding new filters
etc.
+:py:`module_doc_contents` Module documentation contents
+:py:`class_doc_contents` Class documentation contents
+:py:`data_doc_contents` Data documentation contents
=========================== ===================================================
+The :py:`module_doc_contents`, :py:`class_doc_contents` and
+:py:`data_doc_contents` variables are :py:`Dict[str, Dict[str, str]]`, where
+the first level is a name and second level are key/value pairs of the actual
+HTML documentation content. Plugins that parse extra documentation inputs (such
+as `m.sphinx`_) are supposed to add to the dict, which is then used to fill the
+actual documentation contents. The following corresponds to the documentation
+source shown in the `External documentation content`_ section below.
+
+.. code:: py
+
+ class_doc_contents['mymodule.sub.Class'] = {
+ 'summary': "A pretty class",
+ 'details': "This class is *pretty*."
+ }
+
Registration function for a plugin that needs to query the :py:`OUTPUT` setting
might look like this --- the remaining keyword arguments will collapse into
the :py:`**kwargs` parameter. See code of various m.css plugins for actual
global output_dir
output_dir = mcss_settings['OUTPUT']
+`External documentation content`_
+=================================
+
+Because it's often not feasible to have the whole documentation stored in
+Python docstrings, the generator allows you to supply documentation from
+external files. Similarly to `pages`_, the :py:`INPUT_DOCS` setting is a list
+of :abbr:`reST <reStructuredText>` files that contain documentation for
+particular names using custom directives. This is handled by the bundled
+`m.sphinx <{filename}/plugins/sphinx.rst>`_ plugin. See its documentation for
+detailed description of all features, below is a simple example of using it to
+document a class:
+
+.. code:: py
+
+ PLUGINS = ['m.sphinx']
+
+.. code:: rst
+
+ .. py:class:: mymodule.sub.Class
+ :summary: A pretty class
+
+ This class is *pretty*.
+
`pybind11 compatibility`_
=========================
:gh:`m.gl <mosra/m.css$master/plugins/m/gl.py>`,
:gh:`m.vk <mosra/m.css$master/plugins/m/vk.py>`,
:gh:`m.abbr <mosra/m.css$master/plugins/m/abbr.py>`,
- :gh:`m.filesize <mosra/m.css$master/plugins/m/filesize.py>`,
- :gh:`m.alias <mosra/m.css$master/plugins/m/alias.py>`
+ :gh:`m.filesize <mosra/m.css$master/plugins/m/filesize.py>`
+- :gh:`m.alias <mosra/m.css$master/plugins/m/alias.py>`
+ :label-flat-primary:`pelican only`
- :gh:`m.metadata <mosra/m.css$master/plugins/m/metadata.py>`
+ :label-flat-primary:`pelican only`
+- :gh:`m.sphinx <mosra/m.css$master/plugins/m/metadata.py>`
+ :label-flat-warning:`python doc only`
For the `Python doc theme <{filename}/documentation/python.rst>`_ it's enough
to simply list them in :py:`PLUGINS`. For the `Doxygen theme <{filename}/documentation/doxygen.rst>`_,
and images to authors, categories and tags. The information can then appear on
article listing page, as a badge under the article or be added to social meta
tags.
+
+`Sphinx » <{filename}/plugins/sphinx.rst>`_
+===========================================
+
+The :py:`m.sphinx` plugin brings Sphinx-compatible directives for documenting
+modules, classes and other to the `Python doc theme`_.
.. note-dim::
:class: m-text-center
- `« Links and other <{filename}/plugins/links.rst>`_ | `Plugins <{filename}/plugins.rst>`_
+ `« Links and other <{filename}/plugins/links.rst>`_ | `Plugins <{filename}/plugins.rst>`_ | `Sphinx » <{filename}/plugins/sphinx.rst>`_
.. role:: html(code)
:language: html
--- /dev/null
+..
+ This file is part of m.css.
+
+ Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+..
+
+Sphinx
+######
+
+:breadcrumb: {filename}/plugins.rst Plugins
+:footer:
+ .. note-dim::
+ :class: m-text-center
+
+ `« Metadata <{filename}/plugins/metadata.rst>`_ | `Plugins <{filename}/plugins.rst>`_
+
+.. role:: html(code)
+ :language: html
+.. role:: py(code)
+ :language: py
+.. role:: rst(code)
+ :language: rst
+
+Makes it possible to document APIs with the `Python doc theme <{filename}/documentation/python.rst>`_
+using external files in a way similar to `Sphinx <https://www.sphinx-doc.org/>`_.
+
+.. contents::
+ :class: m-block m-default
+
+`How to use`_
+=============
+
+`Pelican`_
+----------
+
+List the plugin in your :py:`PLUGINS`.
+
+.. code:: py
+
+ PLUGINS += ['m.sphinx']
+
+`Module, class and data docs`_
+==============================
+
+The :rst:`.. py:module::`, :rst:`.. py:class::` and :rst:`.. py:data::`
+directives provide a way to supply module, class and data documentation
+content. Directive option is the name to document, directive contents are
+the actual contents; in addition the :py:`:summary:` option can override the
+docstring extracted using inspection. No restrictions are made on the contents,
+it's possible to make use of any additional plugins in the markup. Example:
+
+.. code:: rst
+
+ .. py:module:: mymodule
+ :summary: A top-level module.
+
+ This is the top-level module.
+
+ Usage
+ -----
+
+ .. code:: pycon
+
+ >>> import mymodule
+ >>> mymodule.foo()
+ Hello world!
+
+ .. py:data:: mymodule.ALMOST_PI
+ :summary: :math:`\pi`, but *less precise*.
+
+Compared to docstrings, the :py:`:summary:` is interpreted as
+:abbr:`reST <reStructuredText>`, which means you can keep the docstring
+formatting simpler (for display inside IDEs or via the builtin :py:`help()`),
+while supplying an alternative and more complex-formatted summary for the
+actual rendered docs.
+
+.. note-info::
+
+ Modules, classes and data described using these directives have to actually
+ exist (i.e., accessible via inspection) in given module. If given name
+ doesn't exist, a warning will be printed during processing and the
+ documentation ignored.
'OUTPUT': 'output',
'INPUT_MODULES': [],
'INPUT_PAGES': [],
+ 'INPUT_DOCS': [],
+ 'OUTPUT': 'output',
'THEME_COLOR': '#22272e',
'FAVICON': 'favicon-dark.png',
'STYLESHEETS': [
self.class_index: List[IndexEntry] = []
self.page_index: List[IndexEntry] = []
self.module_mapping: Dict[str, str] = {}
+ self.module_docs: Dict[str, Dict[str, str]] = {}
+ self.class_docs: Dict[str, Dict[str, str]] = {}
+ self.data_docs: Dict[str, Dict[str, str]] = {}
def is_internal_function_name(name: str) -> bool:
"""If the function name is internal.
end = original_signature.find('\n')
logging.warning("cannot parse pybind11 function signature %s", original_signature[:end if end != -1 else None])
if end != -1 and len(original_signature) > end + 1 and original_signature[end + 1] == '\n':
- summary = extract_summary(original_signature[end + 1:])
+ summary = extract_summary({}, [], original_signature[end + 1:])
else:
summary = ''
return (name, summary, [('…', None, None)], None)
end = original_signature.find('\n')
logging.warning("cannot parse pybind11 function signature %s", original_signature[:end if end != -1 else None])
if end != -1 and len(original_signature) > end + 1 and original_signature[end + 1] == '\n':
- summary = extract_summary(original_signature[end + 1:])
+ summary = extract_summary({}, [], original_signature[end + 1:])
else:
summary = ''
return (name, summary, [('…', None, None)], None)
if len(signature) > 1 and signature[1] == '\n':
- summary = extract_summary(signature[2:])
+ summary = extract_summary({}, [], signature[2:])
else:
summary = ''
else:
return [parse_pybind_signature(state, doc)]
-def extract_summary(doc: str) -> str:
+def extract_summary(external_docs, path: List[str], doc: str) -> str:
+ # Prefer external docs, if available
+ path_str = '.'.join(path)
+ if path_str in external_docs:
+ return render_inline_rst(external_docs[path_str]['summary'])
+
if not doc: return '' # some modules (xml.etree) have that :(
doc = inspect.cleandoc(doc)
end = doc.find('\n\n')
# TODO could keep_trailing_newline fix this better?
f.write(b'\n')
-def extract_module_doc(path: List[str], module):
+def extract_module_doc(state: State, path: List[str], module):
assert inspect.ismodule(module)
out = Empty()
out.url = make_url(path)
out.name = path[-1]
- out.summary = extract_summary(module.__doc__)
+ out.summary = extract_summary(state.class_docs, path, module.__doc__)
return out
-def extract_class_doc(path: List[str], class_):
+def extract_class_doc(state: State, path: List[str], class_):
assert inspect.isclass(class_)
out = Empty()
out.url = make_url(path)
out.name = path[-1]
- out.summary = extract_summary(class_.__doc__)
+ out.summary = extract_summary(state.class_docs, path, class_.__doc__)
return out
def extract_enum_doc(state: State, path: List[str], enum_):
if enum_.__doc__ == 'An enumeration.':
out.summary = ''
else:
- out.summary = extract_summary(enum_.__doc__)
+ # TODO: external summary for enums
+ out.summary = extract_summary({}, [], enum_.__doc__)
out.base = extract_type(enum_.__base__)
if i.__doc__ == enum_.__doc__:
value.summary = ''
else:
- value.summary = extract_summary(i.__doc__)
+ # TODO: external summary for enum values
+ value.summary = extract_summary({}, [], i.__doc__)
if value.summary:
out.has_details = True
elif state.config['PYBIND11_COMPATIBILITY']:
assert hasattr(enum_, '__members__')
- out.summary = extract_summary(enum_.__doc__)
+ # TODO: external summary for enums
+ out.summary = extract_summary({}, [], enum_.__doc__)
out.base = None
for name, v in enum_.__members__.items():
out.params = []
out.has_complex_params = False
out.has_details = False
+ # TODO: external summary for functions
out.summary = summary
# Don't show None return type for void functions
out.params = []
out.has_complex_params = False
out.has_details = False
- out.summary = extract_summary(function.__doc__)
+ # TODO: external summary for functions
+ out.summary = extract_summary({}, [], function.__doc__)
# Decide if classmethod or staticmethod in case this is a method
if inspect.isclass(parent):
out = Empty()
out.name = path[-1]
- out.summary = extract_summary(property.__doc__)
+ # TODO: external summary for properties
+ out.summary = extract_summary({}, [], property.__doc__)
out.is_settable = property.fset is not None
out.is_deletable = property.fdel is not None
out.has_details = False
else:
out.value = None
+ # External data summary, if provided
+ path_str = '.'.join(path)
+ if path_str in state.data_docs:
+ # TODO: use also the contents
+ out.summary = render_inline_rst(state.data_docs[path_str]['summary'])
+ del state.data_docs[path_str]
+
return out
def render_module(state: State, path, module, env):
breadcrumb += [(i, url_base + 'html')]
page = Empty()
- page.summary = extract_summary(module.__doc__)
+ page.summary = extract_summary(state.module_docs, path, module.__doc__)
page.url = breadcrumb[-1][1]
page.breadcrumb = breadcrumb
page.prefix_wbr = '.<wbr />'.join(path + [''])
page.data = []
page.has_enum_details = False
+ # External page content, if provided
+ path_str = '.'.join(path)
+ if path_str in state.module_docs:
+ page.content = render_rst(state.module_docs[path_str]['content'])
+ state.module_docs[path_str]['used'] = True
+
# Index entry for this module, returned together with children at the end
index_entry = IndexEntry()
index_entry.kind = 'module'
# standard lib), but not undocumented classes etc. Render the
# submodules and subclasses recursively.
if inspect.ismodule(object):
- page.modules += [extract_module_doc(subpath, object)]
+ page.modules += [extract_module_doc(state, subpath, object)]
index_entry.children += [render_module(state, subpath, object, env)]
elif inspect.isclass(object) and not is_enum(state, object):
- page.classes += [extract_class_doc(subpath, object)]
+ page.classes += [extract_class_doc(state, subpath, object)]
index_entry.children += [render_class(state, subpath, object, env)]
elif inspect.isclass(object) and is_enum(state, object):
enum_ = extract_enum_doc(state, subpath, object)
if is_internal_or_imported_module_member(state, module, path, name, object): continue
subpath = path + [name]
- page.modules += [extract_module_doc(subpath, object)]
+ page.modules += [extract_module_doc(state, subpath, object)]
index_entry.children += [render_module(state, subpath, object, env)]
# Get (and render) inner classes
subpath = path + [name]
if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
- page.classes += [extract_class_doc(subpath, object)]
+ page.classes += [extract_class_doc(state, subpath, object)]
index_entry.children += [render_class(state, subpath, object, env)]
# Get enums
breadcrumb += [(i, url_base + 'html')]
page = Empty()
- page.summary = extract_summary(class_.__doc__)
+ page.summary = extract_summary(state.class_docs, path, class_.__doc__)
page.url = breadcrumb[-1][1]
page.breadcrumb = breadcrumb
page.prefix_wbr = '.<wbr />'.join(path + [''])
page.data = []
page.has_enum_details = False
+ # External page content, if provided
+ path_str = '.'.join(path)
+ if path_str in state.class_docs:
+ page.content = render_rst(state.class_docs[path_str]['content'])
+ state.class_docs[path_str]['used'] = True
+
# Index entry for this module, returned together with children at the end
index_entry = IndexEntry()
index_entry.kind = 'class'
subpath = path + [name]
if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
- page.classes += [extract_class_doc(subpath, object)]
+ page.classes += [extract_class_doc(state, subpath, object)]
index_entry.children += [render_class(state, subpath, object, env)]
# Get enums
render(state.config, 'class.html', page, env)
return index_entry
-def publish_rst(source):
+def publish_rst(source, translator_class=m.htmlsanity.SaneHtmlTranslator):
pub = docutils.core.Publisher(
writer=m.htmlsanity.SaneHtmlWriter(),
source_class=docutils.io.StringInput,
destination_class=docutils.io.StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html')
- #pub.writer.translator_class = m.htmlsanity.SaneHtmlTranslator
+ pub.writer.translator_class = translator_class
pub.process_programmatic_settings(None, m.htmlsanity.docutils_settings, None)
# Docutils uses a deprecated U mode for opening files, so instead of
# monkey-patching docutils.io.FileInput to not do that (like Pelican does),
# I just read the thing myself.
+ # TODO *somehow* need to supply the filename to it for better error
+ # reporting, this is too awful
pub.set_source(source=source)
pub.publish()
return pub
+def render_rst(source):
+ return publish_rst(source).writer.parts.get('body').rstrip()
+
+class _SaneInlineHtmlTranslator(m.htmlsanity.SaneHtmlTranslator):
+ # Unconditionally force compact paragraphs. This means the inline HTML
+ # won't be wrapped in a <p> which is exactly what we want.
+ def should_be_compact_paragraph(self, node):
+ return True
+
+def render_inline_rst(source):
+ return publish_rst(source, _SaneInlineHtmlTranslator).writer.parts.get('body').rstrip()
+
+def render_doc(state: State, filename):
+ logging.debug("parsing docs from %s", filename)
+
+ # Render the file. The directives should take care of everything, so just
+ # discard the output afterwards.
+ with open(filename, 'r') as f: publish_rst(f.read())
+
def render_page(state: State, path, filename, env):
logging.debug("generating %s.html", '.'.join(path))
# Import plugins
for plugin in ['m.htmlsanity'] + config['PLUGINS']:
module = importlib.import_module(plugin)
- module.register_mcss(mcss_settings=config, jinja_environment=env)
+ module.register_mcss(
+ mcss_settings=config,
+ jinja_environment=env,
+ module_doc_contents=state.module_docs,
+ class_doc_contents=state.class_docs,
+ data_doc_contents=state.data_docs)
+
+ # First process the doc input files so we have all data for rendering
+ # module pages
+ for file in config['INPUT_DOCS']:
+ render_doc(state, os.path.join(basedir, file))
for module in config['INPUT_MODULES']:
if isinstance(module, str):
state.class_index += [render_module(state, [module_name], module, env)]
+ # Warn if there are any unused contents left after processing everything
+ unused_module_docs = [key for key, value in state.module_docs.items() if not 'used' in value]
+ unused_class_docs = [key for key, value in state.class_docs.items() if not 'used' in value]
+ unused_data_docs = [key for key, value in state.data_docs.items() if not 'used' in value]
+ if unused_module_docs:
+ logging.warning("The following module doc contents were unused: %s", unused_module_docs)
+ if unused_class_docs:
+ logging.warning("The following class doc contents were unused: %s", unused_class_docs)
+ if unused_data_docs:
+ logging.warning("The following data doc contents were unused: %s", unused_data_docs)
+
for page in config['INPUT_PAGES']:
state.page_index += render_page(state, [os.path.splitext(os.path.basename(page))[0]], os.path.join(config['INPUT'], page), env)
</li>
</ul>
</div>
+ {% endif %}
+ {% if page.content %}
+{{ page.content }}
{% endif %}
{% if page.classes %}
<section id="classes">
</li>
</ul>
</div>
+ {% endif %}
+ {% if page.content %}
+{{ page.content }}
{% endif %}
{% if page.modules %}
<section id="namespaces">
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>My Python Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>Classes</h2>
+ <ul class="m-doc">
+ <li class="m-doc-collapsible">
+ <a href="#" onclick="return toggle(this)">module</a> <a href="content.html" class="m-doc">content</a> <span class="m-doc">This overwrites the docstring for <code>content</code>.</span>
+ <ul class="m-doc">
+ <li>class <a href="content.Class.html" class="m-doc">Class</a> <span class="m-doc">This overwrites the docstring for <code>content.Class</code>.</span></li>
+ </ul>
+ </li>
+ </ul>
+ <script>
+ function toggle(e) {
+ e.parentElement.className = e.parentElement.className == 'm-doc-collapsible' ?
+ 'm-doc-expansible' : 'm-doc-collapsible';
+ return false;
+ }
+ /* Collapse all nodes marked as such. Doing it via JS instead of
+ directly in markup so disabling it doesn't harm usability. The list
+ is somehow regenerated on every iteration and shrinks as I change
+ the classes. It's not documented anywhere and I'm not sure if this
+ is the same across browsers, so I am going backwards in that list to
+ be sure. */
+ var collapsed = document.getElementsByClassName("collapsed");
+ for(var i = collapsed.length - 1; i >= 0; --i)
+ collapsed[i].className = 'm-doc-expansible';
+ </script>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>content.Class | My Python Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ <span class="m-breadcrumb"><a href="content.html">content</a>.<wbr/></span>Class <span class="m-thin">class</span>
+ </h1>
+ <p>This overwrites the docstring for <code>content.Class</code>.</p>
+<p>This is detailed class docs. Here I <em>also</em> hate how it needs to be
+indented.</p>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>content | My Python Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ content <span class="m-thin">module</span>
+ </h1>
+ <p>This overwrites the docstring for <code>content</code>.</p>
+ <div class="m-block m-default">
+ <h3>Contents</h3>
+ <ul>
+ <li>
+ Reference
+ <ul>
+ <li><a href="#classes">Classes</a></li>
+ <li><a href="#data">Data</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+<p>This is detailed module docs. I kinda <em>hate</em> how it needs to be indented,
+tho.</p>
+ <section id="classes">
+ <h2><a href="#classes">Classes</a></h2>
+ <dl class="m-doc">
+ <dt>class <a href="content.Class.html" class="m-doc">Class</a></dt>
+ <dd>This overwrites the docstring for <code>content.Class</code>.</dd>
+ </dl>
+ </section>
+ <section id="data">
+ <h2><a href="#data">Data</a></h2>
+ <dl class="m-doc">
+ <dt>
+ <a href="" class="m-doc-self">CONSTANT</a>: float = 3.14
+ </dt>
+ <dd>This is finally a docstring for <code>content.CONSTANT</code></dd>
+ </dl>
+ </section>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+"""Yes so this is module summary, not shown in the output"""
+
+class Class:
+ """And this class summary, not shown either"""
+
+CONSTANT: float = 3.14
--- /dev/null
+.. py:module:: content
+ :summary: This overwrites the docstring for ``content``.
+
+ This is detailed module docs. I kinda *hate* how it needs to be indented,
+ tho.
+
+.. py:class:: content.Class
+ :summary: This overwrites the docstring for ``content.Class``.
+
+ This is detailed class docs. Here I *also* hate how it needs to be
+ indented.
+
+.. py:data:: content.CONSTANT
+ :summary: This is finally a docstring for ``content.CONSTANT``
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+import os
+
+from . import BaseInspectTestCase
+
+class Content(BaseInspectTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, '', *args, **kwargs)
+
+ def test(self):
+ self.run_python({
+ 'PLUGINS': ['m.sphinx'],
+ 'INPUT_DOCS': ['docs.rst']
+ })
+ self.assertEqual(*self.actual_expected_contents('classes.html'))
+ self.assertEqual(*self.actual_expected_contents('content.html'))
+ self.assertEqual(*self.actual_expected_contents('content.Class.html'))
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+from docutils.parsers import rst
+from docutils.parsers.rst import directives
+
+module_doc_output = None
+class_doc_output = None
+data_doc_output = None
+
+class PyModule(rst.Directive):
+ final_argument_whitespace = True
+ has_content = True
+ required_arguments = 1
+ option_spec = {'summary': directives.unchanged}
+
+ def run(self):
+ module_doc_output[self.arguments[0]] = {
+ 'summary': self.options.get('summary', ''),
+ 'content': '\n'.join(self.content)
+ }
+ return []
+
+class PyClass(rst.Directive):
+ final_argument_whitespace = True
+ has_content = True
+ required_arguments = 1
+ option_spec = {'summary': directives.unchanged}
+
+ def run(self):
+ class_doc_output[self.arguments[0]] = {
+ 'summary': self.options.get('summary', ''),
+ 'content': '\n'.join(self.content)
+ }
+ return []
+
+class PyData(rst.Directive):
+ final_argument_whitespace = True
+ has_content = True
+ required_arguments = 1
+ option_spec = {'summary': directives.unchanged}
+
+ def run(self):
+ data_doc_output[self.arguments[0]] = {
+ 'summary': self.options.get('summary', ''),
+ 'content': '\n'.join(self.content)
+ }
+ return []
+
+def register_mcss(module_doc_contents, class_doc_contents, data_doc_contents, **kwargs):
+ global module_doc_output, class_doc_output, data_doc_output
+ module_doc_output = module_doc_contents
+ class_doc_output = class_doc_contents
+ data_doc_output = data_doc_contents
+
+ rst.directives.register_directive('py:module', PyModule)
+ rst.directives.register_directive('py:class', PyClass)
+ rst.directives.register_directive('py:data', PyData)
+
+def register(): # for Pelican
+ assert not "This plugin is for the m.css Doc theme only" # pragma: no cover
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+# This module gets tested inside documentation/test_python/.
('Themes', 'css/themes/', 'css/themes')]),
('Themes', 'themes/', 'themes', [
('Writing reST content', 'themes/writing-rst-content/', 'pelican/writing-content'),
- ('Pelican', 'themes/pelican/', 'themes/pelican')])]
-
-M_LINKS_NAVBAR2 = [('Doc generators', 'documentation/', 'documentation', [
+ ('Pelican', 'themes/pelican/', 'themes/pelican')]),
+ ('Doc generators', 'documentation/', 'documentation', [
('Doxygen C++ theme', 'documentation/doxygen/', 'documentation/doxygen'),
- ('Python doc theme', 'documentation/python/', 'documentation/python')]),
- ('Plugins', 'plugins/', 'plugins', [
+ ('Python doc theme', 'documentation/python/', 'documentation/python')])]
+
+M_LINKS_NAVBAR2 = [('Plugins', 'plugins/', 'plugins', [
('HTML sanity', 'plugins/htmlsanity/', 'plugins/htmlsanity'),
('Components', 'plugins/components/', 'plugins/components'),
('Images', 'plugins/images/', 'plugins/images'),
('Math and code', 'plugins/math-and-code/', 'plugins/math-and-code'),
('Links and other', 'plugins/links/', 'plugins/links'),
('Plots and graphs', 'plugins/plots-and-graphs/', 'plugins/plots-and-graphs'),
- ('Metadata', 'plugins/metadata/', 'plugins/metadata')]),
+ ('Metadata', 'plugins/metadata/', 'plugins/metadata'),
+ ('Sphinx', 'plugins/sphinx/', 'plugins/sphinx')]),
('GitHub', 'https://github.com/mosra/m.css', '', [])]
M_LINKS_FOOTER1 = [('m.css', '/'),
('Math and code', 'plugins/math-and-code/'),
('Plots and graphs', 'plugins/plots-and-graphs/'),
('Links and other', 'plugins/links/'),
- ('Metadata', 'plugins/metadata/')]
+ ('Metadata', 'plugins/metadata/'),
+ ('Sphinx', 'plugins/sphinx/')]
M_FINE_PRINT = """
| m.css. Copyright © `Vladimír Vondruš <http://mosra.cz>`_, 2017--2019. Site